From cdc0266de52906d929a0c5239a7b5458844dc886 Mon Sep 17 00:00:00 2001 From: Martin Kopec Date: Tue, 26 Mar 2019 08:51:28 +0000 Subject: [PATCH] Allow to specify min flavor memory and disk size python-tempestconf doesn't provide any way how to define a different flavor memory and disk size but provides an option for defining a custom image (not cirros which is default). However, when using custom image the default flavor size is not enough. Therefore the review adds two arguments which can be used for defining flavor minimum memory and minimum disk size. The review also edits the documentation and fixes a few typos. Change-Id: Iba14b65ff8e147765e47d223dea6231ab649135f Story: 2001536 Task: 6341 --- config_tempest/constants.py | 5 +++ config_tempest/flavors.py | 58 ++++++++++++++++------------ config_tempest/main.py | 10 +++++ config_tempest/tests/test_flavors.py | 24 ++++++++---- doc/source/admin/admin_usage.rst | 15 +++++++ doc/source/user/usage.rst | 49 +++++++++++++++-------- 6 files changed, 113 insertions(+), 48 deletions(-) diff --git a/config_tempest/constants.py b/config_tempest/constants.py index 5580edbd..1de8f917 100644 --- a/config_tempest/constants.py +++ b/config_tempest/constants.py @@ -27,6 +27,11 @@ DEFAULT_IMAGE = ("http://download.cirros-cloud.net/0.3.5/" "cirros-0.3.5-x86_64-disk.img") DEFAULT_IMAGE_FORMAT = 'qcow2' +DEFAULT_FLAVOR_RAM = 64 +DEFAULT_FLAVOR_RAM_ALT = 128 +DEFAULT_FLAVOR_DISK = 1 +DEFAULT_FLAVOR_VCPUS = 1 + # The dict holds the credentials, which are not supposed to be printed # to a tempest.conf when --test-accounts CLI parameter is used. ALL_CREDENTIALS_KEYS = { diff --git a/config_tempest/flavors.py b/config_tempest/flavors.py index 658e0ef2..67479b51 100644 --- a/config_tempest/flavors.py +++ b/config_tempest/flavors.py @@ -15,11 +15,12 @@ from operator import itemgetter -from config_tempest.constants import LOG +from config_tempest import constants as C class Flavors(object): - def __init__(self, client, allow_creation, conf, no_rng=False): + def __init__(self, client, allow_creation, conf, min_memory, min_disk, + no_rng=False): """Init. :type client: FlavorsClient object from tempest lib @@ -29,22 +30,31 @@ class Flavors(object): self.client = client self.allow_creation = allow_creation self._conf = conf - self.no_rng = no_rng self.flavor_list = self.client.list_flavors()['flavors'] + min_memory_alt = C.DEFAULT_FLAVOR_RAM_ALT + name = 'm1.nano' + name_alt = 'm1.micro' + if min_memory != C.DEFAULT_FLAVOR_RAM: + min_memory_alt = min_memory + 1 + name = 'custom' + name_alt = 'custom_alt' + self._conf.set('volume', 'volume_size', str(min_disk)) + self.prefs = [ + {'key': 'flavor_ref', 'name': name, 'ram': min_memory, + 'disk': min_disk, 'no_rng': no_rng}, + {'key': 'flavor_ref_alt', 'name': name_alt, + 'ram': min_memory_alt, 'disk': min_disk, 'no_rng': no_rng} + ] def create_tempest_flavors(self): """Find or create flavors and set them in conf. If 'flavor_ref' and 'flavor_ref_alt' are specified in conf, it will try to find them, if not found, it raises an Exception. - Otherwise it will try finding or creating 'm1.nano' and 'm1.micro' - flavors and set their ids in conf. + Otherwise it will try finding or creating the required base flavors + (m1.nano and m1.micro by default) and set their ids in conf. """ - prefs = [ - {'key': 'flavor_ref', 'name': 'm1.nano', 'ram': 64}, - {'key': 'flavor_ref_alt', 'name': 'm1.micro', 'ram': 128} - ] - for pref in prefs: + for pref in self.prefs: flavor_id = None if self._conf.has_option('compute', pref['key']): flavor_id = self._conf.get('compute', pref['key']) @@ -53,13 +63,13 @@ class Flavors(object): raise Exception("%s id '%s' specified by user doesn't" " exist", pref['key'], flavor_id) else: - # create m1.nano/m1.micro flavor - flavor_id = self.create_flavor(pref['name'], ram=pref['ram'], - no_rng=self.no_rng) + flavor_id = self.create_flavor(pref['name'], pref['ram'], + C.DEFAULT_FLAVOR_VCPUS, + pref['disk'], + no_rng=pref['no_rng']) self._conf.set('compute', pref['key'], flavor_id) - def create_flavor(self, flavor_name, ram=64, vcpus=1, - disk=1, no_rng=False): + def create_flavor(self, flavor_name, ram, vcpus, disk, no_rng=False): """Create flavors or try to discover two smallest ones available. :param flavor_name: flavor name to be created (usually m1.nano or @@ -71,10 +81,10 @@ class Flavors(object): """ flavor_id = self.find_flavor_by_name(flavor_name) if flavor_id is not None: - LOG.info("(no change) Found flavor '%s'", flavor_name) + C.LOG.info("(no change) Found flavor '%s'", flavor_name) return flavor_id elif self.allow_creation: - LOG.info("Creating flavor '%s'", flavor_name) + C.LOG.info("Creating flavor '%s'", flavor_name) resp = self.client.create_flavor(name=flavor_name, ram=ram, vcpus=vcpus, disk=disk, id=None) @@ -101,8 +111,8 @@ class Flavors(object): """ found = [f for f in self.flavor_list if f['id'] == flavor_id] if found: - LOG.info("Found flavor '%s' by it's id '%s'", - found[0]['name'], flavor_id) + C.LOG.info("Found flavor '%s' by it's id '%s'", + found[0]['name'], flavor_id) # return flavor's id return found[0]['id'] return None @@ -127,9 +137,9 @@ class Flavors(object): smallest flavor found. :param flavor_name: [m1.nano, m1.micro] """ - LOG.warning("Flavor '%s' not found and creation is not allowed. " - "Trying to autodetect the smallest flavor available.", - flavor_name) + C.LOG.warning("Flavor '%s' not found and creation is not allowed. " + "Trying to autodetect the smallest flavor available.", + flavor_name) flavors = [] for flavor in self.flavor_list: f = self.client.show_flavor(flavor['id'])['flavor'] @@ -145,7 +155,7 @@ class Flavors(object): f = flavors[1] else: f = flavors[0] - LOG.warning("Found '%s' flavor (id: '%s', ram: '%s', disk: '%s', " - "vcpus: '%s') ", f[0], f[1], f[2], f[3], f[4]) + C.LOG.warning("Found '%s' flavor (id: '%s', ram: '%s', disk: '%s', " + "vcpus: '%s') ", f[0], f[1], f[2], f[3], f[4]) # return flavor's id return f[1] diff --git a/config_tempest/main.py b/config_tempest/main.py index 8a3dcd73..1ee27dd6 100755 --- a/config_tempest/main.py +++ b/config_tempest/main.py @@ -306,6 +306,12 @@ def get_arg_parser(): glance if it's not already there. The name of the image is the leaf name of the path. Default is '%s'""" % C.DEFAULT_IMAGE) + parser.add_argument('--flavor-min-mem', default=C.DEFAULT_FLAVOR_RAM, + type=int, help="""Specify minimum memory for new + flavours, default is '%s'.""" % C.DEFAULT_FLAVOR_RAM) + parser.add_argument('--flavor-min-disk', default=C.DEFAULT_FLAVOR_DISK, + type=int, help="""Specify minimum disk size for new + flavours, default is '%s'.""" % C.DEFAULT_FLAVOR_DISK) parser.add_argument('--network-id', help="""Specify which network with external connectivity should be used by the tests.""") @@ -513,6 +519,8 @@ def config_tempest(**kwargs): users = Users(clients.projects, clients.roles, clients.users, conf) users.create_tempest_users(services.is_service('orchestration')) flavors = Flavors(clients.flavors, kwargs.get('create', False), conf, + kwargs.get('flavor_min_mem', C.DEFAULT_FLAVOR_RAM), + kwargs.get('flavor_min_disk', C.DEFAULT_FLAVOR_DISK), no_rng=kwargs.get('no_rng', False)) flavors.create_tempest_flavors() @@ -569,6 +577,8 @@ def main(): create_accounts_file=args.create_accounts_file, debug=args.debug, deployer_input=args.deployer_input, + flavor_min_mem=args.flavor_min_mem, + flavor_min_disk=args.flavor_min_disk, image_disk_format=args.image_disk_format, image_path=args.image, network_id=args.network_id, diff --git a/config_tempest/tests/test_flavors.py b/config_tempest/tests/test_flavors.py index 31c28bd1..198ff6dd 100644 --- a/config_tempest/tests/test_flavors.py +++ b/config_tempest/tests/test_flavors.py @@ -17,6 +17,7 @@ from fixtures import MonkeyPatch import logging import mock +from config_tempest import constants as C from config_tempest.flavors import Flavors from config_tempest.tests.base import BaseConfigTempestTest @@ -44,7 +45,7 @@ class TestFlavors(BaseConfigTempestTest): mock_function = mock.Mock(return_value=return_value) self.useFixture(MonkeyPatch(self.CLIENT_MOCK + '.list_flavors', mock_function)) - self.Service = Flavors(self.client, True, self.conf) + self.Service = Flavors(self.client, True, self.conf, 64, 1) def test_create_tempest_flavors(self): self.Service.flavor_list = [] @@ -54,8 +55,8 @@ class TestFlavors(BaseConfigTempestTest): self.Service.create_tempest_flavors() self.assertEqual(self.conf.get('compute', 'flavor_ref'), "FakeID") self.assertEqual(self.conf.get('compute', 'flavor_ref_alt'), "FakeID") - calls = [mock.call('m1.nano', ram=64, no_rng=False), - mock.call('m1.micro', ram=128, no_rng=False)] + calls = [mock.call('m1.nano', 64, 1, 1, no_rng=False), + mock.call('m1.micro', 128, 1, 1, no_rng=False)] mock_function.assert_has_calls(calls, any_order=True) def check_call_of_discover_smallest_flavor(self): @@ -65,7 +66,9 @@ class TestFlavors(BaseConfigTempestTest): func2mock = 'config_tempest.flavors.Flavors.discover_smallest_flavor' mock_function = mock.Mock() self.useFixture(MonkeyPatch(func2mock, mock_function)) - self.Service.create_flavor('nano') + self.Service.create_flavor('nano', C.DEFAULT_FLAVOR_RAM, + C.DEFAULT_FLAVOR_VCPUS, + C.DEFAULT_FLAVOR_DISK) calls = [mock.call('nano')] mock_function.assert_has_calls(calls, any_order=True) @@ -95,7 +98,9 @@ class TestFlavors(BaseConfigTempestTest): self.Service.allow_creation = False self.Service.flavor_list = [] try: - self.Service.create_flavor('name') + self.Service.create_flavor('name', C.DEFAULT_FLAVOR_RAM, + C.DEFAULT_FLAVOR_VCPUS, + C.DEFAULT_FLAVOR_DISK) except Exception: return # it should have ended in the except block above @@ -104,7 +109,9 @@ class TestFlavors(BaseConfigTempestTest): # not enough flavors found self.Service.flavor_list = [{'id': 'FAKE', 'name': 'fake_name'}] try: - self.Service.create_flavor('name') + self.Service.create_flavor('name', C.DEFAULT_FLAVOR_RAM, + C.DEFAULT_FLAVOR_VCPUS, + C.DEFAULT_FLAVOR_DISK) except Exception: return # it should have ended in the except block above @@ -119,7 +126,10 @@ class TestFlavors(BaseConfigTempestTest): mock_function = mock.Mock(return_value={}) self.useFixture(MonkeyPatch(client + '.set_flavor_extra_spec', mock_function)) - resp = self.Service.create_flavor(flavor_name="MyID", no_rng=no_rng) + resp = self.Service.create_flavor("MyID", C.DEFAULT_FLAVOR_RAM, + C.DEFAULT_FLAVOR_VCPUS, + C.DEFAULT_FLAVOR_DISK, + no_rng=no_rng) self.assertEqual(resp, return_value['flavor']['id']) return mock_function diff --git a/doc/source/admin/admin_usage.rst b/doc/source/admin/admin_usage.rst index 059d744d..d7d00008 100644 --- a/doc/source/admin/admin_usage.rst +++ b/doc/source/admin/admin_usage.rst @@ -39,6 +39,21 @@ resources (`Flavors`_ and `Users`_) if they don't exist already: --os-cloud devstack-admin \ --create +If a user wants to use a custom image (instead of the default cirros one), +a minimum memory and disk size for new flavors can be defined by +``--flavor-min-mem`` and ``--flavor-min-disk`` arguments. + +.. code-block:: shell-session + + $ discover-tempest-config \ + --image \ + --flavor-min-mem 1024 \ + --flavor-min-disk 10 + +In the example above ``python-tempestconf`` will create *custom* flavor with +1024 MB of RAM and 10 GB of disk size and *custom_alt** flavor with 1024 + 1 MB +of RAM and 10 GB of disk size. + ``python-tempestconf`` can also create a minimal accounts file when ``--create-accounts-file`` is used. It can be useful when a user doesn't have diff --git a/doc/source/user/usage.rst b/doc/source/user/usage.rst index 9ca91542..140cdad8 100644 --- a/doc/source/user/usage.rst +++ b/doc/source/user/usage.rst @@ -14,7 +14,7 @@ of the following: * use ``clouds.yaml`` file and take advantage of ``os-client-config`` support and use a named cloud, see `Examples of usage with a named cloud`_ -If a user doesn't use ``--create``, no resources, which requires admin +If a user doesn't use ``--create``, no resources, which require admin credentials, are created. See `Resources`_ section. @@ -66,8 +66,8 @@ generated ``tempest.conf`` from one of the two following reasons: * ``python-tempestconf`` is able to discover it, but a user wants to set it differently -Values specified as overrides will be set to tempest.conf no matter what if -they were discovered or not. If a section or a key don't exist, they will be +Values specified as overrides will be set to tempest.conf no matter if they +were discovered or not. If a section or a key don't exist, they will be created. In the following example we make the tool to print debugging information, we @@ -110,7 +110,7 @@ A user can define key-value pairs which are not wanted to be written to the generated ``tempest.conf``. This can be useful in case when ``python-tempestconf`` discovers something which is not wanted by a user to have in ``tempest.conf``. If the option is used, ``python-tempestconf`` will -make sure, that the defined values are not written to tempest.conf no matter +make sure that the defined values are not written to tempest.conf no matter if they were discovered or not. .. code-block:: shell-session @@ -120,9 +120,9 @@ if they were discovered or not. --remove section2.key2=value \ --remove section3.key3=value1,value2 -In the following case **all** api_extensions will be removed and tempest.conf -will **not contain** the api_extensions key under compute-feature-enabled -section. +In the following case **all** api_extensions will be removed and +``tempest.conf`` will **not contain** the api_extensions key under +compute-feature-enabled section. .. code-block:: shell-session @@ -190,7 +190,7 @@ links: * `how to generate it? `_ When ``--test-accounts`` argument is used, ``python-tempestconf`` will not -write any credentials to generated tempest.conf file, it will add a +write any credentials to generated ``tempest.conf`` file, it will add a **test_accounts_file** key to **auth** section with value equal to the path provided by the ``--test-accounts`` argument. Also **use_dynamic_credentials** under **auth** section will be set to False as @@ -210,7 +210,7 @@ If you already have the file created, you can run --out etc/tempest.conf \ --test-accounts /path/to/my/accounts.yaml -The generated tempest.conf will look like: +The generated ``tempest.conf`` will look like: .. code-block:: shell-session @@ -272,8 +272,8 @@ Then if you use ``--os-cloud`` argument you can run :command:`discover-tempest-config` **without** setting any OS_* environment variable (for example by sourcing any OpenStack RC file). -``--os-cloud`` defines specifies one of the cloud names located in the -``clouds.yaml`` file. +``--os-cloud`` specifies one of the cloud names located in the ``clouds.yaml`` +file. .. code-block:: shell-session :emphasize-lines: 3 @@ -308,7 +308,7 @@ look like: Resources --------- -Without specifying ``--create`` argument, no resources which requires admin +Without specifying ``--create`` argument, no resources which require admin credentials are crated during the ``python-tempestconf`` execution. For the documentation on how to use ``--create`` argument see `Admin User Guide`_ @@ -405,10 +405,25 @@ image. Flavors +++++++ -``python-tempestconf`` looks for these two flavors: +``python-tempestconf`` looks by default for these two flavors: - * m1.nano with 64 MB of RAM, which will be set as **compute.flavor_ref** - * m1.micro with 128 MB of RAM, which will be set as **compute.flavor_alt_ref** + * *m1.nano* with 64 MB of RAM, which will be set as **compute.flavor_ref** + * *m1.micro* with 128 MB of RAM, which will be set as + **compute.flavor_alt_ref** + +If a user used ``--flavor-min-mem`` argument, ``python-tempestconf`` will look +for these two flavors: + + * *custom* + * *custom_alt* + + .. note:: + + ``python-tempestconf`` looks for flavors by name, so if a user has had + a flavor with name *custom*/*custom_alt* already created, those flavors' + IDs will be set as **compute.flavor_ref**/**compute.flavor_ref_alt** + without checking if theirs RAM size is equal to the one specified by + ``--flavor-min-mem``. If they are not found and ``--create`` argument is not used, the tool will try to auto discover two smallest flavors available in the system. If at least two @@ -435,7 +450,7 @@ The generated tempest.conf will look like: flavor_alt_ref = -In the following example, a `override`_ option specifies **compute.flavor_ref** +In the following example, an `override`_ option specifies **compute.flavor_ref** ID, which if it's found, the tool continues with looking for a **m1.micro** flavor to be set as **compute.flavor_alt_ref** as was explained above. @@ -447,4 +462,4 @@ flavor to be set as **compute.flavor_alt_ref** as was explained above. .. note:: If the **compute.flavor_ref** ID is not found, the tool ends with an - exception. \ No newline at end of file + exception.