Пример #1
0
    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
Пример #2
0
    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
Пример #3
0
 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)
Пример #4
0
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
Пример #5
0
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()
Пример #6
0
 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)
Пример #7
0
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
Пример #8
0
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()
Пример #9
0
    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