def add_topology(self, data, user): logger.debug("topology name = %s, model = %s", data[NAME], data[MODEL_NAME]) self._validate_defaults(data[DEFAULTS]) self._top = Topology() self._top.name = data[NAME] self._top.model_name = data[MODEL_NAME] self._top.is_fabric = False self._top.updated_by = user self._top.save() # defaults object self._top.defaults = dict() self._top.defaults[SWITCHES] = list() self._top.defaults[LINKS] = list() # add topology default switches with dummy=True for item in data[DEFAULTS][SWITCHES]: if item[MODEL] == 1: raise IgniteException(ERR_CAN_NOT_ASSIGN_UNKOWN_MODEL) switch = Switch() switch.topology = self._top switch.dummy = True switch.name = DEFAULT switch.tier = item[TIER] switch.model = get_switch(item[MODEL]) switch.image_profile = get_profile(item[IMAGE_PROFILE]) switch.save() # add switch to topology defaults self._top.defaults[SWITCHES].append(switch) # add topology default links with dummy=True for item in data[DEFAULTS][LINKS]: link = Link() link.topology = self._top link.dummy = True link.src_ports = item[SRC_TIER] # store tier name in ports link.dst_ports = item[DST_TIER] # store tier name in ports link.link_type = item[LINK_TYPE] link.num_links = item[NUM_LINKS] link.save() # need tier names in link object while returning link.src_tier = link.src_ports link.dst_tier = link.dst_ports # add link to topology defaults self._top.defaults[LINKS].append(link) # new topology has no switches or links self._top.switches = list() self._top.links = list() return self._top
def post(self, request, format=None): serializer = TopologySerializer(data=request.data) me = RequestValidator(request.META) if serializer.is_valid(): topology_obj = Topology() topology_obj.user_id = me.user_is_exist().user_id topology_obj.name = request.data['name'] topology_obj.submit = request.data['submit'] topology_obj.topology_json = json.dumps(request.data['topology_json']) try: topology_obj.config_json = json.dumps(request.data['config_json']) except: topology_obj.config_json = json.dumps([]) topology_obj.save() serializer = TopologyGetSerializer(topology_obj) return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def clone_topology(topo_id, data, user=""): old_topology = Topology.objects.get(pk=topo_id) # create new fabric new_topology = Topology() new_topology.name = data[NAME] new_topology.model_name = old_topology.model_name new_topology.updated_by = user new_topology.is_fabric = False new_topology.save() # fabric defaults, switches & links objects new_topology.defaults = dict() new_topology.defaults[SWITCHES] = list() new_topology.defaults[LINKS] = list() new_topology.switches = list() new_topology.links = list() # map of topology switch id to fabric switch object # needed when creating fabric links switch_dict = dict() # duplicate all topology switches into fabric for switch in Switch.objects.filter(topology_id=old_topology.id).order_by(ID): # save id for later switch_id = switch.id switch.id = None switch.topology = new_topology switch.save() # store topology switch id to switch mapping switch_dict[switch_id] = switch if switch.dummy: new_topology.defaults[SWITCHES].append(switch) else: new_topology.switches.append(switch) # duplicate all topology links into fabric for link in Link.objects.filter(topology_id=old_topology.id): link.id = None link.topology = new_topology if link.src_switch: link.src_switch = switch_dict[link.src_switch.id] link.dst_switch = switch_dict[link.dst_switch.id] else: link.src_switch = None link.dst_switch = None link.save() if not link.dummy: new_topology.links.append(link) elif link.dummy and not link.src_switch: link.src_tier = link.src_ports link.dst_tier = link.dst_ports new_topology.defaults[LINKS].append(link) new_topology.save() return new_topology
class BaseTopology(object): _TIERS = list() _MANDATORY_TIERS = list() _ALLOWED_LINKS = list() _AUTO_LINKS = list() def __init__(self, top=None): self._top = top @staticmethod def create_object(model): if model == LEAF_SPINE: return LeafSpineTopology() raise IgniteException(ERR_INV_TOP_MODEL) @staticmethod def get_object(top_id, user=""): top = Topology.objects.get(pk=top_id) if user: top.updated_by = user top.save() if top.model_name == LEAF_SPINE: return LeafSpineTopology(top) raise IgniteException(ERR_INV_TOP_MODEL) @staticmethod def get_all_topologies(submit): if submit == TRUE: return Topology.objects.filter(is_fabric=False, submit=True).order_by(NAME) elif submit == FALSE: return Topology.objects.filter(is_fabric=False, submit=False).order_by(NAME) else: return Topology.objects.filter(is_fabric=False).order_by(NAME) def add_topology(self, data, user): logger.debug("topology name = %s, model = %s", data[NAME], data[MODEL_NAME]) self._validate_defaults(data[DEFAULTS]) self._top = Topology() self._top.name = data[NAME] self._top.model_name = data[MODEL_NAME] self._top.is_fabric = False self._top.updated_by = user self._top.save() # defaults object self._top.defaults = dict() self._top.defaults[SWITCHES] = list() self._top.defaults[LINKS] = list() # add topology default switches with dummy=True for item in data[DEFAULTS][SWITCHES]: if item[MODEL] == 1: raise IgniteException(ERR_CAN_NOT_ASSIGN_UNKOWN_MODEL) switch = Switch() switch.topology = self._top switch.dummy = True switch.name = DEFAULT switch.tier = item[TIER] switch.model = get_switch(item[MODEL]) switch.image_profile = get_profile(item[IMAGE_PROFILE]) switch.save() # add switch to topology defaults self._top.defaults[SWITCHES].append(switch) # add topology default links with dummy=True for item in data[DEFAULTS][LINKS]: link = Link() link.topology = self._top link.dummy = True link.src_ports = item[SRC_TIER] # store tier name in ports link.dst_ports = item[DST_TIER] # store tier name in ports link.link_type = item[LINK_TYPE] link.num_links = item[NUM_LINKS] link.save() # need tier names in link object while returning link.src_tier = link.src_ports link.dst_tier = link.dst_ports # add link to topology defaults self._top.defaults[LINKS].append(link) # new topology has no switches or links self._top.switches = list() self._top.links = list() return self._top def _validate_defaults(self, data): # TODO: validate defaults pass @staticmethod def get_topology(top_id): # get topology, its switches and links top = Topology.objects.get(pk=top_id) top.switches = Switch.objects.filter(topology_id=top_id, dummy=False).order_by(ID) top.links = Link.objects.filter(topology_id=top_id, dummy=False) # fill default switches & links in topology defaults top.defaults = dict() top.defaults[SWITCHES] = Switch.objects.filter(topology_id=top_id, dummy=True).order_by(ID) top.defaults[LINKS] = list(Link.objects.filter(topology_id=top_id, src_switch=None)) for link in top.defaults[LINKS]: # need tier names in link object while returning link.src_tier = link.src_ports link.dst_tier = link.dst_ports return top @staticmethod def get_topology_detail(top_id): # get topology, its switches and links top = Topology.objects.get(pk=top_id) obj = dict() obj[SWITCHES] = list() obj[LINKS] = list() obj[ID] = top.id obj[NAME] = top.name for switch in Switch.objects.filter(topology_id=top_id, dummy=False): sw_obj = dict() sw_obj[ID] = switch.id sw_obj[NAME] = switch.name sw_obj[TIER] = switch.tier if switch.feature_profile: sw_obj[FEATURE_PROFILE] = switch.feature_profile.id else: default = Switch.objects.get(topology_id=top_id, tier=switch.tier, dummy=True) if default.feature_profile: sw_obj[FEATURE_PROFILE] = default.feature_profile.id else: sw_obj[FEATURE_PROFILE] = None obj[SWITCHES].append(sw_obj) for link in Link.objects.filter(topology_id=top_id, dummy=False): link_obj = dict() link_obj[ID] = link.id link_obj[SRC_SWITCH] = link.src_switch.id link_obj[DST_SWITCH] = link.dst_switch.id link_obj[LINK_TYPE] = link.link_type link_obj[NUM_LINKS] = link.num_links link_obj[SRC_PORTS] = link.src_ports link_obj[DST_PORTS] = link.dst_ports obj[LINKS].append(link_obj) return obj @staticmethod def update_topology_name(top_id, data, user): logger.debug("topology id = %d, name = %s", top_id, data[NAME]) Topology.objects.filter(pk=top_id).update(name=data[NAME], updated_by=user) @staticmethod def delete_topology(top_id): # check if any switch is booted if Switch.objects.filter(topology_id=top_id, boot_detail__boot_status__isnull=False).count(): raise IgniteException(ERR_CANT_DEL_BOOTED_FABRIC) # cleanup for switches which have boot detail for switch in Switch.objects.filter(topology_id=top_id, boot_detail__isnull=False): BaseTopology._delete_switch(switch) Topology.objects.get(pk=top_id).delete() def add_switches(self, data): for item in data[SWITCHES]: if not item[COUNT]: continue curr_count = Switch.objects.filter(topology_id=self._top.id, tier=item[TIER], dummy=False).count() if item[TIER] == SPINE: if curr_count + item[COUNT] > MAX_SPINES: raise IgniteException(ERR_SPINE_EXCEEDED_MAX_LIMIT) if item[TIER] == LEAF: if curr_count + item[COUNT] > MAX_LEAFS: raise IgniteException(ERR_LEAF_EXCEEDED_MAX_LIMIT) if item[TIER] == CORE: if curr_count + item[COUNT] > MAX_CORES: raise IgniteException(ERR_CORE_EXCEEDED_MAX_LIMIT) if item[TIER] == BORDER: if curr_count + item[COUNT] > MAX_BORDERS: raise IgniteException(ERR_BORDER_EXCEEDED_MAX_LIMIT) # get next switch index for tier index = self._get_next_index(item[TIER]) for ctr in range(item[COUNT]): self._add_switch(item[TIER], index + ctr) def _add_switch(self, tier, index=0): if not index: index = self._get_next_index(tier) # create new switch switch = Switch() switch.topology = self._top switch.dummy = False switch.tier = tier # get switch model from tier default default = Switch.objects.get(topology_id=self._top.id, tier=tier, dummy=True) switch.model = default.model # new switch name = fabric prefix (if any) + tier + index if self._top.is_fabric: switch.name = self._top.name + "_" + tier + str(index) else: switch.name = tier + str(index) switch.save() self._add_auto_links(switch) def _get_next_index(self, tier): # get all switches in tier switches = Switch.objects.filter(topology_id=self._top.id, tier=tier, dummy=False) # match with tier name followed by number like Leaf23 or Spine57 regex = r".*" + tier + "([1-9][0-9]*)$" index = 0 # search for max index in tier name for switch in switches: match = re.match(regex, switch.name) if match: index = max(index, int(match.group(1))) logger.debug("tier = %s, next index = %d", tier, index + 1) return index + 1 def update_switch(self, switch_id, data): switch = Switch.objects.get(pk=switch_id) if data[MODEL] == 1: raise IgniteException(ERR_CAN_NOT_ASSIGN_UNKOWN_MODEL) # TODO: check if model is valid, currently UI perform this check new_model = get_switch(data[MODEL]) # is there a change in model? change = True if new_model != switch.model else False logger.debug("%schange in switch model", "no " if not change else "") # save new values switch.model = new_model switch.image_profile = get_profile(data[IMAGE_PROFILE]) if data[IMAGE_PROFILE] else None switch.save() if change: self.update_model(switch) def delete_switch(self, switch_id): switch = Switch.objects.get(pk=switch_id) if switch.boot_detail: raise IgniteException(ERR_CANT_DEL_PROGRESS_SWITCH) BaseTopology._delete_switch(switch) def decommission_switch(self, switch_id): switch = Switch.objects.get(pk=switch_id) if switch.boot_detail and switch.boot_detail.boot_status == BOOT_PROGRESS: raise IgniteException(ERR_CANT_DEL_PROGRESS_SWITCH) BaseTopology._delete_switch(switch) @staticmethod def _delete_switch(switch): # delete switch and boot detail if any boot_detail = switch.boot_detail switch_id = switch.id try: clear_switch_config(switch_id) except Exception as e: logger.error(e) try: switch.delete() except ProtectedError: raise IgniteException(ERR_SW_IN_USE) if boot_detail: boot_detail.delete() # delete build files if any try: os.remove(os.path.join(REPO_PATH, str(switch_id) + ".cfg")) except OSError: pass try: os.remove(os.path.join(REPO_PATH, str(switch_id) + ".yml")) except OSError: pass def add_link(self, data): src_switch = Switch.objects.get(pk=data[SRC_SWITCH]) dst_switch = Switch.objects.get(pk=data[DST_SWITCH]) self._add_link(src_switch, dst_switch, data[LINK_TYPE], data[NUM_LINKS]) def _add_link(self, src_switch, dst_switch, link_type, num_links, src_ports=[], dst_ports=[]): # src_role is always DOWNLINK # dst_role is DOWNLINK for VPC links else UPLINK src_role = DOWNLINK dst_role = DOWNLINK if link_type in [VPC_PEER, VPC_MEMBER] else UPLINK # get ports on both switches src_ports = self._get_ports(src_switch, src_role, num_links, src_ports) dst_ports = self._get_ports(dst_switch, dst_role, num_links, dst_ports) logger.debug("src_ports = %s", src_ports) logger.debug("dst_ports = %s", dst_ports) # add new link self._add_link_to_db(link_type, num_links, src_switch, dst_switch, src_ports, dst_ports) def _get_ports(self, switch, port_role, num_ports, ports=[]): if switch.model_id == 1 or switch.model_id == None: raise IgniteException("Cant not add switch as no switch model assigned") logger.debug("switch id = %d, port_role = %s", switch.id, port_role) # get all links for switch links = Link.objects.filter( Q(src_switch_id=switch.id) | Q(dst_switch_id=switch.id), topology_id=self._top.id, dummy=False ) used_ports = list() # make list of ports currently in use for link in links: if switch.id == link.src_switch.id: used_ports += string_to_ports(link.src_ports) else: used_ports += string_to_ports(link.dst_ports) # make list of ports available for port_role in switch model # first ports with exact port_role and then role=BOTH # available ports = model ports - used ports model_ports = switch.model.meta[port_role] + switch.model.meta[BOTH] avail_ports = [port for port in model_ports if port not in used_ports] logger.debug("model_ports = %s", ports_to_string(model_ports)) logger.debug("used_ports = %s", ports_to_string(used_ports)) logger.debug("avail_ports = %s", ports_to_string(avail_ports)) # if ports are given, check that they are usable if ports: for port in ports: if port not in avail_ports: raise IgniteException(ERR_INV_PORTS) return ports if num_ports > len(avail_ports): raise IgniteException(ERR_NOT_ENOUGH_PORTS) return avail_ports[:num_ports] def _add_link_to_db(self, link_type, num_links, src_switch, dst_switch, src_ports, dst_ports): # create new link link = Link() link.topology = self._top link.dummy = False link.link_type = link_type link.num_links = num_links link.src_switch = src_switch link.dst_switch = dst_switch link.src_ports = ports_to_string(src_ports) link.dst_ports = ports_to_string(dst_ports) link.save() # create invidual physical links for matching with CDP data if num_links > 1 or link_type in [PORT_CHANNEL, VPC_PEER]: # set dummy = True for such links for index in range(num_links): link = Link() link.topology = self._top link.dummy = True link.link_type = PHYSICAL link.num_links = 1 link.src_switch = src_switch link.dst_switch = dst_switch link.src_ports = src_ports[index] link.dst_ports = dst_ports[index] link.save() def update_link(self, link_id, data): src_ports = string_to_ports(data[SRC_PORTS]) dst_ports = string_to_ports(data[DST_PORTS]) # check that correct # of ports were given if len(src_ports) != data[NUM_LINKS] or len(dst_ports) != data[NUM_LINKS]: raise IgniteException(ERR_INV_PORT_COUNT) # delete current link link = Link.objects.get(pk=link_id) src_switch = link.src_switch dst_switch = link.dst_switch self.delete_link(link_id, True) # add new link with new values self._add_link(src_switch, dst_switch, data[LINK_TYPE], data[NUM_LINKS], src_ports, dst_ports) def _update_link_in_db(self, link, new_ports, is_src): old_src_ports = string_to_ports(link.src_ports) old_dst_ports = string_to_ports(link.dst_ports) # update ports in link if is_src: link.src_ports = ports_to_string(new_ports) else: link.dst_ports = ports_to_string(new_ports) link.save() if link.num_links > 1 or link.link_type in [PORT_CHANNEL, VPC_PEER]: # get member physical links for index in range(link.num_links): member = Link.objects.get( src_switch_id=link.src_switch.id, dst_switch_id=link.dst_switch.id, src_ports=old_src_ports[index], dst_ports=old_dst_ports[index], dummy=True, ) # update port if is_src: member.src_ports = new_ports[index] else: member.dst_ports = new_ports[index] member.save() index += 1 def delete_link(self, link_id): link = Link.objects.get(pk=link_id) # delete physical links created for a logical link if link.num_links > 1 or link.link_type in [PORT_CHANNEL, VPC_PEER]: src_ports = string_to_ports(link.src_ports) dst_ports = string_to_ports(link.dst_ports) for index in range(link.num_links): Link.objects.filter( src_switch_id=link.src_switch.id, dst_switch_id=link.dst_switch.id, src_ports=src_ports[index], dst_ports=dst_ports[index], ).delete() # delete the logical link link.delete() @staticmethod def set_submit(top_id, data, user): Topology.objects.filter(pk=top_id).update(submit=data, updated_by=user) @staticmethod def clear_topology(top_id, user): # delete all switches in topology except defaults # links get automatically deleted Switch.objects.filter(topology_id=top_id, dummy=False).delete() Topology.objects.filter(pk=top_id).update(updated_by=user) def update_defaults(self, data): self._validate_defaults(data) # update default switch attributes for item in data[SWITCHES]: try: switch = Switch.objects.get(topology_id=self._top.id, dummy=True, tier=item[TIER]) except Switch.DoesNotExist: # if tier default does not exist, create one switch = Switch() switch.topology = self._top switch.dummy = True switch.name = DEFAULT switch.tier = item[TIER] switch.model = get_switch(item[MODEL]) switch.image_profile = get_profile(item[IMAGE_PROFILE]) switch.save() # update default link attributes for item in data[LINKS]: link = Link.objects.get( topology_id=self._top.id, dummy=True, src_ports=item[SRC_TIER], dst_ports=item[DST_TIER] ) link.link_type = item[LINK_TYPE] link.num_links = item[NUM_LINKS] link.save()
def post(self, request, format=None): serializer = TopologySerializer(data=request.data) me = RequestValidator(request.META) if serializer.is_valid(): topology_obj = Topology() topology_obj.user_id = me.user_is_exist().user_id topology_obj.name = request.data['name'] topology_obj.submit = request.data['submit'] topology_obj.topology_json = json.dumps( request.data['topology_json']) try: topology_obj.config_json = json.dumps( request.data['config_json']) except: topology_obj.config_json = json.dumps([]) try: topology_obj.defaults = json.dumps(request.data['defaults']) except: topology_obj.defaults = json.dumps({}) topology_obj.save() serializer = TopologyGetSerializer(topology_obj) return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def clone_topology(topo_id, data, user=""): old_topology = Topology.objects.get(pk=topo_id) # create new fabric new_topology = Topology() new_topology.name = data[NAME] new_topology.model_name = old_topology.model_name new_topology.updated_by = user new_topology.is_fabric = False new_topology.save() # fabric defaults, switches & links objects new_topology.defaults = dict() new_topology.defaults[SWITCHES] = list() new_topology.defaults[LINKS] = list() new_topology.switches = list() new_topology.links = list() # map of topology switch id to fabric switch object # needed when creating fabric links switch_dict = dict() # duplicate all topology switches into fabric for switch in Switch.objects.filter( topology_id=old_topology.id).order_by(ID): # save id for later switch_id = switch.id switch.id = None switch.topology = new_topology switch.save() # store topology switch id to switch mapping switch_dict[switch_id] = switch if switch.dummy: new_topology.defaults[SWITCHES].append(switch) else: new_topology.switches.append(switch) # duplicate all topology links into fabric for link in Link.objects.filter(topology_id=old_topology.id): link.id = None link.topology = new_topology if link.src_switch: link.src_switch = switch_dict[link.src_switch.id] link.dst_switch = switch_dict[link.dst_switch.id] else: link.src_switch = None link.dst_switch = None link.save() if not link.dummy: new_topology.links.append(link) elif link.dummy and not link.src_switch: link.src_tier = link.src_ports link.dst_tier = link.dst_ports new_topology.defaults[LINKS].append(link) new_topology.save() return new_topology
class BaseTopology(object): _TIERS = list() _MANDATORY_TIERS = list() _ALLOWED_LINKS = list() _AUTO_LINKS = list() def __init__(self, top=None): self._top = top @staticmethod def create_object(model): if model == LEAF_SPINE: return LeafSpineTopology() raise IgniteException(ERR_INV_TOP_MODEL) @staticmethod def get_object(top_id, user=""): top = Topology.objects.get(pk=top_id) if user: top.updated_by = user top.save() if top.model_name == LEAF_SPINE: return LeafSpineTopology(top) raise IgniteException(ERR_INV_TOP_MODEL) @staticmethod def get_all_topologies(submit): if submit == TRUE: return Topology.objects.filter(is_fabric=False, submit=True).order_by(NAME) elif submit == FALSE: return Topology.objects.filter(is_fabric=False, submit=False).order_by(NAME) else: return Topology.objects.filter(is_fabric=False).order_by(NAME) def add_topology(self, data, user): logger.debug("topology name = %s, model = %s", data[NAME], data[MODEL_NAME]) self._validate_defaults(data[DEFAULTS]) self._top = Topology() self._top.name = data[NAME] self._top.model_name = data[MODEL_NAME] self._top.is_fabric = False self._top.updated_by = user self._top.save() # defaults object self._top.defaults = dict() self._top.defaults[SWITCHES] = list() self._top.defaults[LINKS] = list() # add topology default switches with dummy=True for item in data[DEFAULTS][SWITCHES]: if item[MODEL] == 1: raise IgniteException(ERR_CAN_NOT_ASSIGN_UNKOWN_MODEL) switch = Switch() switch.topology = self._top switch.dummy = True switch.name = DEFAULT switch.tier = item[TIER] switch.model = get_switch(item[MODEL]) switch.image_profile = get_profile(item[IMAGE_PROFILE]) switch.save() # add switch to topology defaults self._top.defaults[SWITCHES].append(switch) # add topology default links with dummy=True for item in data[DEFAULTS][LINKS]: link = Link() link.topology = self._top link.dummy = True link.src_ports = item[SRC_TIER] # store tier name in ports link.dst_ports = item[DST_TIER] # store tier name in ports link.link_type = item[LINK_TYPE] link.num_links = item[NUM_LINKS] link.save() # need tier names in link object while returning link.src_tier = link.src_ports link.dst_tier = link.dst_ports # add link to topology defaults self._top.defaults[LINKS].append(link) # new topology has no switches or links self._top.switches = list() self._top.links = list() return self._top def _validate_defaults(self, data): # TODO: validate defaults pass @staticmethod def get_topology(top_id): # get topology, its switches and links top = Topology.objects.get(pk=top_id) top.switches = Switch.objects.filter(topology_id=top_id, dummy=False).order_by(ID) top.links = Link.objects.filter(topology_id=top_id, dummy=False) # fill default switches & links in topology defaults top.defaults = dict() top.defaults[SWITCHES] = Switch.objects.filter(topology_id=top_id, dummy=True).order_by(ID) top.defaults[LINKS] = list( Link.objects.filter(topology_id=top_id, src_switch=None)) for link in top.defaults[LINKS]: # need tier names in link object while returning link.src_tier = link.src_ports link.dst_tier = link.dst_ports return top @staticmethod def get_topology_detail(top_id): # get topology, its switches and links top = Topology.objects.get(pk=top_id) obj = dict() obj[SWITCHES] = list() obj[LINKS] = list() obj[ID] = top.id obj[NAME] = top.name for switch in Switch.objects.filter(topology_id=top_id, dummy=False): sw_obj = dict() sw_obj[ID] = switch.id sw_obj[NAME] = switch.name sw_obj[TIER] = switch.tier if switch.feature_profile: sw_obj[FEATURE_PROFILE] = switch.feature_profile.id else: default = Switch.objects.get(topology_id=top_id, tier=switch.tier, dummy=True) if default.feature_profile: sw_obj[FEATURE_PROFILE] = default.feature_profile.id else: sw_obj[FEATURE_PROFILE] = None obj[SWITCHES].append(sw_obj) for link in Link.objects.filter(topology_id=top_id, dummy=False): link_obj = dict() link_obj[ID] = link.id link_obj[SRC_SWITCH] = link.src_switch.id link_obj[DST_SWITCH] = link.dst_switch.id link_obj[LINK_TYPE] = link.link_type link_obj[NUM_LINKS] = link.num_links link_obj[SRC_PORTS] = link.src_ports link_obj[DST_PORTS] = link.dst_ports obj[LINKS].append(link_obj) return obj @staticmethod def update_topology_name(top_id, data, user): logger.debug("topology id = %d, name = %s", top_id, data[NAME]) Topology.objects.filter(pk=top_id).update(name=data[NAME], updated_by=user) @staticmethod def delete_topology(top_id): # check if any switch is booted if (Switch.objects.filter( topology_id=top_id, boot_detail__boot_status__isnull=False).count()): raise IgniteException(ERR_CANT_DEL_BOOTED_FABRIC) # cleanup for switches which have boot detail for switch in Switch.objects.filter(topology_id=top_id, boot_detail__isnull=False): BaseTopology._delete_switch(switch) Topology.objects.get(pk=top_id).delete() def add_switches(self, data): for item in data[SWITCHES]: if not item[COUNT]: continue curr_count = Switch.objects.filter(topology_id=self._top.id, tier=item[TIER], dummy=False).count() if item[TIER] == SPINE: if curr_count + item[COUNT] > MAX_SPINES: raise IgniteException(ERR_SPINE_EXCEEDED_MAX_LIMIT) if item[TIER] == LEAF: if curr_count + item[COUNT] > MAX_LEAFS: raise IgniteException(ERR_LEAF_EXCEEDED_MAX_LIMIT) if item[TIER] == CORE: if curr_count + item[COUNT] > MAX_CORES: raise IgniteException(ERR_CORE_EXCEEDED_MAX_LIMIT) if item[TIER] == BORDER: if curr_count + item[COUNT] > MAX_BORDERS: raise IgniteException(ERR_BORDER_EXCEEDED_MAX_LIMIT) # get next switch index for tier index = self._get_next_index(item[TIER]) for ctr in range(item[COUNT]): self._add_switch(item[TIER], index + ctr) def _add_switch(self, tier, index=0): if not index: index = self._get_next_index(tier) # create new switch switch = Switch() switch.topology = self._top switch.dummy = False switch.tier = tier # get switch model from tier default default = Switch.objects.get(topology_id=self._top.id, tier=tier, dummy=True) switch.model = default.model # new switch name = fabric prefix (if any) + tier + index if self._top.is_fabric: switch.name = self._top.name + "_" + tier + str(index) else: switch.name = tier + str(index) switch.save() self._add_auto_links(switch) def _get_next_index(self, tier): # get all switches in tier switches = Switch.objects.filter(topology_id=self._top.id, tier=tier, dummy=False) # match with tier name followed by number like Leaf23 or Spine57 regex = r'.*' + tier + '([1-9][0-9]*)$' index = 0 # search for max index in tier name for switch in switches: match = re.match(regex, switch.name) if match: index = max(index, int(match.group(1))) logger.debug("tier = %s, next index = %d", tier, index + 1) return index + 1 def update_switch(self, switch_id, data): switch = Switch.objects.get(pk=switch_id) if data[MODEL] == 1: raise IgniteException(ERR_CAN_NOT_ASSIGN_UNKOWN_MODEL) # TODO: check if model is valid, currently UI perform this check new_model = get_switch(data[MODEL]) # is there a change in model? change = True if new_model != switch.model else False logger.debug("%schange in switch model", "no " if not change else "") # save new values switch.model = new_model switch.image_profile = (get_profile(data[IMAGE_PROFILE]) if data[IMAGE_PROFILE] else None) switch.save() if change: self.update_model(switch) def delete_switch(self, switch_id): switch = Switch.objects.get(pk=switch_id) if switch.boot_detail: raise IgniteException(ERR_CANT_DEL_PROGRESS_SWITCH) BaseTopology._delete_switch(switch) def decommission_switch(self, switch_id): switch = Switch.objects.get(pk=switch_id) if switch.boot_detail and switch.boot_detail.boot_status == BOOT_PROGRESS: raise IgniteException(ERR_CANT_DEL_PROGRESS_SWITCH) BaseTopology._delete_switch(switch) @staticmethod def _delete_switch(switch): # delete switch and boot detail if any boot_detail = switch.boot_detail switch_id = switch.id try: clear_switch_config(switch_id) except Exception as e: logger.error(e) try: switch.delete() except ProtectedError: raise IgniteException(ERR_SW_IN_USE) if boot_detail: boot_detail.delete() # delete build files if any try: os.remove(os.path.join(REPO_PATH, str(switch_id) + '.cfg')) except OSError: pass try: os.remove(os.path.join(REPO_PATH, str(switch_id) + '.yml')) except OSError: pass def add_link(self, data): src_switch = Switch.objects.get(pk=data[SRC_SWITCH]) dst_switch = Switch.objects.get(pk=data[DST_SWITCH]) self._add_link(src_switch, dst_switch, data[LINK_TYPE], data[NUM_LINKS]) def _add_link(self, src_switch, dst_switch, link_type, num_links, src_ports=[], dst_ports=[]): # src_role is always DOWNLINK # dst_role is DOWNLINK for VPC links else UPLINK src_role = DOWNLINK dst_role = (DOWNLINK if link_type in [VPC_PEER, VPC_MEMBER] else UPLINK) # get ports on both switches src_ports = self._get_ports(src_switch, src_role, num_links, src_ports) dst_ports = self._get_ports(dst_switch, dst_role, num_links, dst_ports) logger.debug("src_ports = %s", src_ports) logger.debug("dst_ports = %s", dst_ports) # add new link self._add_link_to_db(link_type, num_links, src_switch, dst_switch, src_ports, dst_ports) def _get_ports(self, switch, port_role, num_ports, ports=[]): if switch.model_id == 1 or switch.model_id == None: raise IgniteException( "Cant not add switch as no switch model assigned") logger.debug("switch id = %d, port_role = %s", switch.id, port_role) # get all links for switch links = Link.objects.filter(Q(src_switch_id=switch.id) | Q(dst_switch_id=switch.id), topology_id=self._top.id, dummy=False) used_ports = list() # make list of ports currently in use for link in links: if switch.id == link.src_switch.id: used_ports += string_to_ports(link.src_ports) else: used_ports += string_to_ports(link.dst_ports) # make list of ports available for port_role in switch model # first ports with exact port_role and then role=BOTH # available ports = model ports - used ports model_ports = switch.model.meta[port_role] + switch.model.meta[BOTH] avail_ports = [port for port in model_ports if port not in used_ports] logger.debug("model_ports = %s", ports_to_string(model_ports)) logger.debug("used_ports = %s", ports_to_string(used_ports)) logger.debug("avail_ports = %s", ports_to_string(avail_ports)) # if ports are given, check that they are usable if ports: for port in ports: if port not in avail_ports: raise IgniteException(ERR_INV_PORTS) return ports if num_ports > len(avail_ports): raise IgniteException(ERR_NOT_ENOUGH_PORTS) return avail_ports[:num_ports] def _add_link_to_db(self, link_type, num_links, src_switch, dst_switch, src_ports, dst_ports): # create new link link = Link() link.topology = self._top link.dummy = False link.link_type = link_type link.num_links = num_links link.src_switch = src_switch link.dst_switch = dst_switch link.src_ports = ports_to_string(src_ports) link.dst_ports = ports_to_string(dst_ports) link.save() # create invidual physical links for matching with CDP data if num_links > 1 or link_type in [PORT_CHANNEL, VPC_PEER]: # set dummy = True for such links for index in range(num_links): link = Link() link.topology = self._top link.dummy = True link.link_type = PHYSICAL link.num_links = 1 link.src_switch = src_switch link.dst_switch = dst_switch link.src_ports = src_ports[index] link.dst_ports = dst_ports[index] link.save() def update_link(self, link_id, data): src_ports = string_to_ports(data[SRC_PORTS]) dst_ports = string_to_ports(data[DST_PORTS]) # check that correct # of ports were given if (len(src_ports) != data[NUM_LINKS] or len(dst_ports) != data[NUM_LINKS]): raise IgniteException(ERR_INV_PORT_COUNT) # delete current link link = Link.objects.get(pk=link_id) src_switch = link.src_switch dst_switch = link.dst_switch self.delete_link(link_id, True) # add new link with new values self._add_link(src_switch, dst_switch, data[LINK_TYPE], data[NUM_LINKS], src_ports, dst_ports) def _update_link_in_db(self, link, new_ports, is_src): old_src_ports = string_to_ports(link.src_ports) old_dst_ports = string_to_ports(link.dst_ports) # update ports in link if is_src: link.src_ports = ports_to_string(new_ports) else: link.dst_ports = ports_to_string(new_ports) link.save() if link.num_links > 1 or link.link_type in [PORT_CHANNEL, VPC_PEER]: # get member physical links for index in range(link.num_links): member = Link.objects.get(src_switch_id=link.src_switch.id, dst_switch_id=link.dst_switch.id, src_ports=old_src_ports[index], dst_ports=old_dst_ports[index], dummy=True) # update port if is_src: member.src_ports = new_ports[index] else: member.dst_ports = new_ports[index] member.save() index += 1 def delete_link(self, link_id): link = Link.objects.get(pk=link_id) # delete physical links created for a logical link if link.num_links > 1 or link.link_type in [PORT_CHANNEL, VPC_PEER]: src_ports = string_to_ports(link.src_ports) dst_ports = string_to_ports(link.dst_ports) for index in range(link.num_links): Link.objects.filter(src_switch_id=link.src_switch.id, dst_switch_id=link.dst_switch.id, src_ports=src_ports[index], dst_ports=dst_ports[index]).delete() # delete the logical link link.delete() @staticmethod def set_submit(top_id, data, user): Topology.objects.filter(pk=top_id).update(submit=data, updated_by=user) @staticmethod def clear_topology(top_id, user): # delete all switches in topology except defaults # links get automatically deleted Switch.objects.filter(topology_id=top_id, dummy=False).delete() Topology.objects.filter(pk=top_id).update(updated_by=user) def update_defaults(self, data): self._validate_defaults(data) # update default switch attributes for item in data[SWITCHES]: try: switch = Switch.objects.get(topology_id=self._top.id, dummy=True, tier=item[TIER]) except Switch.DoesNotExist: # if tier default does not exist, create one switch = Switch() switch.topology = self._top switch.dummy = True switch.name = DEFAULT switch.tier = item[TIER] switch.model = get_switch(item[MODEL]) switch.image_profile = get_profile(item[IMAGE_PROFILE]) switch.save() # update default link attributes for item in data[LINKS]: link = Link.objects.get(topology_id=self._top.id, dummy=True, src_ports=item[SRC_TIER], dst_ports=item[DST_TIER]) link.link_type = item[LINK_TYPE] link.num_links = item[NUM_LINKS] link.save()
def create(self, the_parsed_topology_xml): """ Create a topology model here. """ # Instance a list of model.Component classes that are all the instanced items from parsed xml. x = the_parsed_topology_xml componentXMLNameToComponent = { } #Dictionary mapss XML names to processes component objects so redundant processing is avoided components = [] for comp_xml_path in x.get_comp_type_file_header_dict(): file_path = os.environ['BUILD_ROOT'] + '/' + comp_xml_path print file_path processedXML = XmlComponentParser.XmlComponentParser(file_path) comp_name = processedXML.get_component().get_name() componentXMLNameToComponent[comp_name] = processedXML for instance in x.get_instances(): if instance.get_type() not in componentXMLNameToComponent.keys(): PRINT.info( "Component XML file type {} was not specified in the topology XML. Please specify the path using <import_component_type> tags." .format(instance.get_type())) else: instance.set_component_object( componentXMLNameToComponent[instance.get_type()]) components.append( Component.Component(instance.get_namespace(), instance.get_name(), instance.get_type(), xml_filename=x.get_xml_filename(), kind2=instance.get_kind())) #print "Assembly name: " + x.get_name(), x.get_base_id(), x.get_base_id_window() #for component in components: # print component.get_name(), component.get_base_id(), component.get_base_id_window() if self.__generate_new_IDS: # Iterate over all the model.Component classes and... instance_name_base_id_list = self.__compute_base_ids( x.get_base_id(), x.get_base_id_window(), x.get_instances(), x.get_xml_filename()) else: instance_name_base_id_list = [] # Iterate over all the model.Component classes and then... # Iterate over all the connection sources and assigne output ports to each Component.. # For each output port you want to assign the connect comment, target component namem, target port and type... # (Requires adding to the model.Port class ether members or a memeber called of type TargetConnection) for component in components: port_obj_list = [] for connection in x.get_connections(): if component.get_name() == connection.get_source()[0]: port = Port.Port(connection.get_source()[1], connection.get_source()[2], None, None, comment=connection.get_comment(), xml_filename=x.get_xml_filename) port.set_source_num(connection.get_source()[3]) port.set_target_comp(connection.get_target()[0]) port.set_target_port(connection.get_target()[1]) port.set_target_type(connection.get_target()[2]) port.set_target_num(connection.get_target()[3]) if connection.get_source()[1].startswith("i"): port.set_direction("input") else: port.set_direction("output") if connection.get_target()[1].startswith("i"): port.set_target_direction("input") else: port.set_target_direction("output") port_obj_list.append(port) component.set_ports(port_obj_list) # Instance a Topology class and give it namespace, comment and list of components. the_topology = Topology.Topology(x.get_namespace(), x.get_comment(), components, x.get_name(), instance_name_base_id_list, x.get_prepend_instance_name()) the_topology.set_instance_header_dict( x.get_comp_type_file_header_dict()) return the_topology