Heat: support create and attach volume in heat type context 61/37961/5
authorJingLu5 <lvjing5@huawei.com>
Mon, 24 Jul 2017 01:39:57 +0000 (01:39 +0000)
committerJing Lu <lvjing5@huawei.com>
Sat, 29 Jul 2017 04:39:36 +0000 (04:39 +0000)
JIRA: YARDSTICK-756

Some test scenarios require VM with volume attached.
This work is about supporting create and attach volume in heat type context.

context:
  name: demo
  image: cirros-0.3.5
  flavor: yardstick-flavor
  user: cirros

  placement_groups:
    pgrp1:
      policy: "availability"

  servers:
    athena:
      floating_ip: true
      # per-vm inline volume definition. if no volume size specified, then this
      # volume should be an existing volume in the openstack environment
      volume: yardstick-volume
      placement: "pgrp1"
    ares:
      # per-vm inline volume definition. if volume size is specified, then this
      # volume will be crated and attach to the vm
      volume:
        name: test-volume
        size: 10
      # volume mountpoint is also configurable
      volume_mountpoint: /dev/vdb
      placement: "pgrp1"

  networks:
    test:
      cidr: '10.0.1.0/24'

Change-Id: Ief87b313980a59eac229eb4780d93ffc929ceb66
Signed-off-by: JingLu5 <lvjing5@huawei.com>
samples/fio_volume.yaml [new file with mode: 0644]
yardstick/benchmark/contexts/model.py
yardstick/common/openstack_utils.py
yardstick/orchestrator/heat.py

diff --git a/samples/fio_volume.yaml b/samples/fio_volume.yaml
new file mode 100644 (file)
index 0000000..edb3837
--- /dev/null
@@ -0,0 +1,74 @@
+##############################################################################
+# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+---
+# Sample benchmark task config file
+# measure storage performance using fio
+#
+# For this sample just like running the command below on the test vm and
+# getting benchmark info back to the yardstick.
+#
+# sudo fio -filename=/home/ubuntu/data.raw -bs=4k -ipdepth=1 -rw=rw \
+#          -ramp_time=10 -runtime=60 -name=yardstick-fio -ioengine=libaio \
+#          -direct=1 -group_reporting -numjobs=1 -time_based \
+#          --output-format=json
+
+schema: "yardstick:task:0.1"
+
+{% set rw = rw or "randrw" %}
+{% set bs = bs or "8k" %}
+{% set size = size or "100g" %}
+{% set rwmixwrite = rwmixwrite or "50" %}
+{% set numjobs = numjobs or "1" %}
+{% set direct = direct or "1" %}
+
+scenarios:
+-
+  type: Fio
+  options:
+    filename: /dev/vdb
+    bs: {{bs}}
+    rw: {{rw}}
+    size: {{size}}
+    rwmixwrite: {{rwmixwrite}}
+    numjobs: {{numjobs}}
+    direct: {{direct}}
+    ramp_time: 10
+
+  host: fio.fio_volume
+
+  runner:
+    type: Duration
+    duration: 60
+    interval: 1
+
+  sla:
+    read_bw: 6000
+    read_iops: 1500
+    read_lat: 500.1
+    write_bw: 6000
+    write_iops: 1500
+    write_lat: 500.1
+    action: monitor
+
+context:
+  name: fio_volume
+  image: yardstick-image
+  flavor: yardstick-flavor
+  user: ubuntu
+  servers:
+    fio:
+      volume:
+        name: fio-volume
+        size: 200
+      volume_mountpoint: "/dev/vdb"
+      floating_ip: true
+  networks:
+    test:
+      cidr: "10.0.1.0/24"
+      port_security_enabled: true
index ec47432..0bf85d0 100644 (file)
@@ -184,6 +184,14 @@ class Server(Object):     # pragma: no cover
             self.placement_groups.append(pg)
             pg.add_member(self.stack_name)
 
             self.placement_groups.append(pg)
             pg.add_member(self.stack_name)
 
+        self.volume = None
+        if "volume" in attrs:
+            self.volume = attrs.get("volume")
+
+        self.volume_mountpoint = None
+        if "volume_mountpoint" in attrs:
+            self.volume_mountpoint = attrs.get("volume_mountpoint")
+
         # support servergroup attr
         self.server_group = None
         sg = attrs.get("server_group")
         # support servergroup attr
         self.server_group = None
         sg = attrs.get("server_group")
@@ -283,6 +291,17 @@ class Server(Object):     # pragma: no cover
             else:
                 self.flavor_name = self.flavor
 
             else:
                 self.flavor_name = self.flavor
 
+        if self.volume:
+            if isinstance(self.volume, dict):
+                self.volume["name"] = \
+                    self.volume.setdefault("name", server_name + "-volume")
+                template.add_volume(**self.volume)
+                template.add_volume_attachment(server_name, self.volume["name"],
+                                               mountpoint=self.volume_mountpoint)
+            else:
+                template.add_volume_attachment(server_name, self.volume,
+                                               mountpoint=self.volume_mountpoint)
+
         template.add_server(server_name, self.image, flavor=self.flavor_name,
                             flavors=self.context.flavors,
                             ports=port_name_list,
         template.add_server(server_name, self.image, flavor=self.flavor_name,
                             flavors=self.context.flavors,
                             ports=port_name_list,
index 8787e60..f027b79 100644 (file)
@@ -15,6 +15,7 @@ import logging
 
 from keystoneauth1 import loading
 from keystoneauth1 import session
 
 from keystoneauth1 import loading
 from keystoneauth1 import session
+from cinderclient import client as cinderclient
 from novaclient import client as novaclient
 from glanceclient import client as glanceclient
 from neutronclient.neutron import client as neutronclient
 from novaclient import client as novaclient
 from glanceclient import client as glanceclient
 from neutronclient.neutron import client as neutronclient
@@ -108,6 +109,21 @@ def get_heat_api_version():     # pragma: no cover
         return api_version
 
 
         return api_version
 
 
+def get_cinder_client_version():      # pragma: no cover
+    try:
+        api_version = os.environ['OS_VOLUME_API_VERSION']
+    except KeyError:
+        return DEFAULT_API_VERSION
+    else:
+        log.info("OS_VOLUME_API_VERSION is set in env as '%s'", api_version)
+        return api_version
+
+
+def get_cinder_client():      # pragma: no cover
+    sess = get_session()
+    return cinderclient.Client(get_cinder_client_version(), session=sess)
+
+
 def get_nova_client_version():      # pragma: no cover
     try:
         api_version = os.environ['OS_COMPUTE_API_VERSION']
 def get_nova_client_version():      # pragma: no cover
     try:
         api_version = os.environ['OS_COMPUTE_API_VERSION']
@@ -430,3 +446,11 @@ def get_port_id_by_ip(neutron_client, ip_address):      # pragma: no cover
 def get_image_id(glance_client, image_name):    # pragma: no cover
     images = glance_client.images.list()
     return next((i.id for i in images if i.name == image_name), None)
 def get_image_id(glance_client, image_name):    # pragma: no cover
     images = glance_client.images.list()
     return next((i.id for i in images if i.name == image_name), None)
+
+
+# *********************************************
+#   CINDER
+# *********************************************
+def get_volume_id(volume_name):    # pragma: no cover
+    volumes = get_cinder_client().volumes.list()
+    return next((v.id for v in volumes if v.name == volume_name), None)
index 95ca0ad..05e3597 100644 (file)
@@ -230,6 +230,40 @@ name (i.e. %s).\
             'value': {'get_resource': name}
         }
 
             'value': {'get_resource': name}
         }
 
+    def add_volume(self, name, size=10):
+        """add to the template a volume description"""
+        log.debug("adding Cinder::Volume '%s' size '%d' ", name, size)
+
+        self.resources[name] = {
+            'type': 'OS::Cinder::Volume',
+            'properties': {'name': name,
+                           'size': size}
+        }
+
+        self._template['outputs'][name] = {
+            'description': 'Volume %s ID' % name,
+            'value': {'get_resource': name}
+        }
+
+    def add_volume_attachment(self, server_name, volume_name, mountpoint=None):
+        """add to the template an association of volume to instance"""
+        log.debug("adding Cinder::VolumeAttachment server '%s' volume '%s' ", server_name,
+                  volume_name)
+
+        name = "%s-%s" % (server_name, volume_name)
+
+        volume_id = op_utils.get_volume_id(volume_name)
+        if not volume_id:
+            volume_id = {'get_resource': volume_name}
+        self.resources[name] = {
+            'type': 'OS::Cinder::VolumeAttachment',
+            'properties': {'instance_uuid': {'get_resource': server_name},
+                           'volume_id': volume_id}
+        }
+
+        if mountpoint:
+            self.resources[name]['properties']['mountpoint'] = mountpoint
+
     def add_network(self, name, physical_network='physnet1', provider=None,
                     segmentation_id=None, port_security_enabled=None):
         """add to the template a Neutron Net"""
     def add_network(self, name, physical_network='physnet1', provider=None,
                     segmentation_id=None, port_security_enabled=None):
         """add to the template a Neutron Net"""