Add server create function to admin utils
[laas.git] / src / dashboard / admin_utils.py
1 from resource_inventory.models import (
2     ResourceTemplate,
3     Image,
4     Server,
5     ResourceBundle,
6     ResourceProfile,
7     InterfaceProfile,
8     PhysicalNetwork,
9     ResourceConfiguration,
10     NetworkConnection,
11     InterfaceConfiguration,
12     Network,
13     DiskProfile,
14     CpuProfile,
15     RamProfile,
16     Interface
17 )
18
19 import json
20
21 from django.contrib.auth.models import User
22
23 from account.models import Lab
24
25 from resource_inventory.resource_manager import ResourceManager
26 from resource_inventory.pdf_templater import PDFTemplater
27
28 from booking.quick_deployer import update_template
29
30 from datetime import timedelta
31
32 from django.utils import timezone
33
34 from booking.models import Booking
35 from notifier.manager import NotificationHandler
36 from api.models import JobFactory
37
38 from api.models import JobStatus
39
40
41 def print_div():
42     print("====================================================================")
43
44
45 def book_host(owner_username, host_labid, lab_username, hostname, image_id, template_name, length_days=21, collaborator_usernames=[], purpose="internal", project="LaaS"):
46     """
47     creates a quick booking using the given host
48     """
49     lab = Lab.objects.get(lab_user__username=lab_username)
50     host = Server.objects.filter(lab=lab).get(labid=host_labid)
51     if host.booked:
52         print("Can't book host, already marked as booked")
53         return
54     else:
55         host.booked = True
56         host.save()
57
58     template = ResourceTemplate.objects.filter(public=True).get(name=template_name)
59     image = Image.objects.get(id=image_id)
60
61     owner = User.objects.get(username=owner_username)
62
63     new_template = update_template(template, image, hostname, owner)
64
65     rmanager = ResourceManager.getInstance()
66
67     vlan_map = rmanager.get_vlans(new_template)
68
69     # only a single host so can reuse var for iter here
70     resource_bundle = ResourceBundle.objects.create(template=new_template)
71     res_configs = new_template.getConfigs()
72
73     for config in res_configs:
74         try:
75             host.bundle = resource_bundle
76             host.config = config
77             rmanager.configureNetworking(resource_bundle, host, vlan_map)
78             host.save()
79         except Exception:
80             host.booked = False
81             host.save()
82             print("Failed to book host due to error configuring it")
83             return
84
85     new_template.save()
86
87     booking = Booking.objects.create(
88         purpose=purpose,
89         project=project,
90         lab=lab,
91         owner=owner,
92         start=timezone.now(),
93         end=timezone.now() + timedelta(days=int(length_days)),
94         resource=resource_bundle,
95         opnfv_config=None
96     )
97
98     booking.pdf = PDFTemplater.makePDF(booking)
99
100     booking.save()
101
102     for collaborator_username in collaborator_usernames:
103         try:
104             user = User.objects.get(username=collaborator_username)
105             booking.collaborators.add(user)
106         except Exception:
107             print("couldn't add user with username ", collaborator_username)
108
109     booking.save()
110
111     JobFactory.makeCompleteJob(booking)
112     NotificationHandler.notify_new_booking(booking)
113
114
115 def mark_working(host_labid, lab_username, working=True):
116     lab = Lab.objects.get(lab_user__username=lab_username)
117     server = Server.objects.filter(lab=lab).get(labid=host_labid)
118     print("changing server working status from ", server.working, "to", working)
119     server.working = working
120     server.save()
121
122
123 def mark_booked(host_labid, lab_username, booked=True):
124     lab = Lab.objects.get(lab_user__username=lab_username)
125     server = Server.objects.filter(lab=lab).get(labid=host_labid)
126     print("changing server booked status from ", server.booked, "to", booked)
127     server.booked = booked
128     server.save()
129
130
131 # returns host filtered by lab and then unique id within lab
132 def get_host(host_labid, lab_username):
133     lab = Lab.objects.get(lab_user__username=lab_username)
134     return Server.objects.filter(lab=lab).get(labid=host_labid)
135
136
137 def get_info(host_labid, lab_username):
138     info = {}
139     host = get_host(host_labid, lab_username)
140     info['host_labid'] = host_labid
141     info['booked'] = host.booked
142     info['working'] = host.working
143     info['profile'] = str(host.profile)
144     if host.bundle:
145         binfo = {}
146         info['bundle'] = binfo
147     if host.config:
148         cinfo = {}
149         info['config'] = cinfo
150
151     return info
152
153
154 def map_cntt_interfaces(labid: str):
155     """
156     Use this during cntt migrations, call it with a host labid and it will change profiles for this host
157     as well as mapping its interfaces across. interface ens1f2 should have the mac address of interface eno50
158     as an invariant before calling this function
159     """
160     host = get_host(labid, "unh_iol")
161     host.profile = ResourceProfile.objects.get(name="HPE x86 CNTT")
162     host.save()
163     host = get_host(labid, "unh_iol")
164
165     for iface in host.interfaces.all():
166         new_ifprofile = None
167         if iface.profile.name == "ens1f2":
168             new_ifprofile = InterfaceProfile.objects.get(host=host.profile, name="eno50")
169         else:
170             new_ifprofile = InterfaceProfile.objects.get(host=host.profile, name=iface.profile.name)
171
172         iface.profile = new_ifprofile
173
174         iface.save()
175
176
177 def detect_leaked_hosts(labid="unh_iol"):
178     """
179     Use this to try to detect leaked hosts.
180     These hosts may still be in the process of unprovisioning,
181     but if they are not (or unprovisioning is frozen) then
182     these hosts are instead leaked
183     """
184     working_servers = Server.objects.filter(working=True, lab__lab_user__username=labid)
185     booked = working_servers.filter(booked=True)
186     filtered = booked
187     print_div()
188     print("In use now:")
189     for booking in Booking.objects.filter(end__gte=timezone.now()):
190         res_for_booking = booking.resource.get_resources()
191         print(res_for_booking)
192         for resource in res_for_booking:
193             filtered = filtered.exclude(id=resource.id)
194     print_div()
195     print("Possibly leaked:")
196     for host in filtered:
197         print(host)
198     print_div()
199     return filtered
200
201
202 def booking_for_host(host_labid: str, labid="unh_iol"):
203     server = Server.objects.get(lab__lab_user__username=labid, labid=host_labid)
204     booking = server.bundle.booking_set.first()
205     print_div()
206     print(booking)
207     print("id:", booking.id)
208     print("owner:", booking.owner)
209     print("job (id):", booking.job, "(" + str(booking.job.id) + ")")
210     print_div()
211     return booking
212
213
214 def force_release_booking(booking_id):
215     booking = Booking.objects.get(id=booking_id)
216     job = booking.job
217     tasks = job.get_tasklist()
218     for task in tasks:
219         task.status = JobStatus.DONE
220         task.save()
221
222
223 def get_network_metadata(booking_id: int):
224     booking = Booking.objects.get(id=booking_id)
225     bundle = booking.resource
226     pnets = PhysicalNetwork.objects.filter(bundle=bundle).all()
227     metadata = {}
228     for pnet in pnets:
229         net = pnet.generic_network
230         mdata = {"vlan_id": pnet.vlan_id, "netname": net.name, "public": net.is_public}
231         metadata[net.name] = mdata
232     return metadata
233
234
235 def print_dict_pretty(a_dict):
236     print(json.dumps(a_dict, sort_keys=True, indent=4))
237
238
239 """
240 schema:
241 {
242     "name": str
243     "description": str
244     "labs": [
245         str (lab username)
246     ]
247     "disks": {
248         <diskname> : {
249             capacity: int (GiB)
250             media_type: str ("SSD" or "HDD")
251             interface: str ("sata", "sas", "ssd", "nvme", "scsi", or "iscsi")
252         }
253     }
254     interfaces: {
255         <intname>: {
256             "speed": int (mbit)
257             "nic_type": str ("onboard" or "pcie")
258             "order": int (compared to the other interfaces, indicates the "order" that the ports are laid out)
259         }
260     }
261     cpus: {
262         cores: int (hardware threads count)
263         architecture: str (x86_64" or "aarch64")
264         cpus: int (number of sockets)
265         cflags: str
266     }
267     ram: {
268         amount: int (GiB)
269         channels: int
270     }
271 }
272 """
273
274
275 def add_profile(data):
276     base_profile = ResourceProfile.objects.create(name=data['name'], description=data['description'])
277     base_profile.save()
278
279     for lab_username in data['labs']:
280         lab = Lab.objects.get(lab_user__username=lab_username)
281
282         base_profile.labs.add(lab)
283         base_profile.save()
284
285     for diskname in data['disks'].keys():
286         disk = data['disks'][diskname]
287
288         disk_profile = DiskProfile.objects.create(name=diskname, size=disk['capacity'], media_type=disk['media_type'], interface=disk['interface'], host=base_profile)
289         disk_profile.save()
290
291     for ifacename in data['interfaces'].keys():
292         iface = data['interfaces'][ifacename]
293
294         iface_profile = InterfaceProfile.objects.create(name=ifacename, speed=iface['speed'], nic_type=iface['nic_type'], order=iface['order'], host=base_profile)
295         iface_profile.save()
296
297     cpu = data['cpus']
298     cpu_prof = CpuProfile.objects.create(cores=cpu['cores'], architecture=cpu['architecture'], cpus=cpu['cpus'], cflags=cpu['cflags'], host=base_profile)
299     cpu_prof.save()
300
301     ram_prof = RamProfile.objects.create(amount=data['ram']['amount'], channels=data['ram']['channels'], host=base_profile)
302     ram_prof.save()
303
304
305 def make_default_template(resource_profile, image_id=None, template_name=None, connected_interface_names=None, interfaces_tagged=False, connected_interface_tagged=False, owner_username="root", lab_username="unh_iol", public=True, temporary=False, description=""):
306
307     if not resource_profile:
308         raise Exception("No viable continuation from none resource_profile")
309
310     if not template_name:
311         template_name = resource_profile.name
312
313     if not connected_interface_names:
314         connected_interface_names = [InterfaceProfile.objects.filter(host=resource_profile).first().name]
315         print("setting connected interface names to", connected_interface_names)
316
317     if not image_id:
318         image_id = Image.objects.filter(host_type=resource_profile).first().id
319
320     image = Image.objects.get(id=image_id)
321
322     base = ResourceTemplate.objects.create(
323         name=template_name,
324         xml="",
325         owner=User.objects.get(username=owner_username),
326         lab=Lab.objects.get(lab_user__username=lab_username), description=description,
327         public=public, temporary=temporary, copy_of=None)
328
329     rconf = ResourceConfiguration.objects.create(profile=resource_profile, image=image, template=base, is_head_node=True, name="opnfv_host")
330     rconf.save()
331
332     connected_interfaces = []
333
334     for iface_prof in InterfaceProfile.objects.filter(host=resource_profile).all():
335         iface_conf = InterfaceConfiguration.objects.create(profile=iface_prof, resource_config=rconf)
336
337         if iface_prof.name in connected_interface_names:
338             connected_interfaces.append(iface_conf)
339
340     network = Network.objects.create(name="public", bundle=base, is_public=True)
341
342     for iface in connected_interfaces:
343         connection = NetworkConnection.objects.create(network=network, vlan_is_tagged=interfaces_tagged)
344         connection.save()
345
346         iface.connections.add(connection)
347         print("adding connection to iface ", iface)
348         iface.save()
349         connection.save()
350
351
352 """
353 Note: interfaces should be dict from interface name (eg ens1f0) to dict of schema:
354     {
355         mac_address: <mac addr>,
356         bus_addr: <bus addr>, //this field is optional, "" is default
357     }
358 """
359
360
361 def add_server(profile, uname, interfaces, lab_username="unh_iol", vendor="unknown", model="unknown"):
362     server = Server.objects.create(
363         bundle=None,
364         profile=profile,
365         config=None,
366         working=True,
367         vendor=vendor,
368         model=model,
369         labid=uname,
370         lab=Lab.objects.get(lab_user__username=lab_username),
371         name=uname,
372         booked=False)
373
374     for iface_prof in InterfaceProfile.objects.filter(host=profile).all():
375         mac_addr = interfaces[iface_prof.name]["mac_address"]
376         bus_addr = "unknown"
377         if "bus_addr" in interfaces[iface_prof.name].keys():
378             bus_addr = interfaces[iface_prof.name]["bus_addr"]
379
380         iface = Interface.objects.create(acts_as=None, profile=iface_prof, mac_address=mac_addr, bus_address=bus_addr)
381         iface.save()
382
383         server.interfaces.add(iface)
384         server.save()