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')
110 self.segmentation_id = attrs.get('segmentation_id')
111 self.network_type = attrs.get('network_type')
113 if "external_network" in attrs:
114 self.router = Router("router", self.name,
115 context, attrs["external_network"])
116 self.vld_id = attrs.get("vld_id")
118 Network.list.append(self)
120 def has_route_to(self, network_name):
121 """determines if this network has a route to the named network"""
122 if self.router and self.router.external_gateway_info == network_name:
127 def find_by_route_to(external_network):
128 """finds a network that has a route to the specified network"""
129 for network in Network.list:
130 if network.has_route_to(external_network):
134 def find_external_network():
135 """return the name of an external network some network in this
136 context has a route to
138 for network in Network.list:
140 return network.router.external_gateway_info
144 class Server(Object): # pragma: no cover
145 """Class that represents a server in the logical model"""
148 def __init__(self, name, context, attrs):
149 super(Server, self).__init__(name, context)
150 self.stack_name = self.name + "." + context.name
151 self.keypair_name = context.keypair_name
152 self.secgroup_name = context.secgroup_name
153 self.user = context.user
154 self.context = context
155 self.public_ip = None
156 self.private_ip = None
163 self.placement_groups = []
164 placement = attrs.get("placement", [])
165 placement = placement if isinstance(placement, list) else [placement]
167 pg = PlacementGroup.get(p)
169 raise ValueError("server '%s', placement '%s' is invalid" %
171 self.placement_groups.append(pg)
172 pg.add_member(self.stack_name)
174 # support servergroup attr
175 self.server_group = None
176 sg = attrs.get("server_group")
178 server_group = ServerGroup.get(sg)
180 raise ValueError("server '%s', server_group '%s' is invalid" %
182 self.server_group = server_group
183 server_group.add_member(self.stack_name)
186 if "instances" in attrs:
187 self.instances = attrs["instances"]
189 # dict with key network name, each item is a dict with port name and ip
192 self.floating_ip = None
193 self.floating_ip_assoc = None
194 if "floating_ip" in attrs:
195 self.floating_ip = {}
196 self.floating_ip_assoc = {}
198 if self.floating_ip is not None:
199 ext_net = Network.find_external_network()
200 assert ext_net is not None
201 self.floating_ip["external_network"] = ext_net
205 self._image = attrs["image"]
208 if "flavor" in attrs:
209 self._flavor = attrs["flavor"]
211 self.user_data = attrs.get('user_data', '')
213 Server.list.append(self)
217 """returns a server's image name"""
221 return self._context.image
225 """returns a server's flavor name"""
229 return self._context.flavor
231 def _add_instance(self, template, server_name, networks, scheduler_hints):
232 """adds to the template one server and corresponding resources"""
234 for network in networks:
235 port_name = server_name + "-" + network.name + "-port"
236 self.ports[network.name] = {"stack_name": port_name}
237 template.add_port(port_name, network.stack_name,
238 network.subnet_stack_name,
239 sec_group_id=self.secgroup_name,
240 provider=network.provider)
241 port_name_list.append(port_name)
244 external_network = self.floating_ip["external_network"]
245 if network.has_route_to(external_network):
246 self.floating_ip["stack_name"] = server_name + "-fip"
247 template.add_floating_ip(self.floating_ip["stack_name"],
250 network.router.stack_if_name,
252 self.floating_ip_assoc["stack_name"] = \
253 server_name + "-fip-assoc"
254 template.add_floating_ip_association(
255 self.floating_ip_assoc["stack_name"],
256 self.floating_ip["stack_name"],
259 if isinstance(self.flavor, dict):
260 self.flavor["name"] = \
261 self.flavor.setdefault("name", self.stack_name + "-flavor")
262 template.add_flavor(**self.flavor)
263 self.flavor_name = self.flavor["name"]
265 self.flavor_name = self.flavor
267 template.add_server(server_name, self.image, flavor=self.flavor_name,
268 flavors=self.context.flavors,
269 ports=port_name_list,
271 key_name=self.keypair_name,
272 user_data=self.user_data,
273 scheduler_hints=scheduler_hints)
275 def add_to_template(self, template, networks, scheduler_hints=None):
276 """adds to the template one or more servers (instances)"""
277 if self.instances == 1:
278 server_name = self.stack_name
279 self._add_instance(template, server_name, networks,
280 scheduler_hints=scheduler_hints)
282 # TODO(hafe) fix or remove, no test/sample for this
283 for i in range(self.instances):
284 server_name = "%s-%d" % (self.stack_name, i)
285 self._add_instance(template, server_name, networks,
286 scheduler_hints=scheduler_hints)
289 def update_scheduler_hints(scheduler_hints, added_servers, placement_group):
290 """update scheduler hints from server's placement configuration
291 TODO: this code is openstack specific and should move somewhere else
293 if placement_group.policy == "affinity":
294 if "same_host" in scheduler_hints:
295 host_list = scheduler_hints["same_host"]
297 host_list = scheduler_hints["same_host"] = []
299 if "different_host" in scheduler_hints:
300 host_list = scheduler_hints["different_host"]
302 host_list = scheduler_hints["different_host"] = []
304 for name in added_servers:
305 if name in placement_group.members:
306 host_list.append({'get_resource': name})