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)
110 self.segmentation_id = attrs.get('segmentation_id', None)
112 if "external_network" in attrs:
113 self.router = Router("router", self.name,
114 context, attrs["external_network"])
115 self.vld_id = attrs.get("vld_id", "")
117 Network.list.append(self)
119 def has_route_to(self, network_name):
120 """determines if this network has a route to the named network"""
121 if self.router and self.router.external_gateway_info == network_name:
126 def find_by_route_to(external_network):
127 """finds a network that has a route to the specified network"""
128 for network in Network.list:
129 if network.has_route_to(external_network):
133 def find_external_network():
134 """return the name of an external network some network in this
135 context has a route to
137 for network in Network.list:
139 return network.router.external_gateway_info
143 class Server(Object): # pragma: no cover
144 """Class that represents a server in the logical model"""
147 def __init__(self, name, context, attrs):
148 super(Server, self).__init__(name, context)
149 self.stack_name = self.name + "." + context.name
150 self.keypair_name = context.keypair_name
151 self.secgroup_name = context.secgroup_name
152 self.user = context.user
153 self.context = context
154 self.public_ip = None
155 self.private_ip = None
162 self.placement_groups = []
163 placement = attrs.get("placement", [])
164 placement = placement if isinstance(placement, list) else [placement]
166 pg = PlacementGroup.get(p)
168 raise ValueError("server '%s', placement '%s' is invalid" %
170 self.placement_groups.append(pg)
171 pg.add_member(self.stack_name)
173 # support servergroup attr
174 self.server_group = None
175 sg = attrs.get("server_group")
177 server_group = ServerGroup.get(sg)
179 raise ValueError("server '%s', server_group '%s' is invalid" %
181 self.server_group = server_group
182 server_group.add_member(self.stack_name)
185 if "instances" in attrs:
186 self.instances = attrs["instances"]
188 # dict with key network name, each item is a dict with port name and ip
191 self.floating_ip = None
192 self.floating_ip_assoc = None
193 if "floating_ip" in attrs:
194 self.floating_ip = {}
195 self.floating_ip_assoc = {}
197 if self.floating_ip is not None:
198 ext_net = Network.find_external_network()
199 assert ext_net is not None
200 self.floating_ip["external_network"] = ext_net
204 self._image = attrs["image"]
207 if "flavor" in attrs:
208 self._flavor = attrs["flavor"]
210 self.user_data = attrs.get('user_data', '')
212 Server.list.append(self)
216 """returns a server's image name"""
220 return self._context.image
224 """returns a server's flavor name"""
228 return self._context.flavor
230 def _add_instance(self, template, server_name, networks, scheduler_hints):
231 """adds to the template one server and corresponding resources"""
233 for network in networks:
234 port_name = server_name + "-" + network.name + "-port"
235 self.ports[network.name] = {"stack_name": port_name}
236 template.add_port(port_name, network.stack_name,
237 network.subnet_stack_name,
238 sec_group_id=self.secgroup_name,
239 provider=network.provider)
240 port_name_list.append(port_name)
243 external_network = self.floating_ip["external_network"]
244 if network.has_route_to(external_network):
245 self.floating_ip["stack_name"] = server_name + "-fip"
246 template.add_floating_ip(self.floating_ip["stack_name"],
249 network.router.stack_if_name,
251 self.floating_ip_assoc["stack_name"] = \
252 server_name + "-fip-assoc"
253 template.add_floating_ip_association(
254 self.floating_ip_assoc["stack_name"],
255 self.floating_ip["stack_name"],
258 if isinstance(self.flavor, dict):
259 self.flavor["name"] = \
260 self.flavor.setdefault("name", self.stack_name + "-flavor")
261 template.add_flavor(**self.flavor)
262 self.flavor_name = self.flavor["name"]
264 self.flavor_name = self.flavor
266 template.add_server(server_name, self.image, flavor=self.flavor_name,
267 flavors=self.context.flavors,
268 ports=port_name_list,
270 key_name=self.keypair_name,
271 user_data=self.user_data,
272 scheduler_hints=scheduler_hints)
274 def add_to_template(self, template, networks, scheduler_hints=None):
275 """adds to the template one or more servers (instances)"""
276 if self.instances == 1:
277 server_name = self.stack_name
278 self._add_instance(template, server_name, networks,
279 scheduler_hints=scheduler_hints)
281 # TODO(hafe) fix or remove, no test/sample for this
282 for i in range(self.instances):
283 server_name = "%s-%d" % (self.stack_name, i)
284 self._add_instance(template, server_name, networks,
285 scheduler_hints=scheduler_hints)
288 def update_scheduler_hints(scheduler_hints, added_servers, placement_group):
289 """update scheduler hints from server's placement configuration
290 TODO: this code is openstack specific and should move somewhere else
292 if placement_group.policy == "affinity":
293 if "same_host" in scheduler_hints:
294 host_list = scheduler_hints["same_host"]
296 host_list = scheduler_hints["same_host"] = []
298 if "different_host" in scheduler_hints:
299 host_list = scheduler_hints["different_host"]
301 host_list = scheduler_hints["different_host"] = []
303 for name in added_servers:
304 if name in placement_group.members:
305 host_list.append({'get_resource': name})