diff options
Diffstat (limited to 'dashboard/src/api/models.py')
-rw-r--r-- | dashboard/src/api/models.py | 342 |
1 files changed, 303 insertions, 39 deletions
diff --git a/dashboard/src/api/models.py b/dashboard/src/api/models.py index 78ec920..1f708ae 100644 --- a/dashboard/src/api/models.py +++ b/dashboard/src/api/models.py @@ -11,6 +11,8 @@ from django.contrib.auth.models import User from django.db import models from django.core.exceptions import PermissionDenied +from django.shortcuts import get_object_or_404 +from django.urls import reverse import json import uuid @@ -21,8 +23,13 @@ from resource_inventory.models import ( HostProfile, Host, Image, - Interface + Interface, + HostOPNFVConfig, + RemoteInfo, + OPNFVConfig ) +from resource_inventory.idf_templater import IDFTemplater +from resource_inventory.pdf_templater import PDFTemplater class JobStatus(object): @@ -42,7 +49,7 @@ class LabManagerTracker(object): """ try: lab = Lab.objects.get(name=lab_name) - except: + except Exception: raise PermissionDenied("Lab not found") if lab.api_token == token: return LabManager(lab) @@ -60,6 +67,47 @@ class LabManager(object): def __init__(self, lab): self.lab = lab + def update_host_remote_info(self, data, host_id): + host = get_object_or_404(Host, labid=host_id, lab=self.lab) + info = {} + try: + info['address'] = data['address'] + info['mac_address'] = data['mac_address'] + info['password'] = data['password'] + info['user'] = data['user'] + info['type'] = data['type'] + info['versions'] = json.dumps(data['versions']) + except Exception as e: + return {"error": "invalid arguement: " + str(e)} + remote_info = host.remote_management + if "default" in remote_info.mac_address: + remote_info = RemoteInfo() + remote_info.address = info['address'] + remote_info.mac_address = info['mac_address'] + remote_info.password = info['password'] + remote_info.user = info['user'] + remote_info.type = info['type'] + remote_info.versions = info['versions'] + remote_info.save() + host.remote_management = remote_info + host.save() + booking = Booking.objects.get(resource=host.bundle) + self.update_xdf(booking) + return {"status": "success"} + + def update_xdf(self, booking): + booking.pdf = PDFTemplater.makePDF(booking) + booking.idf = IDFTemplater().makeIDF(booking) + booking.save() + + def get_pdf(self, booking_id): + booking = get_object_or_404(Booking, pk=booking_id, lab=self.lab) + return booking.pdf + + def get_idf(self, booking_id): + booking = get_object_or_404(Booking, pk=booking_id, lab=self.lab) + return booking.idf + def get_profile(self): prof = {} prof['name'] = self.lab.name @@ -88,6 +136,22 @@ class LabManager(object): inventory['host_types'] = self.serialize_host_profiles(profiles) return inventory + def get_host(self, hostname): + host = get_object_or_404(Host, labid=hostname, lab=self.lab) + return { + "booked": host.booked, + "working": host.working, + "type": host.profile.name + } + + def update_host(self, hostname, data): + host = get_object_or_404(Host, labid=hostname, lab=self.lab) + if "working" in data: + working = data['working'] == "true" + host.working = working + host.save() + return self.get_host(hostname) + def get_status(self): return {"status": self.lab.status} @@ -214,6 +278,10 @@ class Job(models.Model): if 'network' not in d: d['network'] = {} d['network'][relation.task_id] = relation.config.to_dict() + for relation in SnapshotRelation.objects.filter(job=self): + if 'snapshot' not in d: + d['snapshot'] = {} + d['snapshot'][relation.task_id] = relation.config.to_dict() j['payload'] = d @@ -221,7 +289,13 @@ class Job(models.Model): def get_tasklist(self, status="all"): tasklist = [] - clist = [HostHardwareRelation, AccessRelation, HostNetworkRelation, SoftwareRelation] + clist = [ + HostHardwareRelation, + AccessRelation, + HostNetworkRelation, + SoftwareRelation, + SnapshotRelation + ] if status == "all": for cls in clist: tasklist += list(cls.objects.filter(job=self)) @@ -261,6 +335,10 @@ class Job(models.Model): if 'network' not in d: d['network'] = {} d['network'][relation.task_id] = relation.config.get_delta() + for relation in SnapshotRelation.objects.filter(job=self).filter(status=status): + if 'snapshot' not in d: + d['snapshot'] = {} + d['snapshot'][relation.task_id] = relation.config.get_delta() j['payload'] = d return j @@ -283,25 +361,71 @@ class TaskConfig(models.Model): self.delta = '{}' +class BridgeConfig(models.Model): + """ + Displays mapping between jumphost interfaces and + bridges + """ + interfaces = models.ManyToManyField(Interface) + opnfv_config = models.ForeignKey(OPNFVConfig, on_delete=models.CASCADE) + + def to_dict(self): + d = {} + hid = self.interfaces.first().host.labid + d[hid] = {} + for interface in self.interfaces.all(): + d[hid][interface.mac_address] = [] + for vlan in interface.config.all(): + network_role = self.opnfv_model.networks().filter(network=vlan.network) + bridge = IDFTemplater.bridge_names[network_role.name] + br_config = { + "vlan_id": vlan.vlan_id, + "tagged": vlan.tagged, + "bridge": bridge + } + d[hid][interface.mac_address].append(br_config) + return d + + def to_json(self): + return json.dumps(self.to_dict()) + + class OpnfvApiConfig(models.Model): - installer = models.CharField(max_length=100) - scenario = models.CharField(max_length=100) + installer = models.CharField(max_length=200) + scenario = models.CharField(max_length=300) roles = models.ManyToManyField(Host) + # pdf and idf are url endpoints, not the actual file + pdf = models.CharField(max_length=100) + idf = models.CharField(max_length=100) + bridge_config = models.OneToOneField(BridgeConfig, on_delete=models.CASCADE, null=True) delta = models.TextField() + opnfv_config = models.ForeignKey(OPNFVConfig, null=True, on_delete=models.SET_NULL) def to_dict(self): d = {} + if not self.opnfv_config: + return d if self.installer: d['installer'] = self.installer if self.scenario: d['scenario'] = self.scenario + if self.pdf: + d['pdf'] = self.pdf + if self.idf: + d['idf'] = self.idf + if self.bridge_config: + d['bridged_interfaces'] = self.bridge_config.to_dict() hosts = self.roles.all() if hosts.exists(): d['roles'] = [] - for host in self.roles.all(): - d['roles'].append({host.labid: host.config.opnfvRole.name}) + for host in hosts: + d['roles'].append({ + host.labid: self.opnfv_config.host_opnfv_config.get( + host_config__pk=host.config.pk + ).role.name + }) return d @@ -320,6 +444,16 @@ class OpnfvApiConfig(models.Model): d['scenario'] = scenario self.delta = json.dumps(d) + def set_xdf(self, booking, update_delta=True): + kwargs = {'lab_name': booking.lab.name, 'booking_id': booking.id} + self.pdf = reverse('get-pdf', kwargs=kwargs) + self.idf = reverse('get-idf', kwargs=kwargs) + if update_delta: + d = json.loads(self.delta) + d['pdf'] = self.pdf + d['idf'] = self.idf + self.delta = json.dumps(d) + def add_role(self, host): self.roles.add(host) d = json.loads(self.delta) @@ -343,14 +477,17 @@ class AccessConfig(TaskConfig): user = models.ForeignKey(User, on_delete=models.CASCADE) revoke = models.BooleanField(default=False) context = models.TextField(default="") - delta = models.TextField() + delta = models.TextField(default="{}") def to_dict(self): d = {} d['access_type'] = self.access_type d['user'] = self.user.id d['revoke'] = self.revoke - d['context'] = json.loads(self.context) + try: + d['context'] = json.loads(self.context) + except Exception: + pass return d def get_delta(self): @@ -531,8 +668,64 @@ class NetworkConfig(TaskConfig): self.delta = json.dumps(d) +class SnapshotConfig(TaskConfig): + + host = models.ForeignKey(Host, null=True, on_delete=models.DO_NOTHING) + image = models.IntegerField(null=True) + dashboard_id = models.IntegerField() + delta = models.TextField(default="{}") + + def to_dict(self): + d = {} + if self.host: + d['host'] = self.host.labid + if self.image: + d['image'] = self.image + d['dashboard_id'] = self.dashboard_id + return d + + def to_json(self): + return json.dumps(self.to_dict()) + + def get_delta(self): + if not self.delta: + self.delta = self.to_json() + self.save() + + d = json.loads(self.delta) + return d + + def clear_delta(self): + self.delta = json.dumps(self.to_dict()) + self.save() + + def set_host(self, host): + self.host = host + d = json.loads(self.delta) + d['host'] = host.labid + self.delta = json.dumps(d) + + def set_image(self, image): + self.image = image + d = json.loads(self.delta) + d['image'] = self.image + self.delta = json.dumps(d) + + def clear_image(self): + self.image = None + d = json.loads(self.delta) + d.pop("image", None) + self.delta = json.dumps(d) + + def set_dashboard_id(self, dash): + self.dashboard_id = dash + d = json.loads(self.delta) + d['dashboard_id'] = self.dashboard_id + self.delta = json.dumps(d) + + def get_task(task_id): - for taskclass in [AccessRelation, SoftwareRelation, HostHardwareRelation, HostNetworkRelation]: + for taskclass in [AccessRelation, SoftwareRelation, HostHardwareRelation, HostNetworkRelation, SnapshotRelation]: try: ret = taskclass.objects.get(task_id=task_id) return ret @@ -614,15 +807,72 @@ class HostNetworkRelation(TaskRelation): return super(self.__class__, self).delete(*args, **kwargs) +class SnapshotRelation(TaskRelation): + snapshot = models.ForeignKey(Image, on_delete=models.CASCADE) + config = models.OneToOneField(SnapshotConfig, on_delete=models.CASCADE) + + def type_str(self): + return "Snapshot Task" + + def get_delta(self): + return self.config.to_dict() + + def delete(self, *args, **kwargs): + self.config.delete() + return super(self.__class__, self).delete(*args, **kwargs) + + class JobFactory(object): @classmethod + def reimageHost(cls, new_image, booking, host): + """ + This method will make all necessary changes to make a lab + reimage a host. + """ + job = Job.objects.get(booking=booking) + # make hardware task new + hardware_relation = HostHardwareRelation.objects.get(host=host, job=job) + hardware_relation.config.set_image(new_image.lab_id) + hardware_relation.config.save() + hardware_relation.status = JobStatus.NEW + + # re-apply networking after host is reset + net_relation = HostNetworkRelation.objects.get(host=host, job=job) + net_relation.status = JobStatus.NEW + + # re-apply ssh access after host is reset + for relation in AccessRelation.objects.filter(job=job, config__access_type="ssh"): + relation.status = JobStatus.NEW + relation.save() + + hardware_relation.save() + net_relation.save() + + @classmethod + def makeSnapshotTask(cls, image, booking, host): + relation = SnapshotRelation() + job = Job.objects.get(booking=booking) + config = SnapshotConfig.objects.create(dashboard_id=image.id) + + relation.job = job + relation.config = config + relation.config.save() + relation.config = relation.config + relation.snapshot = image + relation.save() + + config.clear_delta() + config.set_host(host) + config.save() + + @classmethod def makeCompleteJob(cls, booking): hosts = Host.objects.filter(bundle=booking.resource) job = None try: job = Job.objects.get(booking=booking) - except: + except Exception: job = Job.objects.create(status=JobStatus.NEW, booking=booking) cls.makeHardwareConfigs( hosts=hosts, @@ -633,7 +883,7 @@ class JobFactory(object): job=job ) cls.makeSoftware( - hosts=hosts, + booking=booking, job=job ) all_users = list(booking.collaborators.all()) @@ -652,7 +902,7 @@ class JobFactory(object): revoke=False, job=job, context={ - "key": user.userprofile.ssh_public_key.read(), + "key": user.userprofile.ssh_public_key.open().read().decode(encoding="UTF-8"), "hosts": [host.labid for host in hosts] } ) @@ -665,7 +915,7 @@ class JobFactory(object): hardware_config = None try: hardware_config = HardwareConfig.objects.get(relation__host=host) - except: + except Exception: hardware_config = HardwareConfig() relation = HostHardwareRelation() @@ -691,12 +941,12 @@ class JobFactory(object): config = AccessConfig() config.access_type = access_type config.user = user - if context: - config.set_context(context) config.save() relation.config = config relation.save() config.clear_delta() + if context: + config.set_context(context) config.set_access_type(access_type) config.set_revoke(revoke) config.set_user(user) @@ -708,7 +958,7 @@ class JobFactory(object): network_config = None try: network_config = NetworkConfig.objects.get(relation__host=host) - except: + except Exception: network_config = NetworkConfig.objects.create() relation = HostNetworkRelation() @@ -724,28 +974,42 @@ class JobFactory(object): network_config.save() @classmethod - def makeSoftware(cls, hosts=[], job=Job()): - def init_config(host): - opnfv_config = OpnfvApiConfig() - if host is not None: - opnfv = host.config.bundle.opnfv_config.first() - opnfv_config.installer = opnfv.installer.name - opnfv_config.scenario = opnfv.scenario.name - opnfv_config.save() - return opnfv_config - + def make_bridge_config(cls, booking): + if booking.resource.hosts.count() < 2: + return None try: - host = None - if len(hosts) > 0: - host = hosts[0] - opnfv_config = init_config(host) + jumphost_config = HostOPNFVConfig.objects.filter( + role__name__iexact="jumphost" + ) + jumphost = Host.objects.get( + bundle=booking.resource, + config=jumphost_config.host_config + ) + except Exception: + return None + br_config = BridgeConfig.objects.create(opnfv_config=booking.opnfv_config) + for iface in jumphost.interfaces.all(): + br_config.interfaces.add(iface) + return br_config - for host in hosts: - opnfv_config.roles.add(host) - software_config = SoftwareConfig.objects.create(opnfv=opnfv_config) - software_config.save() - software_relation = SoftwareRelation.objects.create(job=job, config=software_config) - software_relation.save() - return software_relation - except: + @classmethod + def makeSoftware(cls, booking=None, job=Job()): + + if not booking.opnfv_config: return None + + opnfv_api_config = OpnfvApiConfig.objects.create( + opnfv_config=booking.opnfv_config, + installer=booking.opnfv_config.installer.name, + scenario=booking.opnfv_config.scenario.name, + bridge_config=cls.make_bridge_config(booking) + ) + + opnfv_api_config.set_xdf(booking, False) + opnfv_api_config.save() + + for host in booking.resource.hosts.all(): + opnfv_api_config.roles.add(host) + software_config = SoftwareConfig.objects.create(opnfv=opnfv_api_config) + software_relation = SoftwareRelation.objects.create(job=job, config=software_config) + return software_relation |