1 ##############################################################################
2 # Copyright (c) 2015 Ericsson AB and others.
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 ##############################################################################
13 from __future__ import absolute_import
14 from six.moves import range
18 """Base class for classes in the logical model
19 Contains common attributes and methods
22 def __init__(self, name, context):
23 # model identities and reference
25 self._context = context
28 self.stack_name = None
33 """returns distinguished name for object"""
34 return self.name + "." + self._context.name
37 class PlacementGroup(Object):
38 """Class that represents a placement group in the logical model
39 Concept comes from the OVF specification. Policy should be one of
40 "availability" or "affinity (there are more but they are not supported)"
44 def __init__(self, name, context, policy):
45 if policy not in ["affinity", "availability"]:
46 raise ValueError("placement group '%s', policy '%s' is not valid" %
50 self.stack_name = context.name + "-" + name
52 PlacementGroup.map[name] = self
54 def add_member(self, name):
55 self.members.add(name)
59 return PlacementGroup.map.get(name)
62 class ServerGroup(Object): # pragma: no cover
63 """Class that represents a server group in the logical model
64 Policy should be one of "anti-affinity" or "affinity"
68 def __init__(self, name, context, policy):
69 super(ServerGroup, self).__init__(name, context)
70 if policy not in {"affinity", "anti-affinity"}:
71 raise ValueError("server group '%s', policy '%s' is not valid" %
75 self.stack_name = context.name + "-" + name
77 ServerGroup.map[name] = self
79 def add_member(self, name):
80 self.members.add(name)
84 return ServerGroup.map.get(name)
88 """Class that represents a router in the logical model"""
90 def __init__(self, name, network_name, context, external_gateway_info):
91 super(Router, self).__init__(name, context)
93 self.stack_name = context.name + "-" + network_name + "-" + self.name
94 self.stack_if_name = self.stack_name + "-if0"
95 self.external_gateway_info = external_gateway_info
98 class Network(Object):
99 """Class that represents a network in the logical model"""
102 def __init__(self, name, context, attrs):
103 super(Network, self).__init__(name, context)
104 self.stack_name = context.name + "-" + self.name
105 self.subnet_stack_name = self.stack_name + "-subnet"
106 self.subnet_cidr = attrs.get('cidr', '10.0.1.0/24')
108 self.physical_network = attrs.get('physical_network', 'physnet1')
109 self.provider = attrs.get('provider', None)
111 if "external_network" in attrs:
112 self.router = Router("router", self.name,
113 context, attrs["external_network"])
115 Network.list.append(self)
117 def has_route_to(self, network_name):
118 """determines if this network has a route to the named network"""
119 if self.router and self.router.external_gateway_info == network_name:
124 def find_by_route_to(external_network):
125 """finds a network that has a route to the specified network"""
126 for network in Network.list:
127 if network.has_route_to(external_network):
131 def find_external_network():
132 """return the name of an external network some network in this
133 context has a route to"""
134 for network in Network.list:
136 return network.router.external_gateway_info
140 class Server(Object): # pragma: no cover
141 """Class that represents a server in the logical model"""
144 def __init__(self, name, context, attrs):
145 super(Server, self).__init__(name, context)
146 self.stack_name = self.name + "." + context.name
147 self.keypair_name = context.keypair_name
148 self.secgroup_name = context.secgroup_name
149 self.user = context.user
150 self.context = context
151 self.public_ip = None
152 self.private_ip = None
158 self.placement_groups = []
159 placement = attrs.get("placement", [])
160 placement = placement if isinstance(placement, list) else [placement]
162 pg = PlacementGroup.get(p)
164 raise ValueError("server '%s', placement '%s' is invalid" %
166 self.placement_groups.append(pg)
167 pg.add_member(self.stack_name)
169 # support servergroup attr
170 self.server_group = None
171 sg = attrs.get("server_group")
173 server_group = ServerGroup.get(sg)
175 raise ValueError("server '%s', server_group '%s' is invalid" %
177 self.server_group = server_group
178 server_group.add_member(self.stack_name)
181 if "instances" in attrs:
182 self.instances = attrs["instances"]
184 # dict with key network name, each item is a dict with port name and ip
187 self.floating_ip = None
188 self.floating_ip_assoc = None
189 if "floating_ip" in attrs:
190 self.floating_ip = {}
191 self.floating_ip_assoc = {}
193 if self.floating_ip is not None:
194 ext_net = Network.find_external_network()
195 assert ext_net is not None
196 self.floating_ip["external_network"] = ext_net
200 self._image = attrs["image"]
203 if "flavor" in attrs:
204 self._flavor = attrs["flavor"]
206 self.user_data = attrs.get('user_data', '')
208 Server.list.append(self)
212 """returns a server's image name"""
216 return self._context.image
220 """returns a server's flavor name"""
224 return self._context.flavor
226 def _add_instance(self, template, server_name, networks, scheduler_hints):
227 """adds to the template one server and corresponding resources"""
229 for network in networks:
230 port_name = server_name + "-" + network.name + "-port"
231 self.ports[network.name] = {"stack_name": port_name}
232 template.add_port(port_name, network.stack_name,
233 network.subnet_stack_name,
234 sec_group_id=self.secgroup_name,
235 provider=network.provider)
236 port_name_list.append(port_name)
239 external_network = self.floating_ip["external_network"]
240 if network.has_route_to(external_network):
241 self.floating_ip["stack_name"] = server_name + "-fip"
242 template.add_floating_ip(self.floating_ip["stack_name"],
245 network.router.stack_if_name,
247 self.floating_ip_assoc["stack_name"] = \
248 server_name + "-fip-assoc"
249 template.add_floating_ip_association(
250 self.floating_ip_assoc["stack_name"],
251 self.floating_ip["stack_name"],
254 template.add_server(server_name, self.image, self.flavor,
255 ports=port_name_list,
257 key_name=self.keypair_name,
258 user_data=self.user_data,
259 scheduler_hints=scheduler_hints)
261 def add_to_template(self, template, networks, scheduler_hints=None):
262 """adds to the template one or more servers (instances)"""
263 if self.instances == 1:
264 server_name = self.stack_name
265 self._add_instance(template, server_name, networks,
266 scheduler_hints=scheduler_hints)
268 # TODO(hafe) fix or remove, no test/sample for this
269 for i in range(self.instances):
270 server_name = "%s-%d" % (self.stack_name, i)
271 self._add_instance(template, server_name, networks,
272 scheduler_hints=scheduler_hints)
275 def update_scheduler_hints(scheduler_hints, added_servers, placement_group):
276 """ update scheduler hints from server's placement configuration
277 TODO: this code is openstack specific and should move somewhere else
279 if placement_group.policy == "affinity":
280 if "same_host" in scheduler_hints:
281 host_list = scheduler_hints["same_host"]
283 host_list = scheduler_hints["same_host"] = []
285 if "different_host" in scheduler_hints:
286 host_list = scheduler_hints["different_host"]
288 host_list = scheduler_hints["different_host"] = []
290 for name in added_servers:
291 if name in placement_group.members:
292 host_list.append({'get_resource': name})