blob: fd53c3413e166e03ad6f83b1f7277edc5c8247dd [file] [log] [blame]
Matteo Scandolod2044a42017-08-07 16:08:28 -07001
2# Copyright 2017-present Open Networking Foundation
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16
Sapan Bhatia8918ac32017-07-09 00:43:27 -040017from core.models import Slice, Privilege, SliceRole, Instance, Site, Node, User
Scott Bakere791dc62014-08-28 14:02:54 -070018from plus import PlusObjectMixin
Scott Bakera76f65d2015-01-13 16:22:57 -080019from operator import itemgetter, attrgetter
Scott Baker3e035222015-01-24 00:01:29 -080020from rest_framework.exceptions import APIException
Scott Baker5f4770d2014-07-13 11:17:58 -070021
Scott Bakere791dc62014-08-28 14:02:54 -070022class SlicePlus(Slice, PlusObjectMixin):
Scott Baker88e34372014-07-13 11:46:36 -070023 class Meta:
24 proxy = True
25
Scott Bakera76f65d2015-01-13 16:22:57 -080026 def __init__(self, *args, **kwargs):
27 super(SlicePlus, self).__init__(*args, **kwargs)
Scott Bakera76f65d2015-01-13 16:22:57 -080028 self._update_users = None
Scott Baker7a76f322015-01-16 19:07:36 -080029 self._sliceInfo = None
Scott Baker55f6de62015-01-18 16:07:58 -080030 self.getSliceInfo()
31 self._site_allocation = self._sliceInfo["sitesUsed"]
32 self._initial_site_allocation = self._site_allocation
Scott Baker04ab7c82015-01-20 13:30:40 -080033 self._network_ports = self._sliceInfo["networkPorts"]
34 self._initial_network_ports = self._network_ports
Scott Bakera76f65d2015-01-13 16:22:57 -080035
Scott Baker5f4770d2014-07-13 11:17:58 -070036 def getSliceInfo(self, user=None):
Scott Baker7a76f322015-01-16 19:07:36 -080037 if not self._sliceInfo:
38 used_sites = {}
Scott Baker21909342015-01-22 15:21:24 -080039 ready_sites = {}
Scott Baker7a76f322015-01-16 19:07:36 -080040 used_deployments = {}
Tony Mack3de59e32015-08-19 11:58:18 -040041 instanceCount = 0
Scott Bakerec930102015-01-20 01:02:08 -080042 sshCommands = []
Tony Mack3de59e32015-08-19 11:58:18 -040043 for instance in self.instances.all():
44 site = instance.node.site_deployment.site
45 deployment = instance.node.site_deployment.deployment
Scott Baker7a76f322015-01-16 19:07:36 -080046 used_sites[site.name] = used_sites.get(site.name, 0) + 1
47 used_deployments[deployment.name] = used_deployments.get(deployment.name, 0) + 1
Tony Mack3de59e32015-08-19 11:58:18 -040048 instanceCount = instanceCount + 1
Scott Baker5f4770d2014-07-13 11:17:58 -070049
Tony Mack3de59e32015-08-19 11:58:18 -040050 sshCommand = instance.get_ssh_command()
Scott Baker970314b2015-01-25 22:16:13 -080051 if sshCommand:
52 sshCommands.append(sshCommand)
Scott Bakerec930102015-01-20 01:02:08 -080053
Scott Baker21909342015-01-22 15:21:24 -080054 ready_sites[site.name] = ready_sites.get(site.name, 0) + 1
55
Scott Baker7a76f322015-01-16 19:07:36 -080056 users = {}
Sapan Bhatia8918ac32017-07-09 00:43:27 -040057 for priv in Privilege.objects.filter(object_id=self.id, object_type='Slice', accessor_type='User'):
58 if not (priv.accessor_id in users.keys()):
59 user = User.objects.get(pk=priv.accessor_id)
60 users[priv.accessor_id] = {"name": user.email, "id": user.id, "roles": []}
61 users[priv.accessor_id]["roles"].append(priv.permission)
Scott Baker5f4770d2014-07-13 11:17:58 -070062
Scott Baker04ab7c82015-01-20 13:30:40 -080063 # XXX this assumes there is only one network that can have ports bound
64 # to it for a given slice. This is intended for the tenant view, which
65 # will obey this field.
66 networkPorts = ""
67 for networkSlice in self.networkslices.all():
68 network = networkSlice.network
69 if (network.owner.id != self.id):
70 continue
71 if network.ports:
72 networkPorts = network.ports
73
Scott Baker7a76f322015-01-16 19:07:36 -080074 self._sliceInfo= {"sitesUsed": used_sites,
Scott Baker21909342015-01-22 15:21:24 -080075 "sitesReady": ready_sites,
Scott Baker7a76f322015-01-16 19:07:36 -080076 "deploymentsUsed": used_deployments,
Tony Mack3de59e32015-08-19 11:58:18 -040077 "instanceCount": instanceCount,
Scott Baker7a76f322015-01-16 19:07:36 -080078 "siteCount": len(used_sites.keys()),
79 "users": users,
Scott Bakerec930102015-01-20 01:02:08 -080080 "roles": [],
Scott Baker04ab7c82015-01-20 13:30:40 -080081 "sshCommands": sshCommands,
82 "networkPorts": networkPorts}
Scott Baker7a76f322015-01-16 19:07:36 -080083
84 if user:
85 auser = self._sliceInfo["users"].get(user.id, None)
86 if (auser):
87 self._sliceInfo["roles"] = auser["roles"]
88
89 return self._sliceInfo
Scott Baker88e34372014-07-13 11:46:36 -070090
Scott Baker8b89d302015-01-08 22:34:51 -080091 @property
Scott Baker21909342015-01-22 15:21:24 -080092 def site_ready(self):
93 return self.getSliceInfo()["sitesReady"]
94
95 @site_ready.setter
96 def site_ready(self, value):
97 pass
98
99 @property
Scott Bakerdcf6fbf2015-01-11 13:45:19 -0800100 def site_allocation(self):
Scott Baker55f6de62015-01-18 16:07:58 -0800101 return self._site_allocation
Scott Bakerdcf6fbf2015-01-11 13:45:19 -0800102
103 @site_allocation.setter
104 def site_allocation(self, value):
Scott Baker55f6de62015-01-18 16:07:58 -0800105 self._site_allocation = value
Scott Bakerdcf6fbf2015-01-11 13:45:19 -0800106
107 @property
Scott Baker7a76f322015-01-16 19:07:36 -0800108 def user_names(self):
109 return [user["name"] for user in self.getSliceInfo()["users"].values()]
110
Scott Baker12154242015-01-16 19:26:54 -0800111 @user_names.setter
112 def user_names(self, value):
113 pass # it's read-only
114
Scott Baker7a76f322015-01-16 19:07:36 -0800115 @property
Scott Baker97acad92015-01-12 19:45:40 -0800116 def users(self):
Scott Baker7a76f322015-01-16 19:07:36 -0800117 return [user["id"] for user in self.getSliceInfo()["users"].values()]
Scott Baker97acad92015-01-12 19:45:40 -0800118
119 @users.setter
120 def users(self, value):
Scott Bakera76f65d2015-01-13 16:22:57 -0800121 self._update_users = value
122 #print "XXX set users to", value
Scott Baker97acad92015-01-12 19:45:40 -0800123
124 @property
Scott Bakerd3a6b2c2015-01-08 22:37:34 -0800125 def network_ports(self):
Scott Baker04ab7c82015-01-20 13:30:40 -0800126 return self._network_ports
Scott Baker8b89d302015-01-08 22:34:51 -0800127
Scott Bakerd3a6b2c2015-01-08 22:37:34 -0800128 @network_ports.setter
129 def network_ports(self, value):
Scott Baker04ab7c82015-01-20 13:30:40 -0800130 self._network_ports = value
131 #print "XXX set networkPorts to", value
Scott Baker8b89d302015-01-08 22:34:51 -0800132
Scott Baker88e34372014-07-13 11:46:36 -0700133 @staticmethod
134 def select_by_user(user):
Scott Baker88e34372014-07-13 11:46:36 -0700135 if user.is_admin:
136 qs = SlicePlus.objects.all()
137 else:
Sapan Bhatia8918ac32017-07-09 00:43:27 -0400138 slice_ids = [sp.slice.id for sp in Privilege.objects.filter(accessor_type='User',accessor_id=user.id, object_type='Slice')]
Scott Baker88e34372014-07-13 11:46:36 -0700139 qs = SlicePlus.objects.filter(id__in=slice_ids)
Scott Baker88e34372014-07-13 11:46:36 -0700140 return qs
Scott Bakera76f65d2015-01-13 16:22:57 -0800141
Scott Baker435c2c92015-01-14 00:34:45 -0800142 def get_node_allocation(self, siteList):
Scott Bakera76f65d2015-01-13 16:22:57 -0800143 siteIDList = [site.id for site in siteList]
144 nodeList = []
145 for node in Node.objects.all():
146 if (node.site_deployment.site.id in siteIDList):
Tony Mack3de59e32015-08-19 11:58:18 -0400147 node.instanceCount = 0
148 for instance in node.instances.all():
149 if instance.slice.id == self.id:
150 node.instanceCount = node.instanceCount + 1
Scott Bakera76f65d2015-01-13 16:22:57 -0800151 nodeList.append(node)
152 return nodeList
153
154 def save(self, *args, **kwargs):
Scott Baker8974e552015-02-10 19:26:00 -0800155 if (not hasattr(self,"caller")) or self.caller==None:
156 raise APIException("no self.caller in SlicePlus.save")
157
Scott Baker55f6de62015-01-18 16:07:58 -0800158 updated_image = self.has_field_changed("default_image")
159 updated_flavor = self.has_field_changed("default_flavor")
160
Scott Bakera76f65d2015-01-13 16:22:57 -0800161 super(SlicePlus, self).save(*args, **kwargs)
162
Scott Baker04ab7c82015-01-20 13:30:40 -0800163 # try things out first
164
Scott Baker55f6de62015-01-18 16:07:58 -0800165 updated_sites = (self._site_allocation != self._initial_site_allocation) or updated_image or updated_flavor
166 if updated_sites:
167 self.save_site_allocation(noAct=True, reset=(updated_image or updated_flavor))
Scott Baker435c2c92015-01-14 00:34:45 -0800168
169 if self._update_users:
170 self.save_users(noAct=True)
171
Scott Baker04ab7c82015-01-20 13:30:40 -0800172 if (self._network_ports != self._initial_network_ports):
173 self.save_network_ports(noAct=True)
174
175 # now actually save them
176
Scott Baker55f6de62015-01-18 16:07:58 -0800177 if updated_sites:
178 self.save_site_allocation(reset=(updated_image or updated_flavor))
Scott Bakera76f65d2015-01-13 16:22:57 -0800179
Scott Baker435c2c92015-01-14 00:34:45 -0800180 if self._update_users:
181 self.save_users()
182
Scott Baker04ab7c82015-01-20 13:30:40 -0800183 if (self._network_ports != self._initial_network_ports):
184 self.save_network_ports()
185
Scott Baker55f6de62015-01-18 16:07:58 -0800186 def save_site_allocation(self, noAct = False, reset=False):
187 print "save_site_allocation, reset=",reset
Scott Bakera76f65d2015-01-13 16:22:57 -0800188
Scott Baker75081422015-01-19 08:43:50 -0800189 if (not self._site_allocation):
Tony Mack3de59e32015-08-19 11:58:18 -0400190 # Must be a instance that was just created, and has not site_allocation
Scott Baker75081422015-01-19 08:43:50 -0800191 # field.
192 return
193
Tony Mack3de59e32015-08-19 11:58:18 -0400194 all_slice_instances = self.instances.all()
Scott Baker55f6de62015-01-18 16:07:58 -0800195 for site_name in self._site_allocation.keys():
196 desired_allocation = self._site_allocation[site_name]
Scott Bakera76f65d2015-01-13 16:22:57 -0800197
Tony Mack3de59e32015-08-19 11:58:18 -0400198 # make a list of the instances for this site
199 instances = []
200 for instance in all_slice_instances:
201 if instance.node.site_deployment.site.name == site_name:
202 instances.append(instance)
Scott Bakera76f65d2015-01-13 16:22:57 -0800203
Tony Mack3de59e32015-08-19 11:58:18 -0400204 # delete extra instances
205 while (reset and len(instances)>0) or (len(instances) > desired_allocation):
206 instance = instances.pop()
Scott Bakera76f65d2015-01-13 16:22:57 -0800207 if (not noAct):
Tony Mack3de59e32015-08-19 11:58:18 -0400208 print "deleting instance", instance
209 instance.delete()
Scott Baker55f6de62015-01-18 16:07:58 -0800210 else:
Tony Mack3de59e32015-08-19 11:58:18 -0400211 print "would delete instance", instance
Scott Bakera76f65d2015-01-13 16:22:57 -0800212
Tony Mack3de59e32015-08-19 11:58:18 -0400213 # add more instances
214 if (len(instances) < desired_allocation):
Scott Bakera76f65d2015-01-13 16:22:57 -0800215 site = Site.objects.get(name = site_name)
Scott Baker435c2c92015-01-14 00:34:45 -0800216 nodes = self.get_node_allocation([site])
Scott Bakera76f65d2015-01-13 16:22:57 -0800217
218 if (not nodes):
Scott Baker3e035222015-01-24 00:01:29 -0800219 raise APIException(detail="no nodes in site %s" % site_name)
Scott Bakera76f65d2015-01-13 16:22:57 -0800220
Tony Mack3de59e32015-08-19 11:58:18 -0400221 while (len(instances) < desired_allocation):
Scott Bakera76f65d2015-01-13 16:22:57 -0800222 # pick the least allocated node
Tony Mack3de59e32015-08-19 11:58:18 -0400223 nodes = sorted(nodes, key=attrgetter("instanceCount"))
Scott Bakera76f65d2015-01-13 16:22:57 -0800224 node = nodes[0]
225
Tony Mack3de59e32015-08-19 11:58:18 -0400226 instance = Instance(name=node.name,
Scott Bakera76f65d2015-01-13 16:22:57 -0800227 slice=self,
228 node=node,
229 image = self.default_image,
230 flavor = self.default_flavor,
231 creator = self.creator,
232 deployment = node.site_deployment.deployment)
Tony Mack3de59e32015-08-19 11:58:18 -0400233 instance.caller = self.caller
234 instances.append(instance)
Scott Bakera76f65d2015-01-13 16:22:57 -0800235 if (not noAct):
Tony Mack3de59e32015-08-19 11:58:18 -0400236 print "added instance", instance
237 instance.save()
Scott Baker55f6de62015-01-18 16:07:58 -0800238 else:
Tony Mack3de59e32015-08-19 11:58:18 -0400239 print "would add instance", instance
Scott Bakera76f65d2015-01-13 16:22:57 -0800240
Tony Mack3de59e32015-08-19 11:58:18 -0400241 node.instanceCount = node.instanceCount + 1
Scott Bakera76f65d2015-01-13 16:22:57 -0800242
Scott Baker435c2c92015-01-14 00:34:45 -0800243 def save_users(self, noAct = False):
244 new_users = self._update_users
245
Scott Baker8974e552015-02-10 19:26:00 -0800246 try:
247 default_role = SliceRole.objects.get(role="access")
248 except:
249 default_role = SliceRole.objects.get(role="default")
Scott Baker435c2c92015-01-14 00:34:45 -0800250
Sapan Bhatia8918ac32017-07-09 00:43:27 -0400251 slice_privs = Privilege.objects.filter(object_id=self.id, object_type='Slice', accessor_type='User')
252 slice_user_ids = [priv.accessor_id for priv in slice_privs]
Scott Baker435c2c92015-01-14 00:34:45 -0800253
254 for user_id in new_users:
255 if (user_id not in slice_user_ids):
Sapan Bhatia8918ac32017-07-09 00:43:27 -0400256 priv = Privilege(object_id=self.id, accessor_id=user_id, permission='role:'+default_role, accessor_type='User', object_type='Slice')
Scott Baker8974e552015-02-10 19:26:00 -0800257 priv.caller = self.caller
Scott Baker435c2c92015-01-14 00:34:45 -0800258 if (not noAct):
259 priv.save()
260
261 print "added user id", user_id
262
263 for priv in slice_privs:
264 if (priv.role.id != default_role.id):
265 # only mess with 'default' users; don't kill an admin
266 continue
267
268 if (priv.user.id not in new_users):
269 if (not noAct):
270 priv.delete()
271
272 print "deleted user id", user_id
273
Scott Baker04ab7c82015-01-20 13:30:40 -0800274 def save_network_ports(self, noAct=False):
275 # First search for any network that already has a filled in 'ports'
276 # field. We'll assume there can only be one, so it must be the right
277 # one.
278 for networkSlice in self.networkslices.all():
279 network = networkSlice.network
280 if (network.owner.id != self.id):
281 continue
282 if network.ports:
283 network.ports = self._network_ports
Scott Baker8974e552015-02-10 19:26:00 -0800284 network.caller = self.caller
Scott Baker04ab7c82015-01-20 13:30:40 -0800285 if (not noAct):
286 network.save()
287 return
288
289 # Now try a network that is a "NAT", since setting ports on a non-NAT
290 # network doesn't make much sense.
291 for networkSlice in self.networkslices.all():
292 network = networkSlice.network
293 if (network.owner.id != self.id):
294 continue
295 if network.template.translation=="NAT":
296 network.ports = self._network_ports
Scott Baker8974e552015-02-10 19:26:00 -0800297 network.caller = self.caller
Scott Baker04ab7c82015-01-20 13:30:40 -0800298 if (not noAct):
299 network.save()
300 return
301
302 # uh oh, we didn't find a network
303
Scott Baker3e035222015-01-24 00:01:29 -0800304 raise APIException(detail="No network was found that ports could be set on")
Scott Baker04ab7c82015-01-20 13:30:40 -0800305
Scott Baker435c2c92015-01-14 00:34:45 -0800306
307
308
Scott Bakera76f65d2015-01-13 16:22:57 -0800309