Replace nova create instance with shade client. 19/59119/1
authorShobhi Jain <shobhi.jain@intel.com>
Thu, 22 Mar 2018 16:45:13 +0000 (16:45 +0000)
committerEmma Foley <emma.l.foley@intel.com>
Wed, 27 Jun 2018 15:58:45 +0000 (16:58 +0100)
Remove redundant functions:
*get_instances
*get_instance_status
*get_instance_by_name

Function create_instance_and_wait_for_active now uses shade client.

JIRA: YARDSTICK-1088

Change-Id: I766da333dabbb1b83a7b4ede4bf73567ec70eb8c
Signed-off-by: Shobhi Jain <shobhi.jain@intel.com>
(cherry picked from commit 069d7a13d1f8b6b465ae164e2a440c25270f7e8e)

yardstick/benchmark/scenarios/lib/create_server.py
yardstick/common/exceptions.py
yardstick/common/openstack_utils.py
yardstick/tests/unit/benchmark/scenarios/lib/test_create_server.py
yardstick/tests/unit/common/test_openstack_utils.py

index 31ba18e..e2748ae 100644 (file)
@@ -6,14 +6,11 @@
 # which accompanies this distribution, and is available at
 # http://www.apache.org/licenses/LICENSE-2.0
 ##############################################################################
-
-from __future__ import print_function
-from __future__ import absolute_import
-
 import logging
 
 from yardstick.benchmark.scenarios import base
-import yardstick.common.openstack_utils as op_utils
+from yardstick.common import openstack_utils
+from yardstick.common import exceptions
 
 LOG = logging.getLogger(__name__)
 
@@ -26,15 +23,27 @@ class CreateServer(base.Scenario):
     def __init__(self, scenario_cfg, context_cfg):
         self.scenario_cfg = scenario_cfg
         self.context_cfg = context_cfg
-        self.options = self.scenario_cfg['options']
-
-        self.image_name = self.options.get("image_name", None)
-        self.flavor_name = self.options.get("flavor_name", None)
-        self.openstack = self.options.get("openstack_paras", None)
-
-        self.glance_client = op_utils.get_glance_client()
-        self.neutron_client = op_utils.get_neutron_client()
-        self.nova_client = op_utils.get_nova_client()
+        self.options = self.scenario_cfg["options"]
+
+        self.name = self.options["name"]
+        self.image = self.options["image"]
+        self.flavor = self.options["flavor"]
+        self.auto_ip = self.options.get("auto_ip", True)
+        self.ips = self.options.get("ips")
+        self.ip_pool = self.options.get("ip_pool")
+        self.root_volume = self.options.get("root_volume")
+        self.terminate_volume = self.options.get("terminate_volume", False)
+        self.wait = self.options.get("wait", True)
+        self.timeout = self.options.get("timeout", 180)
+        self.reuse_ips = self.options.get("reuse_ips", True)
+        self.network = self.options.get("network")
+        self.boot_from_volume = self.options.get("boot_from_volume", False)
+        self.volume_size = self.options.get("volume_size", "20")
+        self.boot_volume = self.options.get("boot_volume")
+        self.volumes = self.options.get("volumes")
+        self.nat_destination = self.options.get("nat_destination")
+
+        self.shade_client = openstack_utils.get_shade_client()
 
         self.setup_done = False
 
@@ -49,26 +58,23 @@ class CreateServer(base.Scenario):
         if not self.setup_done:
             self.setup()
 
-        if self.image_name is not None:
-            self.openstack['image'] = op_utils.get_image_id(self.glance_client,
-                                                            self.image_name)
-        if self.flavor_name is not None:
-            self.openstack['flavor'] = op_utils.get_flavor_id(self.nova_client,
-                                                              self.flavor_name)
-
-        vm = op_utils.create_instance_and_wait_for_active(self.openstack)
-
-        if vm:
-            result.update({"instance_create": 1})
-            LOG.info("Create server successful!")
-        else:
+        server = openstack_utils.create_instance_and_wait_for_active(
+            self.shade_client, self.name, self.image,
+            self.flavor, auto_ip=self.auto_ip, ips=self.ips,
+            ip_pool=self.ip_pool, root_volume=self.root_volume,
+            terminate_volume=self.terminate_volume, wait=self.wait,
+            timeout=self.timeout, reuse_ips=self.reuse_ips,
+            network=self.network, boot_from_volume=self.boot_from_volume,
+            volume_size=self.volume_size, boot_volume=self.boot_volume,
+            volumes=self.volumes, nat_destination=self.nat_destination)
+
+        if not server:
             result.update({"instance_create": 0})
             LOG.error("Create server failed!")
+            raise exceptions.ScenarioCreateServerError
 
-        try:
-            keys = self.scenario_cfg.get('output', '').split()
-        except KeyError:
-            pass
-        else:
-            values = [vm.id]
-            return self._push_to_outputs(keys, values)
+        result.update({"instance_create": 1})
+        LOG.info("Create instance successful!")
+        keys = self.scenario_cfg.get("output", '').split()
+        values = [server["id"]]
+        return self._push_to_outputs(keys, values)
index 319c75e..219d039 100644 (file)
@@ -218,6 +218,10 @@ class ScenarioDeleteNetworkError(YardstickException):
     message = 'Delete Neutron Network Scenario failed'
 
 
+class ScenarioCreateServerError(YardstickException):
+    message = 'Nova Create Server Scenario failed'
+
+
 class ApiServerError(YardstickException):
     message = 'An unkown exception happened to Api Server!'
 
index 0d6afc5..c2ef267 100644 (file)
@@ -163,28 +163,6 @@ def get_shade_client():
 # *********************************************
 #   NOVA
 # *********************************************
-def get_instances(nova_client):
-    try:
-        return nova_client.servers.list(search_opts={'all_tenants': 1})
-    except Exception:  # pylint: disable=broad-except
-        log.exception("Error [get_instances(nova_client)]")
-
-
-def get_instance_status(nova_client, instance):     # pragma: no cover
-    try:
-        return nova_client.servers.get(instance.id).status
-    except Exception:  # pylint: disable=broad-except
-        log.exception("Error [get_instance_status(nova_client)]")
-
-
-def get_instance_by_name(nova_client, instance_name):   # pragma: no cover
-    try:
-        return nova_client.servers.find(name=instance_name)
-    except Exception:  # pylint: disable=broad-except
-        log.exception("Error [get_instance_by_name(nova_client, '%s')]",
-                      instance_name)
-
-
 def get_aggregates(nova_client):    # pragma: no cover
     try:
         return nova_client.aggregates.list()
@@ -273,21 +251,91 @@ def create_instance(json_body):    # pragma: no cover
         return None
 
 
-def create_instance_and_wait_for_active(json_body):    # pragma: no cover
-    SLEEP = 3
-    VM_BOOT_TIMEOUT = 180
-    nova_client = get_nova_client()
-    instance = create_instance(json_body)
-    for _ in range(int(VM_BOOT_TIMEOUT / SLEEP)):
-        status = get_instance_status(nova_client, instance)
-        if status.lower() == "active":
-            return instance
-        elif status.lower() == "error":
-            log.error("The instance went to ERROR status.")
-            return None
-        time.sleep(SLEEP)
-    log.error("Timeout booting the instance.")
-    return None
+def create_instance_and_wait_for_active(shade_client, name, image,
+                                        flavor, auto_ip=True, ips=None,
+                                        ip_pool=None, root_volume=None,
+                                        terminate_volume=False, wait=True,
+                                        timeout=180, reuse_ips=True,
+                                        network=None, boot_from_volume=False,
+                                        volume_size='20', boot_volume=None,
+                                        volumes=None, nat_destination=None,
+                                        **kwargs):
+    """Create a virtual server instance.
+
+    :param name:(string) Name of the server.
+    :param image:(dict) Image dict, name or ID to boot with. Image is required
+                 unless boot_volume is given.
+    :param flavor:(dict) Flavor dict, name or ID to boot onto.
+    :param auto_ip: Whether to take actions to find a routable IP for
+                    the server.
+    :param ips: List of IPs to attach to the server.
+    :param ip_pool:(string) Name of the network or floating IP pool to get an
+                   address from.
+    :param root_volume:(string) Name or ID of a volume to boot from.
+                       (defaults to None - deprecated, use boot_volume)
+    :param boot_volume:(string) Name or ID of a volume to boot from.
+    :param terminate_volume:(bool) If booting from a volume, whether it should
+                            be deleted when the server is destroyed.
+    :param volumes:(optional) A list of volumes to attach to the server.
+    :param wait:(optional) Wait for the address to appear as assigned to the server.
+    :param timeout: Seconds to wait, defaults to 60.
+    :param reuse_ips:(bool)Whether to attempt to reuse pre-existing
+                     floating ips should a floating IP be needed.
+    :param network:(dict) Network dict or name or ID to attach the server to.
+                   Mutually exclusive with the nics parameter. Can also be be
+                   a list of network names or IDs or network dicts.
+    :param boot_from_volume:(bool) Whether to boot from volume. 'boot_volume'
+                            implies True, but boot_from_volume=True with
+                            no boot_volume is valid and will create a
+                            volume from the image and use that.
+    :param volume_size: When booting an image from volume, how big should
+                        the created volume be?
+    :param nat_destination: Which network should a created floating IP
+                            be attached to, if it's not possible to infer from
+                            the cloud's configuration.
+    :param meta:(optional) A dict of arbitrary key/value metadata to store for
+                this server. Both keys and values must be <=255 characters.
+    :param reservation_id: A UUID for the set of servers being requested.
+    :param min_count:(optional extension) The minimum number of servers to
+                     launch.
+    :param max_count:(optional extension) The maximum number of servers to
+                     launch.
+    :param security_groups: A list of security group names.
+    :param userdata: User data to pass to be exposed by the metadata server
+                     this can be a file type object as well or a string.
+    :param key_name:(optional extension) Name of previously created keypair to
+                    inject into the instance.
+    :param availability_zone: Name of the availability zone for instance
+                              placement.
+    :param block_device_mapping:(optional) A dict of block device mappings for
+                                this server.
+    :param block_device_mapping_v2:(optional) A dict of block device mappings
+                                   for this server.
+    :param nics:(optional extension) An ordered list of nics to be added to
+                 this server, with information about connected networks, fixed
+                 IPs, port etc.
+    :param scheduler_hints:(optional extension) Arbitrary key-value pairs
+                           specified by the client to help boot an instance.
+    :param config_drive:(optional extension) Value for config drive either
+                         boolean, or volume-id.
+    :param disk_config:(optional extension) Control how the disk is partitioned
+                       when the server is created. Possible values are 'AUTO'
+                       or 'MANUAL'.
+    :param admin_pass:(optional extension) Add a user supplied admin password.
+
+    :returns: The created server.
+    """
+    try:
+        return shade_client.create_server(
+            name, image, flavor, auto_ip=auto_ip, ips=ips, ip_pool=ip_pool,
+            root_volume=root_volume, terminate_volume=terminate_volume,
+            wait=wait, timeout=timeout, reuse_ips=reuse_ips, network=network,
+            boot_from_volume=boot_from_volume, volume_size=volume_size,
+            boot_volume=boot_volume, volumes=volumes,
+            nat_destination=nat_destination, **kwargs)
+    except exc.OpenStackCloudException as o_exc:
+        log.error("Error [create_instance(shade_client)]. "
+                  "Exception message, '%s'", o_exc.orig_message)
 
 
 def attach_server_volume(server_id, volume_id,
index 9d6d8cb..b587851 100644 (file)
@@ -6,29 +6,54 @@
 # which accompanies this distribution, and is available at
 # http://www.apache.org/licenses/LICENSE-2.0
 ##############################################################################
+from oslo_utils import uuidutils
 import unittest
 import mock
 
-from yardstick.benchmark.scenarios.lib.create_server import CreateServer
+from yardstick.common import openstack_utils
+from yardstick.common import exceptions
+from yardstick.benchmark.scenarios.lib import create_server
 
 
 class CreateServerTestCase(unittest.TestCase):
 
-    @mock.patch('yardstick.common.openstack_utils.create_instance_and_wait_for_active')
-    @mock.patch('yardstick.common.openstack_utils.get_nova_client')
-    @mock.patch('yardstick.common.openstack_utils.get_glance_client')
-    @mock.patch('yardstick.common.openstack_utils.get_neutron_client')
-    def test_create_server(self, mock_get_nova_client, mock_get_neutron_client,
-                           mock_get_glance_client, mock_create_instance_and_wait_for_active):
-        scenario_cfg = {
-            'options': {
-                'openstack_paras': 'example'
-            },
-            'output': 'server'
-        }
-        obj = CreateServer(scenario_cfg, {})
-        obj.run({})
-        mock_get_nova_client.assert_called_once()
-        mock_get_glance_client.assert_called_once()
-        mock_get_neutron_client.assert_called_once()
-        mock_create_instance_and_wait_for_active.assert_called_once()
+    def setUp(self):
+
+        self._mock_create_instance_and_wait_for_active = mock.patch.object(
+            openstack_utils, 'create_instance_and_wait_for_active')
+        self.mock_create_instance_and_wait_for_active = (
+            self._mock_create_instance_and_wait_for_active.start())
+        self._mock_get_shade_client = mock.patch.object(
+            openstack_utils, 'get_shade_client')
+        self.mock_get_shade_client = self._mock_get_shade_client.start()
+        self._mock_log = mock.patch.object(create_server, 'LOG')
+        self.mock_log = self._mock_log.start()
+        self.args = {
+            'options': {'name': 'server-name', 'image': 'image-name',
+                        'flavor': 'flavor-name'}}
+        self.result = {}
+
+        self.addCleanup(self._stop_mock)
+        self.cserver_obj = create_server.CreateServer(self.args, mock.ANY)
+
+    def _stop_mock(self):
+        self._mock_create_instance_and_wait_for_active.stop()
+        self._mock_get_shade_client.stop()
+        self._mock_log.stop()
+
+    def test_run(self):
+        _uuid = uuidutils.generate_uuid()
+        self.cserver_obj.scenario_cfg = {'output': 'id'}
+        self.mock_create_instance_and_wait_for_active.return_value = (
+            {'name': 'server-name', 'flavor': 'flavor-name', 'id': _uuid})
+        output = self.cserver_obj.run(self.result)
+        self.assertEqual({'instance_create': 1}, self.result)
+        self.assertEqual({'id': _uuid}, output)
+        self.mock_log.info.asset_called_once_with('Create server successful!')
+
+    def test_run_fail(self):
+        self.mock_create_instance_and_wait_for_active.return_value = None
+        with self.assertRaises(exceptions.ScenarioCreateServerError):
+            self.cserver_obj.run(self.result)
+        self.assertEqual({'instance_create': 0}, self.result)
+        self.mock_log.error.assert_called_once_with('Create server failed!')
index f03f251..a7c5aa5 100644 (file)
@@ -337,3 +337,32 @@ class SecurityGroupTestCase(unittest.TestCase):
         mock_create_security_group_rule.assert_called()
         self.mock_shade_client.delete_security_group(self.sg_name)
         self.assertEqual(self._uuid, output)
+
+# *********************************************
+#   NOVA
+# *********************************************
+
+
+class CreateInstanceTestCase(unittest.TestCase):
+
+    def test_create_instance_and_wait_for_active(self):
+        self.mock_shade_client = mock.Mock()
+        name = 'server_name'
+        image = 'image_name'
+        flavor = 'flavor_name'
+        self.mock_shade_client.create_server.return_value = (
+            {'name': name, 'image': image, 'flavor': flavor})
+        output = openstack_utils.create_instance_and_wait_for_active(
+            self.mock_shade_client, name, image, flavor)
+        self.assertEqual(
+            {'name': name, 'image': image, 'flavor': flavor}, output)
+
+    @mock.patch.object(openstack_utils, 'log')
+    def test_create_instance_and_wait_for_active_fail(self, mock_logger):
+        self.mock_shade_client = mock.Mock()
+        self.mock_shade_client.create_server.side_effect = (
+            exc.OpenStackCloudException('error message'))
+        output = openstack_utils.create_instance_and_wait_for_active(
+            self.mock_shade_client, 'server_name', 'image_name', 'flavor_name')
+        mock_logger.error.assert_called_once()
+        self.assertIsNone(output)