commit 6f8b26326feb9693d3d98a2fc69ad021252072fd
Author: Ilias Tsitsimpis <iliastsi@grnet.gr>
Date:   Fri Aug 23 15:31:32 2013 +0300

    ci: Improve flavors/images finder
    
    Allow for search both by name (using regular
    expressions) or by id.

diff --git a/ci/new_config b/ci/new_config
index 0edaad7..934fdc0 100644
--- a/ci/new_config
+++ b/ci/new_config
@@ -32,11 +32,14 @@ temporary_config = /tmp/ci_temp_conf
 kamaki_cloud =
 # Server name to use for our machine
 server_name = Synnefo Deployment
-# Flavor name (reg expression) to use
-# This is a list of flavor_names (comma seperated) to try
-flavor_name = C8R8...D100drbd
-# Image to use (name must contain this)
-image_name = OldStable
+# A list of flavors (comma seperated) to choose from
+# The user can specify a flavor name (reg expression)
+# with "name:" or a flavor id with "id:".
+flavors = name:C8R8...D100drbd, id:1
+# A list of images (comma seperated) to choose from
+# The user can specify an image name (reg expression)
+# with "name:" or an image id with "id:".
+images = name:Debian Base \(OldStable\), id:72d9844f-1024-4a07-a3c3-60d650b8f5cd
 # File containing the ssh keys to upload/install to server
 # If not set, no ssh keys will be installed
 ssh_keys =
diff --git a/ci/snf-ci b/ci/snf-ci
index 1cf9006..3a0488d 100755
--- a/ci/snf-ci
+++ b/ci/snf-ci
@@ -55,9 +55,15 @@ def main():  # Too many branches. pylint: disable-msg=R0912
     parser.add_option("--cloud", dest="kamaki_cloud", default=None,
                       help="Use specified cloud, as is in .kamakirc")
     parser.add_option("-f", "--flavor", dest="flavor", default=None,
-                      help="Name of flavor to use for the server.")
+                      help="Flavor to use for the server."
+                           " Supports both search by name (reg expression)"
+                           " with \"name:flavor name\" or by id with"
+                           " \"id:flavor id\".")
     parser.add_option("-i", "--image", dest="image", default=None,
-                      help="UUID of image to use for the server.")
+                      help="Image to use for the server."
+                           " Supports both search by name (reg expression)"
+                           " with \"name:image name\" or by id with"
+                           " \"id:image id\".")
     parser.add_option("--ssh-keys", dest="ssh_keys", default=None,
                       help="Upload/Install the public ssh keys contained"
                            " in this file to the server")
@@ -112,8 +118,8 @@ def main():  # Too many branches. pylint: disable-msg=R0912
                            cloud=options.kamaki_cloud)
 
     if getattr(options, CREATE_SERVER_CMD, False):
-        synnefo_ci.create_server(flavor_name=options.flavor,
-                                 image_id=options.image,
+        synnefo_ci.create_server(flavor=options.flavor,
+                                 image=options.image,
                                  ssh_keys=options.ssh_keys)
         synnefo_ci.clone_repo(local_repo=options.local_repo)
     if getattr(options, BUILD_SYNNEFO_CMD, False):
diff --git a/ci/utils.py b/ci/utils.py
index e0cd9eb..03d4590 100644
--- a/ci/utils.py
+++ b/ci/utils.py
@@ -242,7 +242,7 @@ class SynnefoCI(object):
             self._wait_transition(server_id, "ACTIVE", "DELETED")
 
     @_check_kamaki
-    def create_server(self, image_id=None, flavor_name=None, ssh_keys=None):
+    def create_server(self, image=None, flavor=None, ssh_keys=None):
         """Create slave server"""
         self.logger.info("Create a new server..")
 
@@ -260,13 +260,11 @@ class SynnefoCI(object):
                           % _green(self.build_id))
 
         # Find an image to use
-        if image_id is None:
-            image = self._find_image()
-            self.logger.debug("Will use image \"%s\"" % _green(image['name']))
-            image_id = image["id"]
-        self.logger.debug("Image has id %s" % _green(image_id))
+        image_id = self._find_image(image)
         # Find a flavor to use
-        flavor_id = self._find_flavor(flavor_name)
+        flavor_id = self._find_flavor(flavor)
+
+        # Create Server
         server = self.cyclades_client.create_server(
             self.config.get('Deployment', 'server_name'),
             flavor_id,
@@ -317,44 +315,90 @@ class SynnefoCI(object):
         """.format(self.config.get('Global', 'apt_repo'))
         _run(cmd, False)
 
-    def _find_flavor(self, flavor_name):
-        """Given a flavor_name (reg expression) find a flavor id to use"""
-        # Get a list of flavor names from config file
-        flavor_names = self.config.get('Deployment', 'flavor_name').split(",")
-        if flavor_name is not None:
+    def _find_flavor(self, flavor=None):
+        """Find a suitable flavor to use
+
+        Search by name (reg expression) or by id
+        """
+        # Get a list of flavors from config file
+        flavors = self.config.get('Deployment', 'flavors').split(",")
+        if flavor is not None:
             # If we have a flavor_name to use, add it to our list
-            flavor_names.insert(0, flavor_name)
-
-        flavors = self.compute_client.list_flavors()
-        for flname in flavor_names:
-            sflname = flname.strip()
-            self.logger.debug("Try to find a flavor with name \"%s\"" % sflname)
-            fls = [f for f in flavors
-                   if re.search(sflname, f['name']) is not None]
-            if fls:
-                self.logger.debug("Will use %s with id %s"
-                                  % (fls[0]['name'], fls[0]['id']))
-                return fls[0]['id']
+            flavors.insert(0, flavor)
+
+        list_flavors = self.compute_client.list_flavors()
+        for flv in flavors:
+            [flv_type, flv_value] = flv.strip().split(':')
+            if flv_type == "name":
+                # Filter flavors by name
+                self.logger.debug(
+                    "Trying to find a flavor with name \"%s\"" % flv_value)
+                list_flvs = \
+                    [f for f in list_flavors
+                     if re.search(flv_value, f['name'], flags=re.I) is not None]
+            elif flv_type == "id":
+                # Filter flavors by id
+                self.logger.debug(
+                    "Trying to find a flavor with id \"%s\"" % flv_value)
+                list_flvs = \
+                    [f for f in list_flavors
+                     if f['id'].lower() == flv_value.lower()]
+            else:
+                self.logger.error("Unrecognized flavor type %s" % flv_type)
+
+            # Check if we found one
+            if list_flvs:
+                self.logger.debug("Will use \"%s\" with id \"%s\""
+                                  % (list_flvs[0]['name'], list_flvs[0]['id']))
+                return list_flvs[0]['id']
 
         self.logger.error("No matching flavor found.. aborting")
         sys.exit(1)
 
-    def _find_image(self):
+    def _find_image(self, image=None):
         """Find a suitable image to use
 
-        It has to belong to one of the `DEFAULT_SYSTEM_IMAGES_UUID'
-        users and contain the word given by `image_name' option.
+        In case of search by name, the image has to belong to one
+        of the `DEFAULT_SYSTEM_IMAGES_UUID' users.
+        In case of search by id it only has to exist.
         """
-        image_name = self.config.get('Deployment', 'image_name').lower()
-        images = self.image_client.list_public(detail=True)['images']
-        # Select images by `system_uuid' user
-        images = [x for x in images
-                  if x['user_id'] in DEFAULT_SYSTEM_IMAGES_UUID]
-        # Select images with `image_name' in their names
-        images = [x for x in images
-                  if x['name'].lower().find(image_name) != -1]
-        # Let's select the first one
-        return images[0]
+        # Get a list of images from config file
+        images = self.config.get('Deployment', 'images').split(",")
+        if image is not None:
+            # If we have an image from command line, add it to our list
+            images.insert(0, image)
+
+        list_images = self.image_client.list_public(detail=True)['images']
+        for img in images:
+            [img_type, img_value] = img.strip().split(':')
+            if img_type == "name":
+                # Filter images by name
+                self.logger.debug(
+                    "Trying to find an image with name \"%s\"" % img_value)
+                list_imgs = \
+                    [i for i in list_images
+                     if i['user_id'] in DEFAULT_SYSTEM_IMAGES_UUID and
+                        re.search(img_value, i['name'], flags=re.I) is not None]
+            elif img_type == "id":
+                # Filter images by id
+                self.logger.debug(
+                    "Trying to find an image with id \"%s\"" % img_value)
+                list_imgs = \
+                    [i for i in list_images
+                     if i['id'].lower() == img_value.lower()]
+            else:
+                self.logger.error("Unrecognized image type %s" % img_type)
+                sys.exit(1)
+
+            # Check if we found one
+            if list_imgs:
+                self.logger.debug("Will use \"%s\" with id \"%s\""
+                                  % (list_imgs[0]['name'], list_imgs[0]['id']))
+                return list_imgs[0]['id']
+
+        # We didn't found one
+        self.logger.error("No matching image found.. aborting")
+        sys.exit(1)
 
     def _get_server_ip_and_port(self, server):
         """Compute server's IPv4 and ssh port number"""
@@ -614,7 +658,7 @@ class SynnefoCI(object):
         self.logger.info("Deploy Synnefo..")
         if schema is None:
             schema = self.config.get('Global', 'schema')
-        self.logger.debug("Will use %s schema" % schema)
+        self.logger.debug("Will use \"%s\" schema" % schema)
 
         schema_dir = os.path.join(self.ci_dir, "schemas/%s" % schema)
         if not (os.path.exists(schema_dir) and os.path.isdir(schema_dir)):
