]> git.apps.os.sepia.ceph.com Git - teuthology.git/commitdiff
openstack: add aarch64 support
authorLoic Dachary <ldachary@redhat.com>
Fri, 25 Mar 2016 14:10:51 +0000 (15:10 +0100)
committerKyr Shatskyy <kyrylo.shatskyy@gmail.com>
Wed, 3 Apr 2019 19:46:27 +0000 (21:46 +0200)
Signed-off-by: Loic Dachary <loic@dachary.org>
teuthology/openstack/__init__.py
teuthology/openstack/openstack-basic.yaml
teuthology/openstack/test/openstack-integration.py
teuthology/openstack/test/test_openstack.py
teuthology/provision/openstack.py

index dbe237a4624ead11489ff61c33c6a179e69aabe0..3e76997dda67f927f2c0d1c6ee077b4816e6d629 100644 (file)
@@ -48,6 +48,9 @@ from teuthology import misc
 
 log = logging.getLogger(__name__)
 
+class NoFlavorException(Exception):
+    pass
+
 def enforce_json_dictionary(something):
     if type(something) is not types.DictType:
         raise Exception(
@@ -178,29 +181,25 @@ class OpenStackInstance(object):
 
 class OpenStack(object):
 
-    # wget -O debian-8.0.qcow2  http://cdimage.debian.org/cdimage/openstack/current/debian-8.1.0-openstack-amd64.qcow2
-    # wget -O ubuntu-12.04.qcow2 https://cloud-images.ubuntu.com/precise/current/precise-server-cloudimg-amd64-disk1.img
-    # wget -O ubuntu-12.04-i386.qcow2 https://cloud-images.ubuntu.com/precise/current/precise-server-cloudimg-i386-disk1.img
-    # wget -O ubuntu-14.04.qcow2 https://cloud-images.ubuntu.com/trusty/current/trusty-server-cloudimg-amd64-disk1.img
-    # wget -O ubuntu-14.04-i386.qcow2 https://cloud-images.ubuntu.com/trusty/current/trusty-server-cloudimg-i386-disk1.img
-    # wget -O ubuntu-15.04.qcow2 https://cloud-images.ubuntu.com/vivid/current/vivid-server-cloudimg-arm64-disk1.img
-    # wget -O ubuntu-15.04-i386.qcow2 https://cloud-images.ubuntu.com/vivid/current/vivid-server-cloudimg-i386-disk1.img
-    # wget -O opensuse-13.2 http://download.opensuse.org/repositories/Cloud:/Images:/openSUSE_13.2/images/openSUSE-13.2-OpenStack-Guest.x86_64.qcow2
-    # wget -O opensuse-42.1 http://download.opensuse.org/repositories/Cloud:/Images:/Leap_42.1/images/openSUSE-Leap-42.1-OpenStack.x86_64.qcow2
-    # wget -O centos-7.0.qcow2 http://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud.qcow2
-    # wget -O centos-6.6.qcow2 http://cloud.centos.org/centos/6/images/CentOS-6-x86_64-GenericCloud.qcow2
-    # wget -O fedora-22.qcow2 https://download.fedoraproject.org/pub/fedora/linux/releases/22/Cloud/x86_64/Images/Fedora-Cloud-Base-22-20150521.x86_64.qcow2
-    # wget -O fedora-21.qcow2 http://fedora.mirrors.ovh.net/linux/releases/21/Cloud/Images/x86_64/Fedora-Cloud-Base-20141203-21.x86_64.qcow2
-    # wget -O fedora-20.qcow2 http://fedora.mirrors.ovh.net/linux/releases/20/Images/x86_64/Fedora-x86_64-20-20131211.1-sda.qcow2
+    # http://cdimage.debian.org/cdimage/openstack/current/
+    # https://cloud-images.ubuntu.com/precise/current/precise-server-cloudimg-amd64-disk1.img etc.
+    # http://download.opensuse.org/repositories/Cloud:/Images:/openSUSE_13.2/images/openSUSE-13.2-OpenStack-Guest.x86_64.qcow2
+    # http://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud.qcow2 etc.
+    # http://cloud.centos.org/centos/6/images/CentOS-6-x86_64-GenericCloud.qcow2 etc.
+    # https://download.fedoraproject.org/pub/fedora/linux/releases/22/Cloud/x86_64/Images/Fedora-Cloud-Base-22-20150521.x86_64.qcow2
+    # http://fedora.mirrors.ovh.net/linux/releases/21/Cloud/Images/x86_64/Fedora-Cloud-Base-20141203-21.x86_64.qcow2
+    # http://fedora.mirrors.ovh.net/linux/releases/20/Images/x86_64/Fedora-x86_64-20-20131211.1-sda.qcow2
     image2url = {
-        'centos-6.5': 'http://cloud.centos.org/centos/6/images/CentOS-6-x86_64-GenericCloud-1508.qcow2',
-        'centos-7.0': 'http://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud-1503.qcow2',
-        'centos-7.1': 'http://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud-1503.qcow2',
-        'centos-7.2': 'http://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud-1511.qcow2',
-        'opensuse-42.1': 'http://download.opensuse.org/repositories/Cloud:/Images:/Leap_42.1/images/openSUSE-Leap-42.1-OpenStack.x86_64.qcow2',
-        'ubuntu-12.04': 'https://cloud-images.ubuntu.com/precise/current/precise-server-cloudimg-amd64-disk1.img',
-        'ubuntu-14.04': 'https://cloud-images.ubuntu.com/trusty/current/trusty-server-cloudimg-amd64-disk1.img',
-        'debian-8.0': 'http://cdimage.debian.org/cdimage/openstack/current/debian-8.2.0-openstack-amd64.qcow2',
+        'centos-6.5-x86_64': 'http://cloud.centos.org/centos/6/images/CentOS-6-x86_64-GenericCloud-1508.qcow2',
+        'centos-7.0-x86_64': 'http://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud-1503.qcow2',
+        'centos-7.1-x86_64': 'http://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud-1503.qcow2',
+        'centos-7.2-x86_64': 'http://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud-1511.qcow2',
+        'opensuse-42.1-x86_64': 'http://download.opensuse.org/repositories/Cloud:/Images:/Leap_42.1/images/openSUSE-Leap-42.1-OpenStack.x86_64.qcow2',
+        'ubuntu-12.04-x86_64': 'https://cloud-images.ubuntu.com/precise/current/precise-server-cloudimg-amd64-disk1.img',
+        'ubuntu-14.04-x86_64': 'https://cloud-images.ubuntu.com/trusty/current/trusty-server-cloudimg-amd64-disk1.img',
+        'ubuntu-14.04-arm64': 'https://cloud-images.ubuntu.com/trusty/current/trusty-server-cloudimg-arm64-disk1.img',
+        'ubuntu-14.04-i686': 'https://cloud-images.ubuntu.com/trusty/current/trusty-server-cloudimg-i386-disk1.img',
+        'debian-8.0-x86_64': 'http://cdimage.debian.org/cdimage/openstack/current/debian-8.3.0-openstack-amd64.qcow2',
     }
 
     def __init__(self):
@@ -282,7 +281,8 @@ class OpenStack(object):
     def set_provider(self):
         if 'OS_AUTH_URL' not in os.environ:
             raise Exception('no OS_AUTH_URL environment variable')
-        providers = (('cloud.ovh.net', 'ovh'),
+        providers = (('runabove.io', 'runabove'),
+                     ('cloud.ovh.net', 'ovh'),
                      ('entercloudsuite.com', 'entercloudsuite'),
                      ('rackspacecloud.com', 'rackspace'),
                      ('dream.io', 'dreamhost'))
@@ -328,11 +328,11 @@ class OpenStack(object):
                                network))
         return self.get_value(r, 'id')
 
-    def type_version(self, os_type, os_version):
+    def type_version_arch(self, os_type, os_version, arch):
         """
         Return the string used to differentiate os_type and os_version in names.
         """
-        return os_type + '-' + os_version
+        return os_type + '-' + os_version + '-' + arch
 
     def image_name(self, name):
         """
@@ -341,9 +341,9 @@ class OpenStack(object):
         """
         return "teuthology-" + name
 
-    def image_create(self, name):
+    def image_create(self, name, arch):
         """
-        Upload an image into OpenStack with glance.
+        Upload an image into OpenStack
         """
         misc.sh("wget -c -O " + name + ".qcow2 " + self.image2url[name])
         if self.get_provider() == 'dreamhost':
@@ -353,22 +353,37 @@ class OpenStack(object):
         else:
             image = name + ".qcow2"
             disk_format = 'qcow2'
-        misc.sh("glance image-create --property ownedby=teuthology " +
+        if self.provider == 'runabove':
+            properties = [
+                "--property architecture_restrict=" + arch,
+                "--property architecture=" + arch
+            ]
+        elif self.get_provider() == 'cloudlab':
+            # if not, nova-compute fails on the compute node with
+            # Error: Cirrus VGA not available
+            properties = [
+                "--property hw_video_model=vga",
+            ]
+        else:
+            properties = []
+
+        misc.sh("openstack image create --property ownedby=teuthology " +
+                " ".join(properties) +
                 " --disk-format=" + disk_format + " --container-format=bare " +
-                " --visibility private" +
-                " --file " + image + " --name " + self.image_name(name))
+                " --private" +
+                " --file " + image + " " + self.image_name(name))
 
-    def image(self, os_type, os_version):
+    def image(self, os_type, os_version, arch):
         """
         Return the image name for the given os_type and os_version. If the image
         does not exist it will be created.
         """
-        name = self.type_version(os_type, os_version)
+        name = self.type_version_arch(os_type, os_version, arch)
         if not self.image_exists(name):
-            self.image_create(name)
+            self.image_create(name, arch)
         return self.image_name(name)
 
-    def flavor(self, hint, select):
+    def get_sorted_flavors(self, arch, select):
         """
         Return the smallest flavor that satisfies the desired size.
         """
@@ -378,22 +393,54 @@ class OpenStack(object):
         for flavor in flavors:
             if select and not re.match(select, flavor['Name']):
                 continue
-            if (flavor['RAM'] >= hint['ram'] and
-                    flavor['VCPUs'] >= hint['cpus'] and
-                    flavor['Disk'] >= hint['disk']):
-                found.append(flavor)
-        if not found:
-            raise Exception("openstack flavor list: " + flavors_string +
-                            " does not contain a flavor in which" +
-                            " the desired " + str(hint) + " can fit")
+            found.append(flavor)
 
         def sort_flavor(a, b):
             return (a['VCPUs'] - b['VCPUs'] or
                     a['RAM'] - b['RAM'] or
                     a['Disk'] - b['Disk'])
-        sorted_flavor = sorted(found, cmp=sort_flavor)
-        log.debug("sorted flavor = " + str(sorted_flavor))
-        return sorted_flavor[0]['Name']
+        sorted_flavors = sorted(found, cmp=sort_flavor)
+        log.debug("sorted flavors = " + str(sorted_flavors))
+        return sorted_flavors
+
+    def flavor(self, hint, arch, select):
+        """
+        Return the smallest flavor that satisfies the desired size.
+        """
+        flavors = self.get_sorted_flavors(arch, select)
+        for flavor in flavors:
+            if (flavor['RAM'] >= hint['ram'] and
+                    flavor['VCPUs'] >= hint['cpus'] and
+                    flavor['Disk'] >= hint['disk']):
+                return flavor['Name']
+        raise NoFlavorException("openstack flavor list: " + str(flavors) +
+                                " does not contain a flavor in which" +
+                                " the desired " + str(hint) + " can fit")
+
+    def flavor_range(self, min, good, arch, select):
+        """
+        Return the smallest flavor that satisfies the good hint.
+        If no such flavor, get the largest flavor smaller than good
+        and larger than min.
+        """
+        flavors = self.get_sorted_flavors(arch, select)
+        low_range = []
+        for flavor in flavors:
+            if (flavor['RAM'] >= good['ram'] and
+                    flavor['VCPUs'] >= good['cpus'] and
+                    flavor['Disk'] >= good['disk']):
+                return flavor['Name']
+            else:
+                low_range.append(flavor)
+        low_range.reverse()
+        for flavor in low_range:
+            if (flavor['RAM'] >= min['ram'] and
+                    flavor['VCPUs'] >= min['cpus'] and
+                    flavor['Disk'] >= min['disk']):
+                return flavor['Name']
+        raise NoFlavorException("openstack flavor list: " + str(flavors) +
+                                " does not contain a flavor which" +
+                                " is larger than " + str(min))
 
     def interpret_hints(self, defaults, hints):
         """
@@ -508,6 +555,16 @@ class OpenStack(object):
     def get_ip(self, instance_id, network):
         return OpenStackInstance(instance_id).get_ip(network)
 
+    def get_available_arch(self):
+        if (self.provider == 'runabove' and
+            'HZ1' in os.environ.get('OS_REGION_NAME', '')):
+            return ('aarch64',)
+        else:
+            return ('x86_64', 'i686')
+
+    def get_default_arch(self):
+        return self.get_available_archs()[0]
+
 
 class TeuthologyOpenStack(OpenStack):
 
@@ -806,7 +863,7 @@ ssh access           : ssh {identity}{username}@{ip} # logs in /usr/share/nginx/
             log.exception("flavor list")
             raise Exception("verify openrc.sh has been sourced")
 
-    def flavor(self):
+    def flavor(self, arch):
         """
         Return an OpenStack flavor fit to run the teuthology cluster.
         The RAM size depends on the maximum number of workers that
@@ -827,7 +884,7 @@ ssh access           : ssh {identity}{username}@{ip} # logs in /usr/share/nginx/
         select = None
         if self.get_provider() == 'ovh':
             select = '^(vps|eg)-'
-        return super(TeuthologyOpenStack, self).flavor(hint, select)
+        return super(TeuthologyOpenStack, self).flavor(hint, arch, select)
 
     def net(self):
         """
@@ -983,10 +1040,11 @@ openstack security group rule create --proto udp --dst-port 16000:65535 teutholo
             security_group = ''
         else:
             security_group = " --security-group teuthology"
+        arch = self.get_default_arch()
         self.run(
             "server create " +
-            " --image '" + self.image('ubuntu', '14.04') + "' " +
-            " --flavor '" + self.flavor() + "' " +
+            " --image '" + self.image('ubuntu', '14.04', arch) + "' " +
+            " --flavor '" + self.flavor(arch) + "' " +
             " " + self.net() +
             " --key-name " + self.args.key_name +
             " --user-data " + user_data +
index 2df84b57b682771355f43c5c6ce485cff2969866..cf076259c1d8e47100b57b102edb2f29600669a9 100644 (file)
@@ -8,4 +8,4 @@ overrides:
         mon lease ack timeout: 25
   s3tests:
     idle_timeout: 1200
-archive-on-error: true
+archive-on-error: true
\ No newline at end of file
index 95f46e80b693ea549e5c3d0524e6b703bdffa4b0..c8032002bac8eff4302758b0c2ee665c172ce3e0 100644 (file)
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2015 Red Hat, Inc.
+# Copyright (c) 2015, 2016 Red Hat, Inc.
 #
 # Author: Loic Dachary <loic@dachary.org>
 #
@@ -227,12 +227,22 @@ class TestLock(Integration):
         assert teuthology.lock.cli.main(args) == 0
 
     def test_lock_unlock(self):
-        for image in teuthology.openstack.OpenStack.image2url.keys():
-            (os_type, os_version) = image.split('-')
+        default_archs = teuthology.openstack.OpenStack().get_available_archs()
+        if 'TEST_IMAGES' in os.environ:
+            images = os.environ['TEST_IMAGES'].split()
+        else:
+            images = teuthology.openstack.OpenStack.image2url.keys()
+        for image in images:
+            (os_type, os_version, arch) = image.split('-')
+            if arch not in default_archs:
+                logging.info("skipping " + image + " because arch " +
+                             " is not supported (" + str(default_archs) + ")")
+                continue
             args = scripts.lock.parse_args(self.options +
                                            ['--lock-many', '1',
                                             '--os-type', os_type,
-                                            '--os-version', os_version])
+                                            '--os-version', os_version,
+                                            '--arch', arch])
             assert teuthology.lock.cli.main(args) == 0
             locks = teuthology.lock.query.list_locks(locked=True)
             assert len(locks) == 1
@@ -256,7 +266,7 @@ class TestNuke(Integration):
     def test_nuke(self):
         image = teuthology.openstack.OpenStack.image2url.keys()[0]
 
-        (os_type, os_version) = image.split('-')
+        (os_type, os_version, arch) = image.split('-')
         args = scripts.lock.parse_args(self.options +
                                        ['--lock-many', '1',
                                         '--os-type', os_type,
index df38d63c8820a09765dda000c998c1af2644e09c..20f4dbd545d8d918724ff6748057d0313e7858a7 100644 (file)
@@ -34,6 +34,7 @@ import teuthology
 from teuthology import misc
 from teuthology.config import set_config_attr
 from teuthology.openstack import TeuthologyOpenStack, OpenStack, OpenStackInstance
+from teuthology.openstack import NoFlavorException
 import scripts.openstack
 
 
@@ -213,6 +214,344 @@ class TestOpenStackInstance(object):
 
 class TestOpenStack(object):
 
+    flavors = """[
+          {
+            "Name": "eg-60",
+            "RAM": 60000,
+            "Ephemeral": 0,
+            "VCPUs": 16,
+            "Is Public": true,
+            "Disk": 1600,
+            "ID": "0297d7ac-fe6f-4ff1-b6e7-0b8b0908c94f"
+          },
+          {
+            "Name": "win-sp-60",
+            "RAM": 60000,
+            "Ephemeral": 0,
+            "VCPUs": 4,
+            "Is Public": true,
+            "Disk": 400,
+            "ID": "0417a0e6-f68a-4b8b-a642-ca5ecb9652f7"
+          },
+          {
+            "Name": "win-sp-240",
+            "RAM": 240000,
+            "Ephemeral": 0,
+            "VCPUs": 16,
+            "Is Public": true,
+            "Disk": 1600,
+            "ID": "07885848-8831-486d-8525-91484c09cc7e"
+          },
+          {
+            "Name": "vps-ssd-1",
+            "RAM": 2000,
+            "Ephemeral": 0,
+            "VCPUs": 1,
+            "Is Public": true,
+            "Disk": 10,
+            "ID": "164fcc7e-7771-414f-a607-b388cb7b7aa0"
+          },
+          {
+            "Name": "eg-120",
+            "RAM": 120000,
+            "Ephemeral": 0,
+            "VCPUs": 32,
+            "Is Public": true,
+            "Disk": 1600,
+            "ID": "1f1efedf-ec91-4a42-acd7-f5cf64b02d3c"
+          },
+          {
+            "Name": "win-eg-7",
+            "RAM": 7000,
+            "Ephemeral": 0,
+            "VCPUs": 2,
+            "Is Public": true,
+            "Disk": 200,
+            "ID": "377ded36-491f-4ad7-9eb4-876798b2aea9"
+          },
+          {
+            "Name": "eg-30",
+            "RAM": 30000,
+            "Ephemeral": 0,
+            "VCPUs": 8,
+            "Is Public": true,
+            "Disk": 800,
+            "ID": "3c1d6170-0097-4b5c-a3b3-adff1b7a86e0"
+          },
+          {
+            "Name": "eg-15",
+            "RAM": 15000,
+            "Ephemeral": 0,
+            "VCPUs": 4,
+            "Is Public": true,
+            "Disk": 400,
+            "ID": "675558ea-04fe-47a2-83de-40be9b2eacd4"
+          },
+          {
+            "Name": "win-eg-30",
+            "RAM": 30000,
+            "Ephemeral": 0,
+            "VCPUs": 8,
+            "Is Public": true,
+            "Disk": 800,
+            "ID": "6e12cae3-0492-483c-aa39-54a0dcaf86dd"
+          },
+          {
+            "Name": "vps-ssd-2",
+            "RAM": 4000,
+            "Ephemeral": 0,
+            "VCPUs": 1,
+            "Is Public": true,
+            "Disk": 20,
+            "ID": "7939cc5c-79b1-45c0-be2d-aa935d92faa1"
+          },
+          {
+            "Name": "sp-60",
+            "RAM": 60000,
+            "Ephemeral": 0,
+            "VCPUs": 4,
+            "Is Public": true,
+            "Disk": 400,
+            "ID": "80d8510a-79cc-4307-8db7-d1965c9e8ddb"
+          },
+          {
+            "Name": "win-sp-30",
+            "RAM": 30000,
+            "Ephemeral": 0,
+            "VCPUs": 2,
+            "Is Public": true,
+            "Disk": 200,
+            "ID": "8be9dc29-3eca-499b-ae2d-e3c99699131a"
+          },
+          {
+            "Name": "sp-120",
+            "RAM": 120000,
+            "Ephemeral": 0,
+            "VCPUs": 8,
+            "Is Public": true,
+            "Disk": 800,
+            "ID": "ac74cb45-d895-47dd-b9cf-c17778033d83"
+          },
+          {
+            "Name": "win-eg-15",
+            "RAM": 15000,
+            "Ephemeral": 0,
+            "VCPUs": 4,
+            "Is Public": true,
+            "Disk": 400,
+            "ID": "ae900175-72bd-4fbc-8ab2-4673b468aa5b"
+          },
+          {
+            "Name": "win-eg-120",
+            "RAM": 120000,
+            "Ephemeral": 0,
+            "VCPUs": 32,
+            "Is Public": true,
+            "Disk": 1600,
+            "ID": "b798e44e-bf71-488c-9335-f20bf5976547"
+          },
+          {
+            "Name": "sp-30",
+            "RAM": 30000,
+            "Ephemeral": 0,
+            "VCPUs": 2,
+            "Is Public": true,
+            "Disk": 200,
+            "ID": "d1acf88d-6f55-4c5c-a914-4ecbdbd50d6b"
+          },
+          {
+            "Name": "win-eg-60",
+            "RAM": 60000,
+            "Ephemeral": 0,
+            "VCPUs": 16,
+            "Is Public": true,
+            "Disk": 1600,
+            "ID": "def75cbd-a4b1-4f82-9152-90c65df9587b"
+          },
+          {
+            "Name": "vps-ssd-3",
+            "RAM": 8000,
+            "Ephemeral": 0,
+            "VCPUs": 2,
+            "Is Public": true,
+            "Disk": 40,
+            "ID": "e43d7458-6b82-4a78-a712-3a4dc6748cf4"
+          },
+          {
+            "Name": "sp-240",
+            "RAM": 240000,
+            "Ephemeral": 0,
+            "VCPUs": 16,
+            "Is Public": true,
+            "Disk": 1600,
+            "ID": "ed286e2c-769f-4c47-ac52-b8de7a4891f6"
+          },
+          {
+            "Name": "win-sp-120",
+            "RAM": 120000,
+            "Ephemeral": 0,
+            "VCPUs": 8,
+            "Is Public": true,
+            "Disk": 800,
+            "ID": "f247dc56-395b-49de-9a62-93ccc4fff4ed"
+          },
+          {
+            "Name": "eg-7",
+            "RAM": 7000,
+            "Ephemeral": 0,
+            "VCPUs": 2,
+            "Is Public": true,
+            "Disk": 200,
+            "ID": "fa3cc551-0358-4170-be64-56ea432b064c"
+          }
+    ]"""
+
+    @patch('teuthology.misc.sh')
+    def test_sorted_flavors(self, m_sh):
+        o = OpenStack()
+        select = '^(vps|eg)-'
+        m_sh.return_value = TestOpenStack.flavors
+        flavors = o.get_sorted_flavors('arch', select)
+        assert ['vps-ssd-1',
+                'vps-ssd-2',
+                'eg-7',
+                'vps-ssd-3',
+                'eg-15',
+                'eg-30',
+                'eg-60',
+                'eg-120',
+        ] == [ f['Name'] for f in flavors ]
+        m_sh.assert_called_with("openstack flavor list -f json")
+
+    def test_flavor(self):
+        def get_sorted_flavors(self, arch, select):
+            return [
+                {
+                    'Name': 'too_small',
+                    'RAM': 2048,
+                    'Disk': 50,
+                    'VCPUs': 1,
+                },
+            ]
+        with patch.multiple(
+                OpenStack,
+                get_sorted_flavors=get_sorted_flavors,
+        ):
+            with pytest.raises(NoFlavorException):
+                hint = { 'ram': 1000, 'disk': 40, 'cpus': 2 }
+                OpenStack().flavor(hint, 'arch', None)
+
+        flavor = 'good-flavor'
+        def get_sorted_flavors(self, arch, select):
+            return [
+                {
+                    'Name': flavor,
+                    'RAM': 2048,
+                    'Disk': 50,
+                    'VCPUs': 2,
+                },
+            ]
+        with patch.multiple(
+                OpenStack,
+                get_sorted_flavors=get_sorted_flavors,
+        ):
+            hint = { 'ram': 1000, 'disk': 40, 'cpus': 2 }
+            assert flavor == OpenStack().flavor(hint, 'arch', None)
+
+    def test_flavor_range(self):
+        flavors = [
+                {
+                    'Name': 'too_small',
+                    'RAM': 2048,
+                    'Disk': 50,
+                    'VCPUs': 1,
+                },
+        ]
+        def get_sorted_flavors(self, arch, select):
+            return flavors
+
+        min = { 'ram': 1000, 'disk': 40, 'cpus': 2 }
+        good = { 'ram': 4000, 'disk': 40, 'cpus': 2 }
+
+        #
+        # there are no flavors in the required range
+        #
+        with patch.multiple(
+                OpenStack,
+                get_sorted_flavors=get_sorted_flavors,
+        ):
+            with pytest.raises(NoFlavorException):
+                OpenStack().flavor_range(min, good, 'arch', None)
+
+        #
+        # there is one flavor in the required range
+        #
+        flavors.append({
+                    'Name': 'min',
+                    'RAM': 2048,
+                    'Disk': 40,
+                    'VCPUs': 2,
+        })
+
+        with patch.multiple(
+                OpenStack,
+                get_sorted_flavors=get_sorted_flavors,
+        ):
+
+            assert 'min' == OpenStack().flavor_range(min, good, 'arch', None)
+
+        #
+        # out of the two flavors in the required range, get the bigger one
+        #
+        flavors.append({
+                    'Name': 'good',
+                    'RAM': 3000,
+                    'Disk': 40,
+                    'VCPUs': 2,
+        })
+
+        with patch.multiple(
+                OpenStack,
+                get_sorted_flavors=get_sorted_flavors,
+        ):
+
+            assert 'good' == OpenStack().flavor_range(min, good, 'arch', None)
+
+        #
+        # there is one flavor bigger or equal to good, get this one
+        #
+        flavors.append({
+                    'Name': 'best',
+                    'RAM': 4000,
+                    'Disk': 40,
+                    'VCPUs': 2,
+        })
+
+        with patch.multiple(
+                OpenStack,
+                get_sorted_flavors=get_sorted_flavors,
+        ):
+
+            assert 'best' == OpenStack().flavor_range(min, good, 'arch', None)
+
+        #
+        # there are two flavors bigger or equal to good, get the smallest one
+        #
+        flavors.append({
+                    'Name': 'too_big',
+                    'RAM': 30000,
+                    'Disk': 400,
+                    'VCPUs': 20,
+        })
+
+        with patch.multiple(
+                OpenStack,
+                get_sorted_flavors=get_sorted_flavors,
+        ):
+
+            assert 'best' == OpenStack().flavor_range(min, good, 'arch', None)
+
+
     def test_interpret_hints(self):
         defaults = {
             'machine': {
index 49adaf0fa65679e396991a737afcf4a3950de5aa..affb24ba0b926678b76071a8cc6d23ff9e85bf07 100644 (file)
@@ -109,17 +109,20 @@ class ProvisionOpenStack(OpenStack):
         described in resources_hint.
         """
         log.debug('ProvisionOpenStack:create')
+        if arch is None:
+            arch = self.get_default_arch()
         resources_hint = self.interpret_hints({
             'machine': config['openstack']['machine'],
             'volumes': config['openstack']['volumes'],
         }, resources_hint)
         self.init_user_data(os_type, os_version)
-        image = self.image(os_type, os_version)
+        image = self.image(os_type, os_version, arch)
         if 'network' in config['openstack']:
             net = "--nic net-id=" + str(self.net_id(config['openstack']['network']))
         else:
             net = ''
         flavor = self.flavor(resources_hint['machine'],
+                             arch,
                              config['openstack'].get('flavor-select-regexp'))
         cmd = ("flock --close --timeout 28800 /tmp/teuthology-server-create.lock" +
                " openstack server create" +