Add new Kubernetes resource kind: "CustomResourceDefinition"
[yardstick.git] / yardstick / benchmark / contexts / model.py
index 06538d8..a55c11f 100644 (file)
 
 """
 from __future__ import absolute_import
+
+import six
+import logging
+
+from collections import Mapping
 from six.moves import range
 
+from yardstick.common import constants as consts
+
+
+LOG = logging.getLogger(__name__)
+
 
 class Object(object):
     """Base class for classes in the logical model
@@ -104,18 +114,48 @@ class Network(Object):
         self.stack_name = context.name + "-" + self.name
         self.subnet_stack_name = self.stack_name + "-subnet"
         self.subnet_cidr = attrs.get('cidr', '10.0.1.0/24')
+        self.enable_dhcp = attrs.get('enable_dhcp', 'true')
         self.router = None
         self.physical_network = attrs.get('physical_network', 'physnet1')
         self.provider = attrs.get('provider')
         self.segmentation_id = attrs.get('segmentation_id')
         self.network_type = attrs.get('network_type')
-
-        if "external_network" in attrs:
-            self.router = Router("router", self.name,
-                                 context, attrs["external_network"])
-        self.vld_id = attrs.get("vld_id")
-
-        Network.list.append(self)
+        self.port_security_enabled = attrs.get('port_security_enabled')
+        self.vnic_type = attrs.get('vnic_type', 'normal')
+        self.allowed_address_pairs = attrs.get('allowed_address_pairs', [])
+        try:
+            # we require 'null' or '' to disable setting gateway_ip
+            self.gateway_ip = attrs['gateway_ip']
+        except KeyError:
+            # default to explicit None
+            self.gateway_ip = None
+        else:
+            # null is None in YAML, so we have to convert back to string
+            if self.gateway_ip is None:
+                self.gateway_ip = "null"
+
+        self.net_flags = attrs.get('net_flags', {})
+        if self.is_existing():
+            self.subnet = attrs.get('subnet')
+            if not self.subnet:
+                raise Warning('No subnet set in existing netwrok!')
+        else:
+            if "external_network" in attrs:
+                self.router = Router("router", self.name,
+                                     context, attrs["external_network"])
+            Network.list.append(self)
+
+    def is_existing(self):
+        net_is_existing = self.net_flags.get(consts.IS_EXISTING)
+        if net_is_existing and not isinstance(net_is_existing, bool):
+            raise SyntaxError('Network flags should be bool type!')
+        return net_is_existing
+
+    def is_public(self):
+        net_is_public = self.net_flags.get(consts.IS_PUBLIC)
+        if net_is_public and not isinstance(net_is_public, bool):
+            raise SyntaxError('Network flags should be bool type!')
+        return net_is_public
 
     def has_route_to(self, network_name):
         """determines if this network has a route to the named network"""
@@ -171,6 +211,14 @@ class Server(Object):     # pragma: no cover
             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")
@@ -187,6 +235,7 @@ class Server(Object):     # pragma: no cover
             self.instances = attrs["instances"]
 
         # dict with key network name, each item is a dict with port name and ip
+        self.network_ports = attrs.get("network_ports", {})
         self.ports = {}
 
         self.floating_ip = None
@@ -209,9 +258,30 @@ class Server(Object):     # pragma: no cover
             self._flavor = attrs["flavor"]
 
         self.user_data = attrs.get('user_data', '')
+        self.availability_zone = attrs.get('availability_zone')
 
         Server.list.append(self)
 
+    def override_ip(self, network_name, port):
+        def find_port_overrides():
+            for p in ports:
+                # p can be string or dict
+                # we can't just use p[port['port'] in case p is a string
+                # and port['port'] is an int?
+                if isinstance(p, Mapping):
+                    g = p.get(port['port'])
+                    # filter out empty dicts
+                    if g:
+                        yield g
+
+        ports = self.network_ports.get(network_name, [])
+        intf = self.interfaces[port['port']]
+        for override in find_port_overrides():
+            intf['local_ip'] = override.get('local_ip', intf['local_ip'])
+            intf['netmask'] = override.get('netmask', intf['netmask'])
+            # only use the first value
+            break
+
     @property
     def image(self):
         """returns a server's image name"""
@@ -232,29 +302,64 @@ class Server(Object):     # pragma: no cover
         """adds to the template one server and corresponding resources"""
         port_name_list = []
         for network in networks:
-            port_name = server_name + "-" + network.name + "-port"
-            self.ports[network.name] = {"stack_name": port_name}
-            template.add_port(port_name, network.stack_name,
-                              network.subnet_stack_name,
-                              sec_group_id=self.secgroup_name,
-                              provider=network.provider)
-            port_name_list.append(port_name)
-
-            if self.floating_ip:
-                external_network = self.floating_ip["external_network"]
-                if network.has_route_to(external_network):
-                    self.floating_ip["stack_name"] = server_name + "-fip"
-                    template.add_floating_ip(self.floating_ip["stack_name"],
-                                             external_network,
-                                             port_name,
-                                             network.router.stack_if_name,
-                                             self.secgroup_name)
-                    self.floating_ip_assoc["stack_name"] = \
-                        server_name + "-fip-assoc"
-                    template.add_floating_ip_association(
-                        self.floating_ip_assoc["stack_name"],
-                        self.floating_ip["stack_name"],
-                        port_name)
+            # if explicit mapping skip unused networks
+            if self.network_ports:
+                try:
+                    ports = self.network_ports[network.name]
+                except KeyError:
+                    # no port for this network
+                    continue
+                else:
+                    if isinstance(ports, six.string_types):
+                        # because strings are iterable we have to check specifically
+                        raise SyntaxError("network_port must be a list '{}'".format(ports))
+                    # convert port subdicts into their just port name
+                    # port subdicts are used to override Heat IP address,
+                    # but we just need the port name
+                    # we allow duplicates here and let Heat raise the error
+                    ports = [next(iter(p)) if isinstance(p, dict) else p for p in ports]
+            # otherwise add a port for every network with port name as network name
+            else:
+                ports = [network.name]
+            net_flags = network.net_flags
+            for port in ports:
+                port_name = "{0}-{1}-port".format(server_name, port)
+                port_info = {"stack_name": port_name, "port": port}
+                if net_flags:
+                    port_info['net_flags'] = net_flags
+                self.ports.setdefault(network.name, []).append(port_info)
+                # we can't use secgroups if port_security_enabled is False
+                if network.port_security_enabled is False:
+                    sec_group_id = None
+                else:
+                    # if port_security_enabled is None we still need to add to secgroup
+                    sec_group_id = self.secgroup_name
+                # don't refactor to pass in network object, that causes JSON
+                # circular ref encode errors
+                template.add_port(port_name, network,
+                                  sec_group_id=sec_group_id,
+                                  provider=network.provider,
+                                  allowed_address_pairs=network.allowed_address_pairs)
+                if network.is_public():
+                    port_name_list.insert(0, port_name)
+                else:
+                    port_name_list.append(port_name)
+
+                if self.floating_ip:
+                    external_network = self.floating_ip["external_network"]
+                    if network.has_route_to(external_network):
+                        self.floating_ip["stack_name"] = server_name + "-fip"
+                        template.add_floating_ip(self.floating_ip["stack_name"],
+                                                 external_network,
+                                                 port_name,
+                                                 network.router.stack_if_name,
+                                                 sec_group_id)
+                        self.floating_ip_assoc["stack_name"] = \
+                            server_name + "-fip-assoc"
+                        template.add_floating_ip_association(
+                            self.floating_ip_assoc["stack_name"],
+                            self.floating_ip["stack_name"],
+                            port_name)
         if self.flavor:
             if isinstance(self.flavor, dict):
                 self.flavor["name"] = \
@@ -264,13 +369,22 @@ class Server(Object):     # pragma: no cover
             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,
-                            user=self.user,
-                            key_name=self.keypair_name,
-                            user_data=self.user_data,
-                            scheduler_hints=scheduler_hints)
+                            flavors=self.context.flavors, ports=port_name_list,
+                            scheduler_hints=scheduler_hints, user=self.user,
+                            key_name=self.keypair_name, user_data=self.user_data,
+                            availability_zone=self.availability_zone)
 
     def add_to_template(self, template, networks, scheduler_hints=None):
         """adds to the template one or more servers (instances)"""