Merge "Open storperf testcase to huawei-pod2"
[yardstick.git] / yardstick / benchmark / contexts / model.py
index d31f4af..6601ecf 100644 (file)
 """ Logical model
 
 """
 """ Logical model
 
 """
+from __future__ import absolute_import
+from six.moves import range
 
 
 class Object(object):
 
 
 class Object(object):
-    '''Base class for classes in the logical model
+    """Base class for classes in the logical model
     Contains common attributes and methods
     Contains common attributes and methods
-    '''
+    """
+
     def __init__(self, name, context):
         # model identities and reference
         self.name = name
     def __init__(self, name, context):
         # model identities and reference
         self.name = name
@@ -27,15 +30,15 @@ class Object(object):
 
     @property
     def dn(self):
 
     @property
     def dn(self):
-        '''returns distinguished name for object'''
+        """returns distinguished name for object"""
         return self.name + "." + self._context.name
 
 
 class PlacementGroup(Object):
         return self.name + "." + self._context.name
 
 
 class PlacementGroup(Object):
-    '''Class that represents a placement group in the logical model
+    """Class that represents a placement group in the logical model
     Concept comes from the OVF specification. Policy should be one of
     "availability" or "affinity (there are more but they are not supported)"
     Concept comes from the OVF specification. Policy should be one of
     "availability" or "affinity (there are more but they are not supported)"
-    '''
+    """
     map = {}
 
     def __init__(self, name, context, policy):
     map = {}
 
     def __init__(self, name, context, policy):
@@ -53,16 +56,39 @@ class PlacementGroup(Object):
 
     @staticmethod
     def get(name):
 
     @staticmethod
     def get(name):
-        if name in PlacementGroup.map:
-            return PlacementGroup.map[name]
-        else:
-            return None
+        return PlacementGroup.map.get(name)
+
+
+class ServerGroup(Object):     # pragma: no cover
+    """Class that represents a server group in the logical model
+    Policy should be one of "anti-affinity" or "affinity"
+    """
+    map = {}
+
+    def __init__(self, name, context, policy):
+        super(ServerGroup, self).__init__(name, context)
+        if policy not in {"affinity", "anti-affinity"}:
+            raise ValueError("server group '%s', policy '%s' is not valid" %
+                             (name, policy))
+        self.name = name
+        self.members = set()
+        self.stack_name = context.name + "-" + name
+        self.policy = policy
+        ServerGroup.map[name] = self
+
+    def add_member(self, name):
+        self.members.add(name)
+
+    @staticmethod
+    def get(name):
+        return ServerGroup.map.get(name)
 
 
 class Router(Object):
 
 
 class Router(Object):
-    '''Class that represents a router in the logical model'''
+    """Class that represents a router in the logical model"""
+
     def __init__(self, name, network_name, context, external_gateway_info):
     def __init__(self, name, network_name, context, external_gateway_info):
-        super(self.__class__, self).__init__(name, context)
+        super(Router, self).__init__(name, context)
 
         self.stack_name = context.name + "-" + network_name + "-" + self.name
         self.stack_if_name = self.stack_name + "-if0"
 
         self.stack_name = context.name + "-" + network_name + "-" + self.name
         self.stack_if_name = self.stack_name + "-if0"
@@ -70,51 +96,70 @@ class Router(Object):
 
 
 class Network(Object):
 
 
 class Network(Object):
-    '''Class that represents a network in the logical model'''
+    """Class that represents a network in the logical model"""
     list = []
 
     def __init__(self, name, context, attrs):
     list = []
 
     def __init__(self, name, context, attrs):
-        super(self.__class__, self).__init__(name, context)
+        super(Network, self).__init__(name, context)
         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.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.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')
+        self.port_security_enabled = attrs.get('port_security_enabled', True)
+        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"
 
         if "external_network" in attrs:
             self.router = Router("router", self.name,
                                  context, attrs["external_network"])
 
         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)
 
     def has_route_to(self, network_name):
 
         Network.list.append(self)
 
     def has_route_to(self, network_name):
-        '''determines if this network has a route to the named network'''
+        """determines if this network has a route to the named network"""
         if self.router and self.router.external_gateway_info == network_name:
             return True
         return False
 
     @staticmethod
     def find_by_route_to(external_network):
         if self.router and self.router.external_gateway_info == network_name:
             return True
         return False
 
     @staticmethod
     def find_by_route_to(external_network):
-        '''finds a network that has a route to the specified network'''
+        """finds a network that has a route to the specified network"""
         for network in Network.list:
             if network.has_route_to(external_network):
                 return network
 
     @staticmethod
     def find_external_network():
         for network in Network.list:
             if network.has_route_to(external_network):
                 return network
 
     @staticmethod
     def find_external_network():
-        '''return the name of an external network some network in this
-        context has a route to'''
+        """return the name of an external network some network in this
+        context has a route to
+        """
         for network in Network.list:
             if network.router:
                 return network.router.external_gateway_info
         return None
 
 
         for network in Network.list:
             if network.router:
                 return network.router.external_gateway_info
         return None
 
 
-class Server(Object):
-    '''Class that represents a server in the logical model'''
+class Server(Object):     # pragma: no cover
+    """Class that represents a server in the logical model"""
     list = []
 
     def __init__(self, name, context, attrs):
     list = []
 
     def __init__(self, name, context, attrs):
-        super(self.__class__, self).__init__(name, context)
+        super(Server, self).__init__(name, context)
         self.stack_name = self.name + "." + context.name
         self.keypair_name = context.keypair_name
         self.secgroup_name = context.secgroup_name
         self.stack_name = self.name + "." + context.name
         self.keypair_name = context.keypair_name
         self.secgroup_name = context.secgroup_name
@@ -122,13 +167,15 @@ class Server(Object):
         self.context = context
         self.public_ip = None
         self.private_ip = None
         self.context = context
         self.public_ip = None
         self.private_ip = None
+        self.user_data = ''
+        self.interfaces = {}
 
         if attrs is None:
             attrs = {}
 
         self.placement_groups = []
         placement = attrs.get("placement", [])
 
         if attrs is None:
             attrs = {}
 
         self.placement_groups = []
         placement = attrs.get("placement", [])
-        placement = placement if type(placement) is list else [placement]
+        placement = placement if isinstance(placement, list) else [placement]
         for p in placement:
             pg = PlacementGroup.get(p)
             if not pg:
         for p in placement:
             pg = PlacementGroup.get(p)
             if not pg:
@@ -137,6 +184,17 @@ class Server(Object):
             self.placement_groups.append(pg)
             pg.add_member(self.stack_name)
 
             self.placement_groups.append(pg)
             pg.add_member(self.stack_name)
 
+        # support servergroup attr
+        self.server_group = None
+        sg = attrs.get("server_group")
+        if sg:
+            server_group = ServerGroup.get(sg)
+            if not server_group:
+                raise ValueError("server '%s', server_group '%s' is invalid" %
+                                 (name, sg))
+            self.server_group = server_group
+            server_group.add_member(self.stack_name)
+
         self.instances = 1
         if "instances" in attrs:
             self.instances = attrs["instances"]
         self.instances = 1
         if "instances" in attrs:
             self.instances = attrs["instances"]
@@ -163,11 +221,13 @@ class Server(Object):
         if "flavor" in attrs:
             self._flavor = attrs["flavor"]
 
         if "flavor" in attrs:
             self._flavor = attrs["flavor"]
 
+        self.user_data = attrs.get('user_data', '')
+
         Server.list.append(self)
 
     @property
     def image(self):
         Server.list.append(self)
 
     @property
     def image(self):
-        '''returns a server's image name'''
+        """returns a server's image name"""
         if self._image:
             return self._image
         else:
         if self._image:
             return self._image
         else:
@@ -175,21 +235,28 @@ class Server(Object):
 
     @property
     def flavor(self):
 
     @property
     def flavor(self):
-        '''returns a server's flavor name'''
+        """returns a server's flavor name"""
         if self._flavor:
             return self._flavor
         else:
             return self._context.flavor
 
     def _add_instance(self, template, server_name, networks, scheduler_hints):
         if self._flavor:
             return self._flavor
         else:
             return self._context.flavor
 
     def _add_instance(self, template, server_name, networks, scheduler_hints):
-        '''adds to the template one server and corresponding resources'''
+        """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}
         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)
+            # we can't use secgroups if port_security_enabled is False
+            if network.port_security_enabled:
+                sec_group_id = self.secgroup_name
+            else:
+                sec_group_id = None
+            # don't refactor to pass in network object, that causes JSON
+            # circular ref encode errors
+            template.add_port(port_name, network.stack_name, network.subnet_stack_name,
+                              sec_group_id=sec_group_id, provider=network.provider,
+                              allowed_address_pairs=network.allowed_address_pairs)
             port_name_list.append(port_name)
 
             if self.floating_ip:
             port_name_list.append(port_name)
 
             if self.floating_ip:
@@ -200,22 +267,32 @@ class Server(Object):
                                              external_network,
                                              port_name,
                                              network.router.stack_if_name,
                                              external_network,
                                              port_name,
                                              network.router.stack_if_name,
-                                             self.secgroup_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)
                     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)
-
-        template.add_server(server_name, self.image, self.flavor,
+        if self.flavor:
+            if isinstance(self.flavor, dict):
+                self.flavor["name"] = \
+                    self.flavor.setdefault("name", self.stack_name + "-flavor")
+                template.add_flavor(**self.flavor)
+                self.flavor_name = self.flavor["name"]
+            else:
+                self.flavor_name = self.flavor
+
+        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,
                             ports=port_name_list,
                             user=self.user,
                             key_name=self.keypair_name,
+                            user_data=self.user_data,
                             scheduler_hints=scheduler_hints)
 
     def add_to_template(self, template, networks, scheduler_hints=None):
                             scheduler_hints=scheduler_hints)
 
     def add_to_template(self, template, networks, scheduler_hints=None):
-        '''adds to the template one or more servers (instances)'''
+        """adds to the template one or more servers (instances)"""
         if self.instances == 1:
             server_name = self.stack_name
             self._add_instance(template, server_name, networks,
         if self.instances == 1:
             server_name = self.stack_name
             self._add_instance(template, server_name, networks,
@@ -229,9 +306,9 @@ class Server(Object):
 
 
 def update_scheduler_hints(scheduler_hints, added_servers, placement_group):
 
 
 def update_scheduler_hints(scheduler_hints, added_servers, placement_group):
-    ''' update scheduler hints from server's placement configuration
+    """update scheduler hints from server's placement configuration
     TODO: this code is openstack specific and should move somewhere else
     TODO: this code is openstack specific and should move somewhere else
-    '''
+    """
     if placement_group.policy == "affinity":
         if "same_host" in scheduler_hints:
             host_list = scheduler_hints["same_host"]
     if placement_group.policy == "affinity":
         if "same_host" in scheduler_hints:
             host_list = scheduler_hints["same_host"]