Merge "vsperf: Initial integration with VSPERF"
[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         self.floating_ip_assoc = None
149         if "floating_ip" in attrs:
150             self.floating_ip = {}
151             self.floating_ip_assoc = {}
152
153         if self.floating_ip is not None:
154             ext_net = Network.find_external_network()
155             assert ext_net is not None
156             self.floating_ip["external_network"] = ext_net
157
158         self._image = None
159         if "image" in attrs:
160             self._image = attrs["image"]
161
162         self._flavor = None
163         if "flavor" in attrs:
164             self._flavor = attrs["flavor"]
165
166         Server.list.append(self)
167
168     @property
169     def image(self):
170         '''returns a server's image name'''
171         if self._image:
172             return self._image
173         else:
174             return self._context.image
175
176     @property
177     def flavor(self):
178         '''returns a server's flavor name'''
179         if self._flavor:
180             return self._flavor
181         else:
182             return self._context.flavor
183
184     def _add_instance(self, template, server_name, networks, scheduler_hints):
185         '''adds to the template one server and corresponding resources'''
186         port_name_list = []
187         for network in networks:
188             port_name = server_name + "-" + network.name + "-port"
189             self.ports[network.name] = {"stack_name": port_name}
190             template.add_port(port_name, network.stack_name,
191                               network.subnet_stack_name,
192                               sec_group_id=self.secgroup_name)
193             port_name_list.append(port_name)
194
195             if self.floating_ip:
196                 external_network = self.floating_ip["external_network"]
197                 if network.has_route_to(external_network):
198                     self.floating_ip["stack_name"] = server_name + "-fip"
199                     template.add_floating_ip(self.floating_ip["stack_name"],
200                                              external_network,
201                                              port_name,
202                                              network.router.stack_if_name,
203                                              self.secgroup_name)
204                 self.floating_ip_assoc["stack_name"] = \
205                     server_name + "-fip-assoc"
206                 template.add_floating_ip_association(
207                     self.floating_ip_assoc["stack_name"],
208                     self.floating_ip["stack_name"],
209                     port_name)
210
211         template.add_server(server_name, self.image, self.flavor,
212                             ports=port_name_list,
213                             user=self.user,
214                             key_name=self.keypair_name,
215                             scheduler_hints=scheduler_hints)
216
217     def add_to_template(self, template, networks, scheduler_hints=None):
218         '''adds to the template one or more servers (instances)'''
219         if self.instances == 1:
220             server_name = self.stack_name
221             self._add_instance(template, server_name, networks,
222                                scheduler_hints=scheduler_hints)
223         else:
224             # TODO(hafe) fix or remove, no test/sample for this
225             for i in range(self.instances):
226                 server_name = "%s-%d" % (self.stack_name, i)
227                 self._add_instance(template, server_name, networks,
228                                    scheduler_hints=scheduler_hints)
229
230
231 def update_scheduler_hints(scheduler_hints, added_servers, placement_group):
232     ''' update scheduler hints from server's placement configuration
233     TODO: this code is openstack specific and should move somewhere else
234     '''
235     if placement_group.policy == "affinity":
236         if "same_host" in scheduler_hints:
237             host_list = scheduler_hints["same_host"]
238         else:
239             host_list = scheduler_hints["same_host"] = []
240     else:
241         if "different_host" in scheduler_hints:
242             host_list = scheduler_hints["different_host"]
243         else:
244             host_list = scheduler_hints["different_host"] = []
245
246     for name in added_servers:
247         if name in placement_group.members:
248             host_list.append({'get_resource': name})