HeatContext model update to match heat code update
[yardstick.git] / yardstick / benchmark / contexts / model.py
1 ##############################################################################
2 # Copyright (c) 2015 Ericsson AB and others.
3 #
4 # All rights reserved. This program and the accompanying materials
5 # are made available under the terms of the Apache License, Version 2.0
6 # which accompanies this distribution, and is available at
7 # http://www.apache.org/licenses/LICENSE-2.0
8 ##############################################################################
9
10 """ Logical model
11
12 """
13
14
15 class Object(object):
16     '''Base class for classes in the logical model
17     Contains common attributes and methods
18     '''
19     def __init__(self, name, context):
20         # model identities and reference
21         self.name = name
22         self._context = context
23
24         # stack identities
25         self.stack_name = None
26         self.stack_id = None
27
28     @property
29     def dn(self):
30         '''returns distinguished name for object'''
31         return self.name + "." + self._context.name
32
33
34 class PlacementGroup(Object):
35     '''Class that represents a placement group in the logical model
36     Concept comes from the OVF specification. Policy should be one of
37     "availability" or "affinity (there are more but they are not supported)"
38     '''
39     map = {}
40
41     def __init__(self, name, context, policy):
42         if policy not in ["affinity", "availability"]:
43             raise ValueError("placement group '%s', policy '%s' is not valid" %
44                              (name, policy))
45         self.name = name
46         self.members = set()
47         self.stack_name = context.name + "-" + name
48         self.policy = policy
49         PlacementGroup.map[name] = self
50
51     def add_member(self, name):
52         self.members.add(name)
53
54     @staticmethod
55     def get(name):
56         if name in PlacementGroup.map:
57             return PlacementGroup.map[name]
58         else:
59             return None
60
61
62 class Router(Object):
63     '''Class that represents a router in the logical model'''
64     def __init__(self, name, network_name, context, external_gateway_info):
65         super(self.__class__, self).__init__(name, context)
66
67         self.stack_name = context.name + "-" + network_name + "-" + self.name
68         self.stack_if_name = self.stack_name + "-if0"
69         self.external_gateway_info = external_gateway_info
70
71
72 class Network(Object):
73     '''Class that represents a network in the logical model'''
74     list = []
75
76     def __init__(self, name, context, attrs):
77         super(self.__class__, self).__init__(name, context)
78         self.stack_name = context.name + "-" + self.name
79         self.subnet_stack_name = self.stack_name + "-subnet"
80         self.subnet_cidr = attrs.get('cidr', '10.0.1.0/24')
81         self.router = None
82
83         if "external_network" in attrs:
84             self.router = Router("router", self.name,
85                                  context, attrs["external_network"])
86
87         Network.list.append(self)
88
89     def has_route_to(self, network_name):
90         '''determines if this network has a route to the named network'''
91         if self.router and self.router.external_gateway_info == network_name:
92             return True
93         return False
94
95     @staticmethod
96     def find_by_route_to(external_network):
97         '''finds a network that has a route to the specified network'''
98         for network in Network.list:
99             if network.has_route_to(external_network):
100                 return network
101
102     @staticmethod
103     def find_external_network():
104         '''return the name of an external network some network in this
105         context has a route to'''
106         for network in Network.list:
107             if network.router:
108                 return network.router.external_gateway_info
109         return None
110
111
112 class Server(Object):
113     '''Class that represents a server in the logical model'''
114     list = []
115
116     def __init__(self, name, context, attrs):
117         super(self.__class__, self).__init__(name, context)
118         self.stack_name = self.name + "." + context.name
119         self.keypair_name = context.keypair_name
120         self.secgroup_name = context.secgroup_name
121         self.user = context.user
122         self.context = context
123         self.public_ip = None
124         self.private_ip = None
125
126         if attrs is None:
127             attrs = {}
128
129         self.placement_groups = []
130         placement = attrs.get("placement", [])
131         placement = placement if type(placement) is list else [placement]
132         for p in placement:
133             pg = PlacementGroup.get(p)
134             if not pg:
135                 raise ValueError("server '%s', placement '%s' is invalid" %
136                                  (name, p))
137             self.placement_groups.append(pg)
138             pg.add_member(self.stack_name)
139
140         self.instances = 1
141         if "instances" in attrs:
142             self.instances = attrs["instances"]
143
144         # dict with key network name, each item is a dict with port name and ip
145         self.ports = {}
146
147         self.floating_ip = None
148         if "floating_ip" in attrs:
149             self.floating_ip = {}
150
151         if self.floating_ip is not None:
152             ext_net = Network.find_external_network()
153             assert ext_net is not None
154             self.floating_ip["external_network"] = ext_net
155
156         self._image = None
157         if "image" in attrs:
158             self._image = attrs["image"]
159
160         self._flavor = None
161         if "flavor" in attrs:
162             self._flavor = attrs["flavor"]
163
164         Server.list.append(self)
165
166     @property
167     def image(self):
168         '''returns a server's image name'''
169         if self._image:
170             return self._image
171         else:
172             return self._context.image
173
174     @property
175     def flavor(self):
176         '''returns a server's flavor name'''
177         if self._flavor:
178             return self._flavor
179         else:
180             return self._context.flavor
181
182     def _add_instance(self, template, server_name, networks, scheduler_hints):
183         '''adds to the template one server and corresponding resources'''
184         port_name_list = []
185         for network in networks:
186             port_name = server_name + "-" + network.name + "-port"
187             self.ports[network.name] = {"stack_name": port_name}
188             template.add_port(port_name, network.stack_name,
189                               network.subnet_stack_name,
190                               sec_group_id=self.secgroup_name)
191             port_name_list.append(port_name)
192
193             if self.floating_ip:
194                 external_network = self.floating_ip["external_network"]
195                 if network.has_route_to(external_network):
196                     self.floating_ip["stack_name"] = server_name + "-fip"
197                     template.add_floating_ip(self.floating_ip["stack_name"],
198                                              external_network,
199                                              port_name,
200                                              network.router.stack_if_name,
201                                              self.secgroup_name)
202
203         template.add_server(server_name, self.image, self.flavor,
204                             ports=port_name_list,
205                             user=self.user,
206                             key_name=self.keypair_name,
207                             scheduler_hints=scheduler_hints)
208
209     def add_to_template(self, template, networks, scheduler_hints=None):
210         '''adds to the template one or more servers (instances)'''
211         if self.instances == 1:
212             server_name = self.stack_name
213             self._add_instance(template, server_name, networks,
214                                scheduler_hints=scheduler_hints)
215         else:
216             # TODO(hafe) fix or remove, no test/sample for this
217             for i in range(self.instances):
218                 server_name = "%s-%d" % (self.stack_name, i)
219                 self._add_instance(template, server_name, networks,
220                                    scheduler_hints=scheduler_hints)
221
222
223 def update_scheduler_hints(scheduler_hints, added_servers, placement_group):
224     ''' update scheduler hints from server's placement configuration
225     TODO: this code is openstack specific and should move somewhere else
226     '''
227     if placement_group.policy == "affinity":
228         if "same_host" in scheduler_hints:
229             host_list = scheduler_hints["same_host"]
230         else:
231             host_list = scheduler_hints["same_host"] = []
232     else:
233         if "different_host" in scheduler_hints:
234             host_list = scheduler_hints["different_host"]
235         else:
236             host_list = scheduler_hints["different_host"] = []
237
238     for name in added_servers:
239         if name in placement_group.members:
240             host_list.append({'get_resource': name})