Merge "Docker container for Yardstick CI"
[yardstick.git] / yardstick / benchmark / context / model.py
index ff56fc7..6e754d4 100644 (file)
@@ -12,7 +12,6 @@
 """
 
 import sys
-import os
 
 from yardstick.orchestrator.heat import HeatTemplate
 
@@ -82,7 +81,7 @@ class Network(Object):
         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["cidr"]
+        self.subnet_cidr = attrs.get('cidr', '10.0.1.0/24')
         self.router = None
 
         if "external_network" in attrs:
@@ -120,9 +119,12 @@ class Server(Object):
 
     def __init__(self, name, context, attrs):
         super(Server, self).__init__(name, context)
-        self.stack_name = context.name + "-" + self.name
+        self.stack_name = self.name + "." + context.name
         self.keypair_name = context.keypair_name
         self.secgroup_name = context.secgroup_name
+        self.context = context
+        self.public_ip = None
+        self.private_ip = None
 
         if attrs is None:
             attrs = {}
@@ -209,12 +211,13 @@ class Server(Object):
     def add_to_template(self, template, networks, scheduler_hints=None):
         '''adds to the template one or more servers (instances)'''
         if self.instances == 1:
-            server_name = "%s-%s" % (template.name, self.name)
+            server_name = self.stack_name
             self._add_instance(template, server_name, networks,
                                scheduler_hints=scheduler_hints)
         else:
+            # TODO(hafe) fix or remove, no test/sample for this
             for i in range(self.instances):
-                server_name = "%s-%s-%d" % (template.name, self.name, i)
+                server_name = "%s-%d" % (self.stack_name, i)
                 self._add_instance(template, server_name, networks,
                                    scheduler_hints=scheduler_hints)
 
@@ -255,11 +258,22 @@ class Context(object):
         self._image = None
         self._flavor = None
         self._user = None
+        self.template_file = None
+        self.heat_parameters = None
         Context.list.append(self)
 
     def init(self, attrs):
         '''initializes itself from the supplied arguments'''
         self.name = attrs["name"]
+
+        if "user" in attrs:
+            self._user = attrs["user"]
+
+        if "heat_template" in attrs:
+            self.template_file = attrs["heat_template"]
+            self.heat_parameters = attrs.get("heat_parameters", None)
+            return
+
         self.keypair_name = self.name + "-key"
         self.secgroup_name = self.name + "-secgroup"
 
@@ -269,9 +283,6 @@ class Context(object):
         if "flavor" in attrs:
             self._flavor = attrs["flavor"]
 
-        if "user" in attrs:
-            self._user = attrs["user"]
-
         if "placement_groups" in attrs:
             for name, pgattrs in attrs["placement_groups"].items():
                 pg = PlacementGroup(name, self, pgattrs["policy"])
@@ -323,6 +334,11 @@ class Context(object):
         list_of_servers = sorted(self.servers,
                                  key=lambda s: len(s.placement_groups))
 
+        #
+        # add servers with scheduler hints derived from placement groups
+        #
+
+        # create list of servers with availability policy
         availability_servers = []
         for server in list_of_servers:
             for pg in server.placement_groups:
@@ -330,7 +346,7 @@ class Context(object):
                     availability_servers.append(server)
                     break
 
-        # add servers with scheduler hints derived from placement groups
+        # add servers with availability policy
         added_servers = []
         for server in availability_servers:
             scheduler_hints = {}
@@ -339,6 +355,7 @@ class Context(object):
             server.add_to_template(template, self.networks, scheduler_hints)
             added_servers.append(server.stack_name)
 
+        # create list of servers with affinity policy
         affinity_servers = []
         for server in list_of_servers:
             for pg in server.placement_groups:
@@ -346,6 +363,7 @@ class Context(object):
                     affinity_servers.append(server)
                     break
 
+        # add servers with affinity policy
         for server in affinity_servers:
             if server.stack_name in added_servers:
                 continue
@@ -355,16 +373,23 @@ class Context(object):
             server.add_to_template(template, self.networks, scheduler_hints)
             added_servers.append(server.stack_name)
 
+        # add remaining servers with no placement group configured
+        for server in list_of_servers:
+            if len(server.placement_groups) == 0:
+                server.add_to_template(template, self.networks, {})
+
     def deploy(self):
         '''deploys template into a stack using cloud'''
-        print "Deploying context as stack '%s' using auth_url %s" % (
-            self.name, os.environ.get('OS_AUTH_URL'))
+        print "Deploying context '%s'" % self.name
+
+        heat_template = HeatTemplate(self.name, self.template_file,
+                                     self.heat_parameters)
 
-        template = HeatTemplate(self.name)
-        self._add_resources_to_template(template)
+        if self.template_file is None:
+            self._add_resources_to_template(heat_template)
 
         try:
-            self.stack = template.create()
+            self.stack = heat_template.create()
         except KeyboardInterrupt:
             sys.exit("\nStack create interrupted")
         except RuntimeError as err:
@@ -372,25 +397,73 @@ class Context(object):
         except Exception as err:
             sys.exit("error: failed to deploy stack: '%s'" % err)
 
-        # copy some vital stack output into context
-        for server in Server.list:
-            for port in server.ports.itervalues():
-                port["ipaddr"] = self.stack.outputs[port["stack_name"]]
+        # copy some vital stack output into server objects
+        for server in self.servers:
+            if len(server.ports) > 0:
+                # TODO(hafe) can only handle one internal network for now
+                port = server.ports.values()[0]
+                server.private_ip = self.stack.outputs[port["stack_name"]]
 
             if server.floating_ip:
-                server.floating_ip["ipaddr"] = \
+                server.public_ip = \
                     self.stack.outputs[server.floating_ip["stack_name"]]
 
-        print "Context deployed"
+        print "Context '%s' deployed" % self.name
 
     def undeploy(self):
         '''undeploys stack from cloud'''
         if self.stack:
-            print "Undeploying context (stack) '%s'" % self.name
+            print "Undeploying context '%s'" % self.name
             self.stack.delete()
             self.stack = None
-            print "Context undeployed"
+            print "Context '%s' undeployed" % self.name
+
+    @staticmethod
+    def get_server_by_name(dn):
+        '''lookup server object by DN
 
-    def get_server(self, name):
-        '''lookup server object by name from context'''
-        return self._server_map[name]
+        dn is a distinguished name including the context name'''
+        if "." not in dn:
+            raise ValueError("dn '%s' is malformed" % dn)
+
+        for context in Context.list:
+            if dn in context._server_map:
+                return context._server_map[dn]
+
+        return None
+
+    @staticmethod
+    def get_context_by_name(name):
+        for context in Context.list:
+            if name == context.name:
+                return context
+        return None
+
+    @staticmethod
+    def get_server(attr_name):
+        '''lookup server object by name from context
+        attr_name: either a name for a server created by yardstick or a dict
+        with attribute name mapping when using external heat templates
+        '''
+        if type(attr_name) is dict:
+            cname = attr_name["name"].split(".")[1]
+            context = Context.get_context_by_name(cname)
+            if context is None:
+                raise ValueError("context not found for server '%s'" %
+                                 attr_name["name"])
+
+            public_ip = None
+            private_ip = None
+            if "public_ip_attr" in attr_name:
+                public_ip = context.stack.outputs[attr_name["public_ip_attr"]]
+            if "private_ip_attr" in attr_name:
+                private_ip = context.stack.outputs[
+                    attr_name["private_ip_attr"]]
+
+            # Create a dummy server instance for holding the *_ip attributes
+            server = Server(attr_name["name"].split(".")[0], context, {})
+            server.public_ip = public_ip
+            server.private_ip = private_ip
+            return server
+        else:
+            return Context.get_server_by_name(attr_name)