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')
109 if "external_network" in attrs:
110 self.router = Router("router", self.name,
111 context, attrs["external_network"])
113 Network.list.append(self)
115 def has_route_to(self, network_name):
116 """determines if this network has a route to the named network"""
117 if self.router and self.router.external_gateway_info == network_name:
122 def find_by_route_to(external_network):
123 """finds a network that has a route to the specified network"""
124 for network in Network.list:
125 if network.has_route_to(external_network):
129 def find_external_network():
130 """return the name of an external network some network in this
131 context has a route to"""
132 for network in Network.list:
134 return network.router.external_gateway_info
138 class Server(Object): # pragma: no cover
139 """Class that represents a server in the logical model"""
142 def __init__(self, name, context, attrs):
143 super(Server, self).__init__(name, context)
144 self.stack_name = self.name + "." + context.name
145 self.keypair_name = context.keypair_name
146 self.secgroup_name = context.secgroup_name
147 self.user = context.user
148 self.context = context
149 self.public_ip = None
150 self.private_ip = None
156 self.placement_groups = []
157 placement = attrs.get("placement", [])
158 placement = placement if type(placement) is list else [placement]
160 pg = PlacementGroup.get(p)
162 raise ValueError("server '%s', placement '%s' is invalid" %
164 self.placement_groups.append(pg)
165 pg.add_member(self.stack_name)
167 # support servergroup attr
168 self.server_group = None
169 sg = attrs.get("server_group")
171 server_group = ServerGroup.get(sg)
173 raise ValueError("server '%s', server_group '%s' is invalid" %
175 self.server_group = server_group
176 server_group.add_member(self.stack_name)
179 if "instances" in attrs:
180 self.instances = attrs["instances"]
182 # dict with key network name, each item is a dict with port name and ip
185 self.floating_ip = None
186 self.floating_ip_assoc = None
187 if "floating_ip" in attrs:
188 self.floating_ip = {}
189 self.floating_ip_assoc = {}
191 if self.floating_ip is not None:
192 ext_net = Network.find_external_network()
193 assert ext_net is not None
194 self.floating_ip["external_network"] = ext_net
198 self._image = attrs["image"]
201 if "flavor" in attrs:
202 self._flavor = attrs["flavor"]
204 self.user_data = attrs.get('user_data', '')
206 Server.list.append(self)
210 """returns a server's image name"""
214 return self._context.image
218 """returns a server's flavor name"""
222 return self._context.flavor
224 def _add_instance(self, template, server_name, networks, scheduler_hints):
225 """adds to the template one server and corresponding resources"""
227 for network in networks:
228 port_name = server_name + "-" + network.name + "-port"
229 self.ports[network.name] = {"stack_name": port_name}
230 template.add_port(port_name, network.stack_name,
231 network.subnet_stack_name,
232 sec_group_id=self.secgroup_name)
233 port_name_list.append(port_name)
236 external_network = self.floating_ip["external_network"]
237 if network.has_route_to(external_network):
238 self.floating_ip["stack_name"] = server_name + "-fip"
239 template.add_floating_ip(self.floating_ip["stack_name"],
242 network.router.stack_if_name,
244 self.floating_ip_assoc["stack_name"] = \
245 server_name + "-fip-assoc"
246 template.add_floating_ip_association(
247 self.floating_ip_assoc["stack_name"],
248 self.floating_ip["stack_name"],
251 template.add_server(server_name, self.image, self.flavor,
252 ports=port_name_list,
254 key_name=self.keypair_name,
255 user_data=self.user_data,
256 scheduler_hints=scheduler_hints)
258 def add_to_template(self, template, networks, scheduler_hints=None):
259 """adds to the template one or more servers (instances)"""
260 if self.instances == 1:
261 server_name = self.stack_name
262 self._add_instance(template, server_name, networks,
263 scheduler_hints=scheduler_hints)
265 # TODO(hafe) fix or remove, no test/sample for this
266 for i in range(self.instances):
267 server_name = "%s-%d" % (self.stack_name, i)
268 self._add_instance(template, server_name, networks,
269 scheduler_hints=scheduler_hints)
272 def update_scheduler_hints(scheduler_hints, added_servers, placement_group):
273 """ update scheduler hints from server's placement configuration
274 TODO: this code is openstack specific and should move somewhere else
276 if placement_group.policy == "affinity":
277 if "same_host" in scheduler_hints:
278 host_list = scheduler_hints["same_host"]
280 host_list = scheduler_hints["same_host"] = []
282 if "different_host" in scheduler_hints:
283 host_list = scheduler_hints["different_host"]
285 host_list = scheduler_hints["different_host"] = []
287 for name in added_servers:
288 if name in placement_group.members:
289 host_list.append({'get_resource': name})