From f446907704b9cc0fee83cf4b4633cbfda03d2430 Mon Sep 17 00:00:00 2001 From: Parker Berberian Date: Thu, 14 Mar 2019 15:51:19 -0400 Subject: Fixing Network Models Change-Id: Ia2cdf069e90c8091e8d984c368e47f375aed02ea Signed-off-by: Parker Berberian --- src/workflow/models.py | 94 ++------------ src/workflow/resource_bundle_workflow.py | 214 ++++++++++++++++++------------- 2 files changed, 136 insertions(+), 172 deletions(-) (limited to 'src/workflow') diff --git a/src/workflow/models.py b/src/workflow/models.py index cdfddef..3784fe1 100644 --- a/src/workflow/models.py +++ b/src/workflow/models.py @@ -203,27 +203,6 @@ class Confirmation_Step(WorkflowStep): title = "Confirm Changes" description = "Does this all look right?" - def get_vlan_warning(self): - grb = self.repo_get(self.repo.SELECTED_GRESOURCE_BUNDLE, False) - if not grb: - return 0 - if self.repo.BOOKING_MODELS not in self.repo.el: - return 0 - vlan_manager = grb.lab.vlan_manager - if vlan_manager is None: - return 0 - hosts = grb.getHosts() - for host in hosts: - for interface in host.generic_interfaces.all(): - for vlan in interface.vlans.all(): - if vlan.public: - if not vlan_manager.public_vlan_is_available(vlan.vlan_id): - return 1 - else: - if not vlan_manager.is_available(vlan.vlan_id): - return 1 # There is a problem with these vlans - return 0 - def get_context(self): context = super(Confirmation_Step, self).get_context() context['form'] = ConfirmationForm() @@ -231,7 +210,6 @@ class Confirmation_Step(WorkflowStep): self.repo_get(self.repo.CONFIRMATION), default_flow_style=False ).strip() - context['vlan_warning'] = self.get_vlan_warning() return context @@ -262,33 +240,8 @@ class Confirmation_Step(WorkflowStep): pass else: - if "vlan_input" in request.POST: - if request.POST.get("vlan_input") == "True": - self.translate_vlans() - return self.render(request) pass - def translate_vlans(self): - grb = self.repo_get(self.repo.SELECTED_GRESOURCE_BUNDLE, False) - if not grb: - return 0 - vlan_manager = grb.lab.vlan_manager - if vlan_manager is None: - return 0 - hosts = grb.getHosts() - for host in hosts: - for interface in host.generic_interfaces.all(): - for vlan in interface.vlans.all(): - if not vlan.public: - if not vlan_manager.is_available(vlan.vlan_id): - vlan.vlan_id = vlan_manager.get_vlan() - vlan.save() - else: - if not vlan_manager.public_vlan_is_available(vlan.vlan_id): - pub_vlan = vlan_manager.get_public_vlan() - vlan.vlan_id = pub_vlan.vlan - vlan.save() - class Workflow(): @@ -453,6 +406,11 @@ class Repository(): except Exception as e: return "GRB, saving hosts generated exception: " + str(e) + " CODE:0x0005" + if 'networks' in models: + for net in models['networks'].values(): + net.bundle = bundle + net.save() + if 'interfaces' in models: for interface_set in models['interfaces'].values(): for interface in interface_set: @@ -464,20 +422,21 @@ class Repository(): else: return "GRB, no interface set provided. CODE:0x001a" - if 'vlans' in models: - for resource_name, mapping in models['vlans'].items(): - for profile_name, vlan_set in mapping.items(): + if 'connections' in models: + for resource_name, mapping in models['connections'].items(): + for profile_name, connection_set in mapping.items(): interface = GenericInterface.objects.get( profile__name=profile_name, host__resource__name=resource_name, host__resource__bundle=models['bundle'] ) - for vlan in vlan_set: + for connection in connection_set: try: - vlan.save() - interface.vlans.add(vlan) + connection.network = connection.network + connection.save() + interface.connections.add(connection) except Exception as e: - return "GRB, saving vlan " + str(vlan) + " failed. Exception: " + str(e) + ". CODE:0x0017" + return "GRB, saving vlan " + str(connection) + " failed. Exception: " + str(e) + ". CODE:0x0017" else: return "GRB, no vlan set provided. CODE:0x0018" @@ -534,9 +493,6 @@ class Repository(): else: return "BOOK, no selected resource. CODE:0x000e" - if not self.reserve_vlans(selected_grb): - return "BOOK, vlans not available" - if 'booking' in models: booking = models['booking'] else: @@ -593,30 +549,6 @@ class Repository(): except Exception as e: return "BOOK, saving booking generated exception: " + str(e) + " CODE:0x0016" - def reserve_vlans(self, grb): - """ - True is success - """ - vlans = [] - public_vlan = None - vlan_manager = grb.lab.vlan_manager - if vlan_manager is None: - return True - for host in grb.getHosts(): - for interface in host.generic_interfaces.all(): - for vlan in interface.vlans.all(): - if vlan.public: - public_vlan = vlan - else: - vlans.append(vlan.vlan_id) - - try: - vlan_manager.reserve_vlans(vlans) - vlan_manager.reserve_public_vlan(public_vlan.vlan_id) - return True - except Exception: - return False - def __init__(self): self.el = {} self.el[self.CONFIRMATION] = {} diff --git a/src/workflow/resource_bundle_workflow.py b/src/workflow/resource_bundle_workflow.py index 4858ebe..536187f 100644 --- a/src/workflow/resource_bundle_workflow.py +++ b/src/workflow/resource_bundle_workflow.py @@ -10,6 +10,7 @@ from django.shortcuts import render from django.forms import formset_factory +from django.conf import settings import json import re @@ -25,11 +26,12 @@ from workflow.forms import ( ) from resource_inventory.models import ( GenericResourceBundle, - Vlan, GenericInterface, GenericHost, GenericResource, - HostProfile + HostProfile, + Network, + NetworkConnection ) from dashboard.exceptions import ( InvalidVlanConfigurationException, @@ -185,6 +187,7 @@ class Define_Nets(WorkflowStep): hostlist = self.repo_get(self.repo.GRB_LAST_HOSTLIST, None) added_list = [] added_dict = {} + context['debug'] = settings.DEBUG context['added_hosts'] = [] if hostlist is not None: new_hostlist = [] @@ -239,15 +242,15 @@ class Define_Nets(WorkflowStep): self.metastep.set_valid("Networks applied successfully") except ResourceAvailabilityException: self.metastep.set_invalid("Public network not availble") - except Exception: - self.metastep.set_invalid("An error occurred when applying networks") + except Exception as e: + self.metastep.set_invalid("An error occurred when applying networks: " + str(e)) return self.render(request) def updateModels(self, xmlData): models = self.repo_get(self.repo.GRESOURCE_BUNDLE_MODELS, {}) - models["vlans"] = {} - given_hosts, interfaces = self.parseXml(xmlData) - vlan_manager = models['bundle'].lab.vlan_manager + models["connections"] = {} + models['networks'] = {} + given_hosts, interfaces, networks = self.parseXml(xmlData) existing_host_list = models.get("hosts", []) existing_hosts = {} # maps id to host for host in existing_host_list: @@ -255,104 +258,133 @@ class Define_Nets(WorkflowStep): bundle = models.get("bundle", GenericResourceBundle(owner=self.repo_get(self.repo.SESSION_USER))) + for net_id, net in networks.items(): + network = Network() + network.name = net['name'] + network.bundle = bundle + network.is_public = net['public'] + models['networks'][net_id] = network + for hostid, given_host in given_hosts.items(): existing_host = existing_hosts[hostid[5:]] for ifaceId in given_host['interfaces']: iface = interfaces[ifaceId] - if existing_host.resource.name not in models['vlans']: - models['vlans'][existing_host.resource.name] = {} - models['vlans'][existing_host.resource.name][iface['profile_name']] = [] - for network in iface['networks']: - vlan_id = network['network']['vlan'] - is_public = network['network']['public'] - if is_public: - public_net = vlan_manager.get_public_vlan() - if public_net is None: - raise ResourceAvailabilityException("No public networks available") - vlan_id = vlan_manager.get_public_vlan().vlan - vlan = Vlan(vlan_id=vlan_id, tagged=network['tagged'], public=is_public) - models['vlans'][existing_host.resource.name][iface['profile_name']].append(vlan) + if existing_host.resource.name not in models['connections']: + models['connections'][existing_host.resource.name] = {} + models['connections'][existing_host.resource.name][iface['profile_name']] = [] + for connection in iface['connections']: + network_id = connection['network'] + net = models['networks'][network_id] + connection = NetworkConnection(vlan_is_tagged=connection['tagged'], network=net) + models['connections'][existing_host.resource.name][iface['profile_name']].append(connection) bundle.xml = xmlData self.repo_put(self.repo.GRESOURCE_BUNDLE_MODELS, models) - # serialize and deserialize xml from mxGraph - def parseXml(self, xmlString): - parent_nets = {} # map network ports to networks - networks = {} # maps net id to network object - hosts = {} # cotains id -> hosts, each containing interfaces, referencing networks - interfaces = {} # maps id -> interface + def decomposeXml(self, xmlString): + """ + This function takes in an xml doc from our front end + and returns dictionaries that map cellIds to the xml + nodes themselves. There is no unpacking of the + xml objects, just grouping and organizing + """ + + connections = {} + networks = {} + hosts = {} + interfaces = {} + network_ports = {} + xmlDom = minidom.parseString(xmlString) root = xmlDom.documentElement.firstChild - netids = {} - untagged_ints = {} for cell in root.childNodes: cellId = cell.getAttribute('id') + group = cellId.split("_")[0] + parentGroup = cell.getAttribute("parent").split("_")[0] + # place cell into correct group if cell.getAttribute("edge"): - # cell is a network connection - escaped_json_str = cell.getAttribute("value") - json_str = escaped_json_str.replace('"', '"') - attributes = json.loads(json_str) - tagged = attributes['tagged'] - interface = None - network = None - src = cell.getAttribute("source") - tgt = cell.getAttribute("target") - if src in parent_nets: - # src is a network port - network = networks[parent_nets[src]] - if tgt in untagged_ints and not tagged: - raise InvalidVlanConfigurationException("More than one untagged vlan on an interface") - interface = interfaces[tgt] - untagged_ints[tgt] = True - else: - network = networks[parent_nets[tgt]] - if src in untagged_ints and not tagged: - raise InvalidVlanConfigurationException("More than one untagged vlan on an interface") - interface = interfaces[src] - untagged_ints[src] = True - interface['networks'].append({"network": network, "tagged": tagged}) - - elif "network" in cellId: # cell is a network - escaped_json_str = cell.getAttribute("value") - json_str = escaped_json_str.replace('"', '"') - net_info = json.loads(json_str) - nid = net_info['vlan_id'] - public = net_info['public'] - try: - int_netid = int(nid) - assert public or int_netid > 1, "Net id is 1 or lower" - assert int_netid < 4095, "Net id is 4095 or greater" - except Exception: - raise InvalidVlanConfigurationException("VLAN ID is not an integer more than 1 and less than 4095") - if nid in netids: - raise NetworkExistsException("Non unique network id found") - else: - pass - network = {"name": net_info['name'], "vlan": net_info['vlan_id'], "public": public} - netids[net_info['vlan_id']] = True - networks[cellId] = network - - elif "host" in cellId: # cell is a host/machine - # TODO gather host info - cell_json_str = cell.getAttribute("value") - cell_json = json.loads(cell_json_str) - host = {"interfaces": [], "name": cellId, "profile_name": cell_json['name']} - hosts[cellId] = host - - elif cell.hasAttribute("parent"): - parentId = cell.getAttribute('parent') - if "network" in parentId: - parent_nets[cellId] = parentId - elif "host" in parentId: - # TODO gather iface info - cell_json_str = cell.getAttribute("value") - cell_json = json.loads(cell_json_str) - iface = {"name": cellId, "networks": [], "profile_name": cell_json['name']} - hosts[parentId]['interfaces'].append(cellId) - interfaces[cellId] = iface - return hosts, interfaces + connections[cellId] = cell + + elif "network" in group: + networks[cellId] = cell + + elif "host" in group: + hosts[cellId] = cell + + elif "host" in parentGroup: + interfaces[cellId] = cell + + # make network ports also map to thier network + elif "network" in parentGroup: + network_ports[cellId] = cell.getAttribute("parent") # maps port ID to net ID + + return connections, networks, hosts, interfaces, network_ports + + # serialize and deserialize xml from mxGraph + def parseXml(self, xmlString): + networks = {} # maps net name to network object + hosts = {} # cotains id -> hosts, each containing interfaces, referencing networks + interfaces = {} # maps id -> interface + untagged_ifaces = set() # used to check vlan config + network_names = set() # used to check network names + xml_connections, xml_nets, xml_hosts, xml_ifaces, xml_ports = self.decomposeXml(xmlString) + + # parse Hosts + for cellId, cell in xml_hosts.items(): + cell_json_str = cell.getAttribute("value") + cell_json = json.loads(cell_json_str) + host = {"interfaces": [], "name": cellId, "profile_name": cell_json['name']} + hosts[cellId] = host + + # parse networks + for cellId, cell in xml_nets.items(): + escaped_json_str = cell.getAttribute("value") + json_str = escaped_json_str.replace('"', '"') + net_info = json.loads(json_str) + net_name = net_info['name'] + public = net_info['public'] + if net_name in network_names: + raise NetworkExistsException("Non unique network name found") + network = {"name": net_name, "public": public, "id": cellId} + networks[cellId] = network + network_names.add(net_name) + + # parse interfaces + for cellId, cell in xml_ifaces.items(): + parentId = cell.getAttribute('parent') + cell_json_str = cell.getAttribute("value") + cell_json = json.loads(cell_json_str) + iface = {"name": cellId, "connections": [], "profile_name": cell_json['name']} + hosts[parentId]['interfaces'].append(cellId) + interfaces[cellId] = iface + + # parse connections + for cellId, cell in xml_connections.items(): + escaped_json_str = cell.getAttribute("value") + json_str = escaped_json_str.replace('"', '"') + attributes = json.loads(json_str) + tagged = attributes['tagged'] + interface = None + network = None + src = cell.getAttribute("source") + tgt = cell.getAttribute("target") + if src in interfaces: + interface = interfaces[src] + network = networks[xml_ports[tgt]] + else: + interface = interfaces[tgt] + network = networks[xml_ports[src]] + + if not tagged: + if interface['name'] in untagged_ifaces: + raise InvalidVlanConfigurationException("More than one untagged vlan on an interface") + untagged_ifaces.add(interface['name']) + + # add connection to interface + interface['connections'].append({"tagged": tagged, "network": network['id']}) + + return hosts, interfaces, networks class Resource_Meta_Info(WorkflowStep): -- cgit 1.2.3-korg