예제 #1
0
class ChangePlanValidator:
    def __init__(self):
        self.instance_table = InstanceTable()
        self.model_table = ModelTable()
        self.rack_table = RackTable()
        self.user_table = UserTable()
        self.dc_table = DatacenterTable()
        self.cp_table = ChangePlanTable()
        self.cp_action_table = ChangePlanActionTable()
        self.instance_manager = InstanceManager()
        self.rack_height = 42

        self.cp_asset_set = set()
        self.decom_asset_set = set()
        self.no_pow_conflict = False

    def validate_action(self, cp_action, all_cp_actions):
        self.cp_asset_set = set()
        self.decom_asset_set = set()
        self.no_pow_conflict = False
        cp_validation_result = self._validate_change_plan(
            cp_action.change_plan_id)
        if cp_validation_result != Constants.API_SUCCESS:
            return cp_validation_result

        if cp_action.action == Constants.CREATE_KEY:
            return self._create_action_validate(cp_action, all_cp_actions)
        elif cp_action.action == Constants.UPDATE_KEY:
            return self._edit_action_validate(cp_action, all_cp_actions)
        elif cp_action.action == Constants.DECOMMISSION_KEY:
            return self._decom_action_validate(cp_action, all_cp_actions)
        else:
            return Constants.API_SUCCESS

    def _validate_change_plan(self, cp_id: int):
        change_plan = self.cp_table.get_change_plan(cp_id)
        if change_plan is None:
            return "Change plan actions must correspond to an existing change plan."

        if change_plan.executed:
            return "Cannot modify a change plan that has already been executed"

        return Constants.API_SUCCESS

    def _create_action_validate(self, cp_action, all_cp_actions):
        instance = self.instance_manager.make_instance(cp_action.new_record)
        datacenter = self.dc_table.get_datacenter(instance.datacenter_id)
        if datacenter is None:
            return "The datacenter does not exist. Please select a valid datacenter."

        input_validation_result = self._validate_inputs(instance, datacenter)
        if input_validation_result != Constants.API_SUCCESS:
            return input_validation_result

        model_template = self.model_table.get_model(instance.model_id)
        if model_template is None:
            return "The model does not exist."

        if (instance.mount_type !=
                Constants.BLADE_KEY) and (not datacenter.is_offline_storage):
            instance_bottom = int(instance.rack_position)
            instance_top = instance_bottom + int(model_template.height) - 1

            if instance_top > self.rack_height:
                return "The placement of the instance exceeds the height of the rack."
        else:
            instance_bottom = 0
            instance_top = 0

        new_asset_number = instance.asset_number
        new_hostname = instance.hostname
        pow_connections = set(instance.power_connections)

        prev_action_val_result = self._validate_vs_prev_actions(
            instance,
            all_cp_actions,
            cp_action,
            new_asset_number,
            new_hostname,
            pow_connections,
            instance_bottom,
            instance_top,
            datacenter,
        )
        if prev_action_val_result != Constants.API_SUCCESS:
            return prev_action_val_result

        common_val_result = self._common_validations(instance, cp_action,
                                                     instance_bottom,
                                                     instance_top, datacenter)
        if common_val_result != Constants.API_SUCCESS:
            return common_val_result

        if (not datacenter.is_offline_storage) and (instance.mount_type !=
                                                    Constants.BLADE_KEY):
            if not self.no_pow_conflict:
                rack = self.rack_table.get_rack(instance.rack_label,
                                                instance.datacenter_id)
                for p_connection in instance.power_connections:
                    char1 = p_connection[0].upper()
                    num = int(p_connection[1:])
                    if char1 == "L":
                        pdu_arr = rack.pdu_left
                    elif char1 == "R":
                        pdu_arr = rack.pdu_right
                    else:
                        return "Invalid power connection. Please specify left or right PDU."

                    if pdu_arr[num - 1] == 1:
                        return f"There is already an asset connected at PDU {char1}{num}. Please pick an empty PDU port."

            connection_validation_result = self._validate_connections(
                instance.network_connections,
                instance.hostname,
                cp_action,
                all_cp_actions,
            )
            if connection_validation_result != Constants.API_SUCCESS:
                return connection_validation_result

        return Constants.API_SUCCESS

    def _edit_action_validate(self, cp_action, all_cp_actions):
        instance = self.instance_manager.make_instance(cp_action.new_record)
        datacenter = self.dc_table.get_datacenter(instance.datacenter_id)
        if datacenter is None:
            return "The datacenter does not exist. Please select a valid datacenter."

        input_validation_result = self._validate_inputs(instance, datacenter)
        if input_validation_result != Constants.API_SUCCESS:
            return input_validation_result

        model_template = self.model_table.get_model(instance.model_id)
        if model_template is None:
            return "The model does not exist."

        if (instance.mount_type !=
                Constants.BLADE_KEY) and (not datacenter.is_offline_storage):
            instance_bottom = int(instance.rack_position)
            instance_top = instance_bottom + int(model_template.height) - 1

            if instance_top > self.rack_height:
                return "The placement of the instance exceeds the height of the rack."
        else:
            instance_bottom = 0
            instance_top = 0

        prev_update_in_plan = self.cp_action_table.get_newest_asset_record_in_plan(
            cp_action.change_plan_id,
            cp_action.original_asset_number,
            999999999,
        )
        if (prev_update_in_plan is not None
                and prev_update_in_plan.action != Constants.COLLATERAL_KEY
                and prev_update_in_plan.step != cp_action.step):
            return f"This asset is already being modified in step {prev_update_in_plan.step}. Please update your desired information there."

        new_asset_number = instance.asset_number
        new_hostname = instance.hostname
        pow_connections = set(instance.power_connections)

        prev_action_val_result = self._validate_vs_prev_actions(
            instance,
            all_cp_actions,
            cp_action,
            new_asset_number,
            new_hostname,
            pow_connections,
            instance_bottom,
            instance_top,
            datacenter,
        )
        if prev_action_val_result != Constants.API_SUCCESS:
            return prev_action_val_result

        common_val_result = self._common_validations(instance, cp_action,
                                                     instance_bottom,
                                                     instance_top, datacenter)
        if common_val_result != Constants.API_SUCCESS:
            return common_val_result

        if (not datacenter.is_offline_storage) and (instance.mount_type !=
                                                    Constants.BLADE_KEY):
            if prev_update_in_plan is not None:
                prev_connections = prev_update_in_plan.new_record.get(
                    Constants.NETWORK_CONNECTIONS_KEY)
            else:
                prev_instance = self.instance_table.get_instance_by_asset_number(
                    cp_action.original_asset_number)
                prev_connections = prev_instance.network_connections

            connection_validation_result = self._validate_connections_edit(
                instance.network_connections,
                instance.hostname,
                cp_action,
                all_cp_actions,
                prev_connections,
            )
            if connection_validation_result != Constants.API_SUCCESS:
                return connection_validation_result

        return Constants.API_SUCCESS

    def _decom_action_validate(self, cp_action, all_cp_actions):
        asset = self.instance_table.get_instance_by_asset_number(
            cp_action.original_asset_number)
        if asset is None:
            return f"Asset with asset number {cp_action.original_asset_number} does not exist."

        prev_update_in_plan = self.cp_action_table.get_newest_asset_record_in_plan(
            cp_action.change_plan_id,
            cp_action.original_asset_number,
            999999999,
        )
        if (prev_update_in_plan is not None
                and prev_update_in_plan.action != Constants.COLLATERAL_KEY
                and prev_update_in_plan.step != cp_action.step):
            return f"This asset is already being modified in step {prev_update_in_plan.step}. Please update your desired information there."

        if asset.mount_type == Constants.CHASIS_KEY:
            no_conflict = set()
            for a in all_cp_actions:
                if a.new_record.get(
                        Constants.CHASSIS_HOSTNAME_KEY) == asset.hostname:
                    return f"A blade chassis must be empty before it can be decommissioned. A blade is placed in the chassis in step {a.step} of the change plan."
                else:
                    no_conflict.add(a.original_asset_number)

            blade_list = self.instance_table.get_blades_by_chassis_hostname(
                asset.hostname)
            if blade_list is not None:
                for blade in blade_list:
                    if not blade.asset_number in no_conflict:
                        return f"A blade chassis must be empty before it can be decommissioned. Blade with asset number {blade.asset_number} is in the chassis."

        return Constants.API_SUCCESS

    def _validate_inputs(self, instance, datacenter):
        if (not datacenter.is_offline_storage) and (instance.mount_type !=
                                                    Constants.BLADE_KEY):
            rack = self.rack_table.get_rack(instance.rack_label,
                                            instance.datacenter_id)
            if rack is None:
                return f"Rack {instance.rack_label} does not exist in datacenter {datacenter.name}. Assets must be created on preexisting racks"

            rack_u_pattern = re.compile("[0-9]+")
            if rack_u_pattern.fullmatch(str(instance.rack_position)) is None:
                return "The value for Rack U must be a positive integer."

        asset_pattern = re.compile("[0-9]{6}")
        if asset_pattern.fullmatch(str(instance.asset_number)) is None:
            return "Asset numbers must be 6 digits long and only contain numbers."

        if instance.hostname is not None and instance.hostname != "":
            if len(instance.hostname) > 64:
                return "Hostnames must be 64 characters or less"

            host_pattern = re.compile("[a-zA-Z]*[A-Za-z0-9-]*[A-Za-z0-9]")
            if host_pattern.fullmatch(instance.hostname) is None:
                return "Hostnames must start with a letter, only contain letters, numbers, periods, and hyphens, and end with a letter or number."

        if instance.owner != "" and self.user_table.get_user(
                instance.owner) is None:
            return f"The owner {instance.owner} is not an existing user. Please enter the username of an existing user."

        return Constants.API_SUCCESS

    def _validate_vs_prev_actions(
        self,
        instance,
        all_cp_actions,
        cp_action,
        new_asset_number,
        new_hostname,
        pow_connections,
        instance_bottom: int,
        instance_top: int,
        datacenter,
    ):
        for prev_action in all_cp_actions:
            if prev_action.step >= cp_action.step:
                continue

            if prev_action.action != Constants.CREATE_KEY:
                self.cp_asset_set.add(prev_action.original_asset_number)
            if prev_action.action == Constants.DECOMMISSION_KEY:
                self.decom_asset_set.add(prev_action.original_asset_number)

            # Asset Number Validation
            if (prev_action.original_asset_number == new_asset_number
                    and prev_action.action != Constants.DECOMMISSION_KEY):
                return f"Asset numbers must be unique. An asset with asset number {instance.asset_number} already exists."

            if (prev_action.new_record.get(
                    Constants.ASSET_NUMBER_KEY) == new_asset_number
                    and prev_action.action == Constants.CREATE_KEY):
                return f"Asset with asset number {new_asset_number} is already being created in step {prev_action.step} of the change plan."

            # Hostname Validation
            if new_hostname is not None and new_hostname != "":
                if (prev_action.new_record.get(
                        Constants.HOSTNAME_KEY) == new_hostname
                        and prev_action.action != Constants.DECOMMISSION_KEY):
                    return f"Asset with hostname {new_hostname} already exists in step {prev_action.step} of the change plan."

            # Location Validation
            if not datacenter.is_offline_storage:
                if instance.mount_type != Constants.BLADE_KEY:
                    if (prev_action.action == Constants.CREATE_KEY
                            or prev_action.action == Constants.UPDATE_KEY):
                        model_id = self.instance_manager.get_model_id_from_name(
                            prev_action.new_record.get(Constants.MODEL_KEY))
                        model = self.model_table.get_model(model_id)
                        other_bottom = int(
                            prev_action.new_record.get(
                                Constants.RACK_POSITION_KEY))
                        other_top = (int(
                            prev_action.new_record.get(
                                Constants.RACK_POSITION_KEY)) + model.height -
                                     1)
                        if (other_bottom >= instance_bottom
                                and other_bottom <= instance_top):
                            result = f"The asset placement conflicts with asset with asset number {prev_action.new_record.get(Constants.ASSET_NUMBER_KEY)} "
                            result += f"edited in step {prev_action.step}."
                            return result
                        elif other_top >= instance_bottom and other_top <= instance_top:
                            result = f"The asset placement conflicts with asset with asset number {prev_action.new_record.get(Constants.ASSET_NUMBER_KEY)} "
                            result += f"edited in step {prev_action.step}."
                            return result
                elif (instance.mount_type == Constants.BLADE_KEY
                      and prev_action.new_record.get(
                          Constants.MOUNT_TYPE_KEY) == Constants.BLADE_KEY):
                    if instance.chassis_hostname == prev_action.new_record.get(
                            Constants.CHASSIS_HOSTNAME_KEY
                    ) and instance.chassis_slot == prev_action.new_record.get(
                            Constants.CHASSIS_SLOT_KEY):
                        return f"A blade is already being placed in chassis with hostname {instance.chassis_hostname} at position {instance.chassis_slot} in step {prev_action.step} of the change plan."

            # Power Connection Validation
            if (not datacenter.is_offline_storage) and (instance.mount_type !=
                                                        Constants.BLADE_KEY):
                if (prev_action.action != Constants.DECOMMISSION_KEY
                        and prev_action.action != Constants.COLLATERAL_KEY):
                    prev_pow_connections = prev_action.new_record.get(
                        Constants.POWER_CONNECTIONS_KEY)
                    print(prev_action.new_record)
                    prev_pow_connections = set(prev_pow_connections)
                    pow_intersection = pow_connections.intersection(
                        prev_pow_connections)
                    if len(pow_intersection) > 0:
                        return f"There is already an asset connected at PDU port {pow_intersection.pop()}. Please pick an empty PDU port."

                if (prev_action.action == Constants.UPDATE_KEY
                        or prev_action.action == Constants.DECOMMISSION_KEY):
                    old_pow_connections = prev_action.old_record.get(
                        Constants.POWER_CONNECTIONS_KEY)
                    if old_pow_connections is None:
                        self.no_pow_conflict = True
                    else:
                        old_pow_connections = set(old_pow_connections)
                        old_pow_intersection = pow_connections.intersection(
                            old_pow_connections)
                        if len(old_pow_intersection) > 0:
                            self.no_pow_conflict = True

        return Constants.API_SUCCESS

    def _common_validations(self, instance, cp_action, instance_bottom,
                            instance_top, datacenter):
        if (self.instance_table.get_instance_by_asset_number(
                instance.asset_number) is not None
                and not instance.asset_number in self.decom_asset_set
                and instance.asset_number != cp_action.original_asset_number):
            return f"Asset numbers must be unique. An asset with asset number {instance.asset_number} already exists."

        if instance.hostname != "" and instance.hostname is not None:
            duplicate_hostname = self.instance_table.get_instance_by_hostname(
                instance.hostname)

            if (duplicate_hostname is not None and
                    not duplicate_hostname.asset_number in self.cp_asset_set
                    and
                    duplicate_hostname.asset_number != instance.asset_number):
                return f"An asset with hostname {duplicate_hostname.hostname} exists at location {duplicate_hostname.rack_label} U{duplicate_hostname.rack_position}"

        if not datacenter.is_offline_storage:
            if instance.mount_type != Constants.BLADE_KEY:
                instance_list = self.instance_table.get_instances_by_rack(
                    instance.rack_label, instance.datacenter_id)
                if instance_list is not None:
                    for current_instance in instance_list:
                        if (current_instance.asset_number in self.cp_asset_set
                                or current_instance.asset_number
                                == cp_action.original_asset_number):
                            continue

                        model = self.model_table.get_model(instance.model_id)
                        current_instance_top = (
                            current_instance.rack_position + model.height - 1)
                        if (current_instance.rack_position >= instance_bottom
                                and current_instance.rack_position <=
                                instance_top):
                            return self.return_conflict(current_instance)
                        elif (current_instance_top >= instance_bottom
                              and current_instance_top <= instance_top):
                            return self.return_conflict(current_instance)
            elif instance.mount_type == Constants.BLADE_KEY:
                blade_conflict = self.instance_table.get_blade_by_chassis_and_slot(
                    instance.chassis_hostname, instance.chassis_slot)
                if (blade_conflict is not None and
                        not blade_conflict.asset_number in self.cp_asset_set):
                    return f"A blade with hostname {blade_conflict.hostname} is already located in position {instance.chassis_slot} of chassis with hostname {instance.chassis_hostname}."

        return Constants.API_SUCCESS

    def _validate_connections(self, network_connections, hostname, cp_action,
                              all_cp_actions):
        result = ""
        new_connections = {}
        for my_port in network_connections:
            mac_adress = network_connections[my_port][
                Constants.MAC_ADDRESS_KEY]
            connection_hostname = network_connections[my_port][
                "connection_hostname"]
            connection_port = network_connections[my_port]["connection_port"]

            if connection_hostname in new_connections.keys():
                if new_connections[connection_hostname] == connection_port:
                    result += "Cannot make two network connections to the same port."
            elif connection_hostname != "" and connection_port != "":
                new_connections[connection_hostname] = connection_port

            mac_pattern = re.compile(
                "[a-f0-9]{2}:[a-f0-9]{2}:[a-f0-9]{2}:[a-f0-9]{2}:[a-f0-9]{2}:[a-f0-9]{2}"
            )
            if mac_adress != "" and mac_pattern.fullmatch(
                    mac_adress.lower()) is None:
                result += f"Invalid MAC address for port {my_port}. MAC addresses must be 6 byte, colon separated hexidecimal strings (i.e. a1:b2:c3:d4:e5:f6). \n"

            if (connection_hostname == "" or connection_hostname is None) and (
                    connection_port == "" or connection_port is None):
                continue

            if not (connection_hostname != "" and connection_port != ""):
                result += "Connections require both a hostname and connection port."

            other_instance = None
            for prev_action in all_cp_actions:
                if prev_action.step >= cp_action.step:
                    continue

                if (prev_action.new_record[Constants.HOSTNAME_KEY] ==
                        connection_hostname):
                    other_instance = self.instance_manager.make_instance(
                        prev_action.new_record)
            if other_instance is None:
                other_instance = self.instance_table.get_instance_by_hostname(
                    connection_hostname)
                if other_instance is None:
                    result += f"The asset with hostname {connection_hostname} does not exist. Connections must be between assets with existing hostnames. \n"
                    continue

            if connection_port in other_instance.network_connections:
                if (other_instance.network_connections[connection_port]
                    ["connection_port"] != my_port) and (
                        other_instance.network_connections[connection_port]
                        ["connection_port"] != ""):
                    result += f"The port {connection_port} on asset with hostname {connection_hostname} is connected to another asset. \n"
                    continue
                if (other_instance.network_connections[connection_port]
                    ["connection_hostname"] != hostname) and (
                        other_instance.network_connections[connection_port]
                        ["connection_hostname"] != ""):
                    result += f"The port {connection_port} on asset with hostname {connection_hostname} is already connected to another asset."

        if result == "":
            return Constants.API_SUCCESS
        else:
            return result

    def _validate_connections_edit(self, network_connections, hostname,
                                   cp_action, all_cp_actions,
                                   prev_connections):
        result = ""
        new_connections = {}
        for my_port in network_connections:
            mac_adress = network_connections[my_port][
                Constants.MAC_ADDRESS_KEY]
            connection_hostname = network_connections[my_port][
                "connection_hostname"]
            connection_port = network_connections[my_port]["connection_port"]

            if connection_hostname in new_connections.keys():
                if new_connections[connection_hostname] == connection_port:
                    result += "Cannot make two network connections to the same port."
            elif connection_hostname != "" and connection_port != "":
                new_connections[connection_hostname] = connection_port

            mac_pattern = re.compile(
                "[a-f0-9]{2}:[a-f0-9]{2}:[a-f0-9]{2}:[a-f0-9]{2}:[a-f0-9]{2}:[a-f0-9]{2}"
            )
            if mac_adress != "" and mac_pattern.fullmatch(
                    mac_adress.lower()) is None:
                result += f"Invalid MAC address for port {my_port}. MAC addresses must be 6 byte, colon separated hexidecimal strings (i.e. a1:b2:c3:d4:e5:f6). \n"

            if (connection_hostname == "" or connection_hostname is None) and (
                    connection_port == "" or connection_port is None):
                continue

            if (connection_hostname
                    == prev_connections[my_port]["connection_hostname"]
                    and connection_port
                    == prev_connections[my_port]["connection_port"]):
                continue

            if not (connection_hostname != "" and connection_port != ""):
                result += "Connections require both a hostname and connection port."

            other_instance = None
            for prev_action in all_cp_actions:
                if prev_action.step >= cp_action.step:
                    continue

                if (prev_action.new_record[Constants.HOSTNAME_KEY] ==
                        connection_hostname):
                    other_instance = self.instance_manager.make_instance(
                        prev_action.new_record)
            if other_instance is None:
                other_instance = self.instance_table.get_instance_by_hostname(
                    connection_hostname)
                if other_instance is None:
                    result += f"The asset with hostname {connection_hostname} does not exist. Connections must be between assets with existing hostnames. \n"
                    continue

            if connection_port in other_instance.network_connections:
                if (other_instance.network_connections[connection_port]
                    ["connection_port"] != my_port) and (
                        other_instance.network_connections[connection_port]
                        ["connection_port"] != ""):
                    result += f"The port {connection_port} on asset with hostname {connection_hostname} is connected to another asset. \n"
                    continue
                if (other_instance.network_connections[connection_port]
                    ["connection_hostname"] != hostname) and (
                        other_instance.network_connections[connection_port]
                        ["connection_hostname"] != ""):
                    result += f"The port {connection_port} on asset with hostname {connection_hostname} is already connected to another asset."

        if result == "":
            return Constants.API_SUCCESS
        else:
            return result

    def return_conflict(self, current_instance):
        result = f"The asset placement conflicts with asset with asset number {current_instance.asset_number} "
        result += f"on rack {current_instance.rack_label} at height U{current_instance.rack_position}."
        return result
예제 #2
0
class InstanceManager:
    def __init__(self):
        self.table = InstanceTable()
        self.model_table = ModelTable()
        self.dc_table = DatacenterTable()
        self.rack_table = RackTable()
        self.validate = InstanceValidator()
        self.asset_num_generator = AssetNumGenerator()

    def create_instance(self, instance_data):
        print("INSTANCE DATA")
        print(instance_data)
        try:
            try:
                new_instance = self.make_instance(instance_data)
                if type(new_instance) is InvalidInputsError:
                    return new_instance
            except InvalidInputsError as e:
                return e.message
            create_validation_result = Constants.API_SUCCESS
            try:
                create_validation_result = self.validate.create_instance_validation(
                    new_instance)
                if create_validation_result != Constants.API_SUCCESS:
                    raise InvalidInputsError(create_validation_result)
            except InvalidInputsError as e:
                raise InvalidInputsError(e.message)

            # try:
            is_in_offline_storage = (DatacenterTable().get_datacenter_by_name(
                instance_data.get(Constants.DC_NAME_KEY)).is_offline_storage)
            self.table.add_instance(new_instance)
            if (new_instance.mount_type != Constants.BLADE_KEY
                    and not is_in_offline_storage):
                power_result = self.add_power_connections(new_instance)
                print("POWER RESULT")
                print(power_result)
                if power_result != Constants.API_SUCCESS:
                    self.table.delete_instance_by_asset_number(
                        new_instance.asset_number)
                    raise InvalidInputsError(
                        "An error occurred when trying to add power connections."
                    )

                connect_result = self.make_corresponding_connections(
                    new_instance.network_connections, new_instance.hostname)
                if connect_result != Constants.API_SUCCESS:
                    self.table.delete_instance_by_asset_number(
                        new_instance.asset_number)
                    raise InvalidInputsError(connect_result)
            # except:
            #     raise InvalidInputsError("Unable to create asset")
        except InvalidInputsError as e:
            print(e.message)
            raise InvalidInputsError(e.message)
        # except Exception as e:
        #     print(str(e))
        #     raise InvalidInputsError(
        #         "An error occurred when attempting to create the asset."
        #     )

    def delete_instance(self, instance_data):
        asset_number = self.check_null(
            instance_data[Constants.ASSET_NUMBER_KEY])

        if asset_number == "":
            raise InvalidInputsError("Must provide an asset number")

        asset = self.table.get_instance_by_asset_number(asset_number)
        if asset is None:
            raise InvalidInputsError(
                "The asset you are trying to delete was not found.")

        if asset.mount_type == Constants.CHASIS_KEY:
            blade_list = self.table.get_blades_by_chassis_hostname(
                asset.hostname)
            if blade_list is not None:
                raise InvalidInputsError(
                    "A blade chassis must be empty before it can be decommissioned or deleted."
                )

        if asset.mount_type != Constants.BLADE_KEY:
            delete_power_result = self.delete_power_connections(asset)
            print(delete_power_result)
            if delete_power_result != Constants.API_SUCCESS:
                raise InvalidInputsError(
                    "An error occurred when trying to remove power connections."
                )

            delete_connection_result = self.delete_connections(asset)
            if delete_connection_result != Constants.API_SUCCESS:
                raise InvalidInputsError(delete_connection_result)

        try:
            self.table.delete_instance_by_asset_number(asset_number)
        except:
            raise InvalidInputsError(
                "An error occurred when trying to delete the specified asset.")

    def detail_view(self, instance_data):
        print(instance_data)
        asset_number = instance_data.get(Constants.ASSET_NUMBER_KEY)

        try:
            print("Get these things")
            print(asset_number)
            instance = self.table.get_instance_by_asset_number(asset_number)
            return instance
        except:
            raise InvalidInputsError(
                "An error occured while retrieving data for this asset.")

    def edit_instance(self, instance_data):
        print("INSTANCE DATA")
        print(instance_data)
        try:
            original_asset_number = instance_data.get(
                Constants.ASSET_NUMBER_ORIG_KEY)
            if original_asset_number is None:
                raise InvalidInputsError("Unable to find the asset to edit.")

            original_asset = self.table.get_instance_by_asset_number(
                original_asset_number)
            if original_asset is None:
                raise InvalidInputsError("Could not find asset to update.")

            new_instance = self.make_instance(instance_data)
            if type(new_instance) is InvalidInputsError:
                return new_instance

            self.delete_power_connections(original_asset)

            delete_connection_result = self.delete_connections(original_asset)
            if delete_connection_result != Constants.API_SUCCESS:
                raise InvalidInputsError(
                    "Failed to update network connections.")

            edit_validation_result = self.validate.edit_instance_validation(
                new_instance, original_asset_number)
            if edit_validation_result != Constants.API_SUCCESS:
                original_instance = self.table.get_instance_by_asset_number(
                    original_asset_number)
                self.make_corresponding_connections(
                    original_instance.network_connections,
                    original_instance.hostname)
                raise InvalidInputsError(edit_validation_result)
        except InvalidInputsError as e:
            raise InvalidInputsError(e.message)

        self.add_power_connections(new_instance)

        edit_connection_result = self.make_corresponding_connections(
            new_instance.network_connections, new_instance.hostname)
        if edit_connection_result != Constants.API_SUCCESS:
            original_instance = self.table.get_instance_by_asset_number(
                original_asset_number)
            self.make_corresponding_connections(
                original_instance.network_connections,
                original_instance.hostname)
            raise InvalidInputsError(edit_connection_result)

        self.table.edit_instance(new_instance, original_asset_number)

        if new_instance.mount_type == Constants.CHASIS_KEY and (
                original_asset.hostname != new_instance.hostname
                or original_asset.datacenter_id != new_instance.datacenter_id):
            blade_list = self.table.get_blades_by_chassis_hostname(
                original_asset.hostname)
            if blade_list is not None:
                for blade in blade_list:
                    blade.chassis_hostname = new_instance.hostname
                    blade.datacenter_id = new_instance.datacenter_id
                    self.table.edit_instance(blade, blade.asset_number)

    def get_instances(self, filter, dc_name, limit: int):
        model_name = filter.get(Constants.MODEL_KEY)
        try:
            if model_name is not None and model_name != "":
                print("MODEL_NAME")
                print(model_name)
                model_id = self.get_model_id_from_name(model_name)
            else:
                model_id = None
        except:
            raise InvalidInputsError(
                "An error occurred while trying to filter by model name. Please input a different model name"
            )

        try:
            if dc_name is not None:
                dc_id = self.get_datacenter_id_from_name(dc_name)
                if dc_id == -1:
                    dc_id = None
        except:
            raise InvalidInputsError(
                "An error occurred while trying to filter by datacenter name. Please input a different model name"
            )

        hostname = filter.get(Constants.HOSTNAME_KEY)
        rack_label = filter.get(Constants.RACK_KEY)
        rack_position = filter.get(Constants.RACK_POSITION_KEY)

        try:
            instance_list = self.table.get_instances_with_filters(
                model_id=model_id,
                hostname=hostname,
                rack_label=rack_label,
                rack_position=rack_position,
                datacenter_id=dc_id,
                limit=limit,
            )
            return instance_list
        except:
            raise InvalidInputsError(
                "An error occurred while trying to retrieve instance data.")

    def get_possible_models_with_filters(self, prefix_json):
        try:
            return_list = []

            model_list = self.model_table.get_all_models()
            for model in model_list:
                model_name = model.vendor + " " + model.model_number
                # if model_name.startswith(prefix):
                return_list.append(model_name)

            return return_list
        except:
            raise InvalidInputsError(
                "An error occurred while trying to retrieve model options.")

    def make_instance(self, instance_data):
        model_name = self.check_null(instance_data[Constants.MODEL_KEY])
        model_id = self.get_model_id_from_name(model_name)
        model = self.get_model_from_id(model_id)

        mount_type = model.mount_type

        datacenter_name = self.check_null(instance_data[Constants.DC_NAME_KEY])
        datacenter_id = self.get_datacenter_id_from_name(datacenter_name)
        try:
            hostname = self.check_null(instance_data[Constants.HOSTNAME_KEY])
            rack = self.check_null(instance_data[Constants.RACK_KEY].upper())
            rack_position = self.check_null(
                instance_data[Constants.RACK_POSITION_KEY])
            owner = self.check_null(instance_data[Constants.OWNER_KEY])
            comment = self.check_null(instance_data[Constants.COMMENT_KEY])
            network_connections = self.check_null(
                instance_data[Constants.NETWORK_CONNECTIONS_KEY])
            power_connections = self.check_null(
                instance_data[Constants.POWER_CONNECTIONS_KEY])
            asset_number = self.check_null(
                instance_data[Constants.ASSET_NUMBER_KEY])
        except:
            raise InvalidInputsError(
                "Could not read data fields correctly. Client-server error occurred."
            )

        display_color = self.asset_or_model_val(
            instance_data.get(Constants.DISPLAY_COLOR_KEY),
            model.display_color)
        cpu = self.asset_or_model_val(instance_data.get(Constants.CPU_KEY),
                                      model.cpu)
        self.asset_or_model_val(instance_data.get(Constants.CPU_KEY),
                                model.cpu)
        memory = self.asset_or_model_val(
            instance_data.get(Constants.MEMORY_KEY), model.memory)
        storage = self.asset_or_model_val(
            instance_data.get(Constants.STORAGE_KEY), model.storage)

        if mount_type == Constants.BLADE_KEY:
            chassis_hostname = instance_data.get(
                Constants.CHASSIS_HOSTNAME_KEY)
            chassis_slot = instance_data.get(Constants.CHASSIS_SLOT_KEY)
        else:
            chassis_hostname = ""
            chassis_slot = -1

        # if rack == "":
        #     return InvalidInputsError("Must provide a rack location")
        # if rack_position == "":
        #     return InvalidInputsError("Must provide a rack location")
        if asset_number == "":
            return InvalidInputsError("Must provide an asset number")

        return Instance(
            model_id,
            hostname,
            rack,
            rack_position,
            owner,
            comment,
            datacenter_id,
            network_connections,
            power_connections,
            asset_number,
            mount_type,
            display_color,
            cpu,
            memory,
            storage,
            chassis_hostname,
            chassis_slot,
        )

    def get_model_id_from_name(self, model_name):
        try:
            model_list = self.model_table.get_all_models()
            for model in model_list:
                if model.vendor + " " + model.model_number == model_name:
                    print("FOUND MATCH")
                    model_id = self.model_table.get_model_id_by_vendor_number(
                        model.vendor, model.model_number)
                    if model_id is None:
                        model_id = -1

                    return model_id
            return -1
        except:
            raise InvalidInputsError(
                "An error occurred while trying to retrieve model info corresponding to the instance."
            )

    def get_datacenter_id_from_name(self, datacenter_name):
        try:
            datacenter_id = self.dc_table.get_datacenter_id_by_name(
                datacenter_name)
            if datacenter_id is None:
                return -1
            return datacenter_id
        except:
            raise InvalidInputsError(
                "An error occurred while trying to retrieve datacenter info corresponding to the instance."
            )

    def get_model_from_id(self, model_id):
        model = self.model_table.get_model(model_id)
        if model is None:
            raise InvalidInputsError(
                "An error occurred while trying to retrieve model info corresponding to the instance."
            )

        return model

    def get_dc_from_id(self, dc_id):
        datacenter = self.dc_table.get_datacenter(dc_id)
        if datacenter is None:
            raise InvalidInputsError(
                "An error occurred while trying to retrieve datacenter info corresponding to the instance."
            )

        return datacenter

    def make_corresponding_connections(self, network_connections, hostname):
        for port in network_connections:
            connection_hostname = network_connections[port][
                "connection_hostname"]
            connection_port = network_connections[port]["connection_port"]

            if (connection_hostname == "" or connection_hostname is None) and (
                    connection_port == "" or connection_port is None):
                continue

            # print("SEARCH " + connection_hostname)
            other_instance = self.table.get_instance_by_hostname(
                connection_hostname)
            print("COMPLETE")
            if other_instance is None:
                return f"An error occurred when attempting to add the network connection. Could not find asset with hostname {connection_hostname}."

            other_instance.network_connections[connection_port][
                "connection_hostname"] = hostname
            other_instance.network_connections[connection_port][
                "connection_port"] = port
            print(other_instance.network_connections)

            try:
                print("EDITIG")
                self.table.edit_instance(other_instance,
                                         other_instance.asset_number)
                print("EDITED SUCCESS")
            except:
                return f"Could not add new network connections to asset with hostname {other_instance.hostname}."

        return Constants.API_SUCCESS

    def delete_connections(self, asset):
        if asset is None:
            return "Failed to find the asset to delete"

        for port in asset.network_connections:
            connection_hostname = asset.network_connections[port][
                "connection_hostname"]
            connection_port = asset.network_connections[port][
                "connection_port"]

            if (connection_hostname == "" or connection_hostname is None) and (
                    connection_port == "" or connection_port is None):
                continue

            other_instance = self.table.get_instance_by_hostname(
                connection_hostname)
            if other_instance is None:
                return f"An error occurred when attempting to delete the network connection. Could not find asset with hostname {connection_hostname}."

            other_instance.network_connections[connection_port][
                "connection_hostname"] = ""
            other_instance.network_connections[connection_port][
                "connection_port"] = ""
            print(other_instance.network_connections)

            try:
                print("EDITIG")
                self.table.edit_instance(other_instance,
                                         other_instance.asset_number)
                print("EDITED SUCCESS")
            except:
                return f"Could not add new network connections to asset with hostname {other_instance.hostname}."

        return Constants.API_SUCCESS

    def add_power_connections(self, instance):
        rack = self.rack_table.get_rack(instance.rack_label,
                                        instance.datacenter_id)
        if rack is None:
            return f"Could not find rack {instance.rack_label}"

        for p_connection in instance.power_connections:
            char1 = p_connection[0].upper()
            num = int(p_connection[1:])
            if char1 == "L":
                rack.pdu_left[num - 1] = 1
            elif char1 == "R":
                rack.pdu_right[num - 1] = 1
            else:
                return "Invalid power connection. Please specify left or right PDU."

        self.rack_table.edit_rack(rack)
        return Constants.API_SUCCESS

    def delete_power_connections(self, instance):
        if instance is None:
            return "Asset could not be found."

        rack = self.rack_table.get_rack(instance.rack_label,
                                        instance.datacenter_id)
        if rack is None:
            return f"Could not find rack {instance.rack_label}"

        for p_connection in instance.power_connections:
            char1 = p_connection[0].upper()
            num = int(p_connection[1:])
            if char1 == "L":
                rack.pdu_left[num - 1] = 0
            elif char1 == "R":
                rack.pdu_right[num - 1] = 0
            else:
                return "Invalid power connection. Please specify left or right PDU."

        self.rack_table.edit_rack(rack)
        return Constants.API_SUCCESS

    def get_network_neighborhood(self, asset_number):
        if asset_number is None or asset_number == "":
            raise InvalidInputsError("No asset number found in the request.")

        asset = self.table.get_instance_by_asset_number(asset_number)
        if asset is None:
            raise InvalidInputsError("The asset requested could not be found.")

        connections_dict = {}

        if asset.mount_type == Constants.CHASIS_KEY:
            blade_list = self.table.get_blades_by_chassis_hostname(
                asset.hostname)
            if blade_list is not None and len(blade_list) != 0:
                for blade in blade_list:
                    connections_dict[blade.hostname] = []

        is_blade = asset.mount_type == Constants.BLADE_KEY
        if is_blade:
            connected_asset = self.table.get_instance_by_hostname(
                asset.chassis_hostname)
            if connected_asset is None:
                raise InvalidInputsError(
                    f"Connection to asset with hostname {asset.chassis_hostname} was not found."
                )
            two_deep_list = self.make_two_deep_list(connected_asset)
            connections_dict[connected_asset.hostname] = two_deep_list
        else:
            for port in asset.network_connections:
                hostname = asset.network_connections[port][
                    "connection_hostname"]
                if hostname is None or hostname == "":
                    continue
                connected_asset = self.table.get_instance_by_hostname(hostname)
                if connected_asset is None:
                    raise InvalidInputsError(
                        f"Connection to asset with hostname {hostname} was not found."
                    )
                two_deep_list = self.make_two_deep_list(connected_asset)
                connections_dict[hostname] = two_deep_list

        print(connections_dict)
        return connections_dict

    def make_two_deep_list(self, connected_asset):
        two_deep_list = []
        if connected_asset.mount_type == Constants.CHASIS_KEY:
            blade_list = self.table.get_blades_by_chassis_hostname(
                connected_asset.hostname)
            if blade_list is not None and len(blade_list) != 0:
                for blade in blade_list:
                    two_deep_list.append(blade.hostname)

        for port2 in connected_asset.network_connections:
            host2 = connected_asset.network_connections[port2][
                "connection_hostname"]
            two_deep_list.append(host2)

        return two_deep_list

    def get_all_chassis(self):
        try:
            chassis_list = self.table.get_asset_by_mount_type(
                Constants.CHASIS_KEY)
            if chassis_list is None:
                chassis_list = []
            return chassis_list
        except:
            raise InvalidInputsError(
                "An error occurred while trying to retrieve blade chassis.")

    def get_blades_in_chassis(self, asset_data):
        try:
            chassis_hostname = asset_data.get(Constants.CHASSIS_HOSTNAME_KEY)
            if chassis_hostname is None or chassis_hostname == "":
                raise InvalidInputsError(
                    "Must provide a valid blade chassis hostname.")
            blade_list = self.table.get_blades_by_chassis_hostname(
                chassis_hostname)
            if blade_list is None:
                return []
            return blade_list
        except InvalidInputsError as e:
            raise InvalidInputsError(e.message)
        except:
            raise InvalidInputsError(
                "An error occurred while trying to retrieve blade chassis.")

    def check_null(self, val):
        if val is None:
            return ""
        else:
            return val

    def asset_or_model_val(self, instance_val, model_val):
        if self.check_null(instance_val) != "":
            return instance_val
        else:
            return model_val
예제 #3
0
class InstanceValidator:
    def __init__(self):
        self.instance_table = InstanceTable()
        self.model_table = ModelTable()
        self.rack_table = RackTable()
        self.user_table = UserTable()
        self.dc_table = DatacenterTable()
        self.rack_height = 42

    def create_instance_validation(self, instance, queue=None):
        basic_val_result = self.basic_validations(instance, -1)
        if basic_val_result != Constants.API_SUCCESS:
            return basic_val_result

        model_template = self.model_table.get_model(instance.model_id)
        if model_template is None:
            return "The model does not exist."

        dc_template = self.dc_table.get_datacenter(instance.datacenter_id)
        if dc_template is None:
            return "The datacenter does not exist."

        # Check that connections are only within a single datacenter
        net_cons = instance.network_connections
        for port in net_cons.keys():
            dest_hostname = net_cons[port][Constants.CONNECTION_HOSTNAME]
            if dest_hostname != "" and dest_hostname is not None:
                dc_id = (InstanceTable().get_instance_by_hostname(
                    dest_hostname).datacenter_id)
                if dc_id != instance.datacenter_id:
                    raise InvalidInputsError(
                        "Network connections cannot span multiple datacenters")

        if dc_template.is_offline_storage:
            return Constants.API_SUCCESS

        if instance.mount_type == Constants.BLADE_KEY:
            return self.blade_validation(instance, -1, queue)
        else:
            return self.rackmount_validation(instance, -1, model_template,
                                             dc_template)

    def edit_instance_validation(self, instance, original_asset_number):
        basic_val_result = self.basic_validations(instance,
                                                  original_asset_number)
        if basic_val_result != Constants.API_SUCCESS:
            return basic_val_result

        model_template = self.model_table.get_model(instance.model_id)
        if model_template is None:
            return "The model does not exist."

        dc_template = self.dc_table.get_datacenter(instance.datacenter_id)
        if dc_template is None:
            return "The datacenter does not exist."

        # Check that connections are only within a single datacenter
        net_cons = instance.network_connections
        for port in net_cons.keys():
            dest_hostname = net_cons[port][Constants.CONNECTION_HOSTNAME]
            if dest_hostname != "" and dest_hostname is not None:
                dc_id = (InstanceTable().get_instance_by_hostname(
                    dest_hostname).datacenter_id)
                if dc_id != instance.datacenter_id:
                    raise InvalidInputsError(
                        "Network connections cannot span multiple datacenters")

        if dc_template.is_offline_storage:
            return Constants.API_SUCCESS

        if instance.mount_type == Constants.BLADE_KEY:
            return self.blade_validation(instance, original_asset_number)
        else:
            return self.rackmount_validation(instance, original_asset_number,
                                             model_template, dc_template)

        return Constants.API_SUCCESS

    def basic_validations(self, instance, original_asset_number):
        if (instance.asset_number != original_asset_number
                and original_asset_number != -1):
            asset_pattern = re.compile("[0-9]{6}")
            if asset_pattern.fullmatch(str(instance.asset_number)) is None:
                return "Asset numbers must be 6 digits long and only contain numbers."
            if (self.instance_table.get_instance_by_asset_number(
                    instance.asset_number) is not None):
                return f"Asset numbers must be unique. An asset with asset number {instance.asset_number} already exists."

        if instance.hostname != "" and instance.hostname is not None:
            duplicate_hostname = self.instance_table.get_instance_by_hostname(
                instance.hostname)

            if duplicate_hostname is not None:
                if duplicate_hostname.asset_number != original_asset_number:
                    print("DUPLICATE NUMBER", duplicate_hostname.asset_number)
                    print("ORIGINAL NUMBER", original_asset_number)
                    return f"An asset with hostname {duplicate_hostname.hostname} already exists. Please provide a unique hostname."

            if len(instance.hostname) > 64:
                return "Hostnames must be 64 characters or less"

            host_pattern = re.compile("[a-zA-Z]*[A-Za-z0-9-]*[A-Za-z0-9]")
            if host_pattern.fullmatch(instance.hostname) is None:
                return "Hostnames must start with a letter, only contain letters, numbers, periods, and hyphens, and end with a letter or number."

        if instance.owner != "" and self.user_table.get_user(
                instance.owner) is None:
            return f"The owner {instance.owner} is not an existing user. Please enter the username of an existing user."

        return Constants.API_SUCCESS

    def rackmount_validation(self, instance, original_asset_number,
                             model_template, dc_template):
        if instance.mount_type == Constants.CHASIS_KEY and instance.hostname == "":
            if (self.instance_table.get_blades_by_chassis_hostname(
                    instance.hostname) is not None):
                return "Blade chassis with blades require a hostname."

        rack = self.rack_table.get_rack(instance.rack_label,
                                        instance.datacenter_id)
        if rack is None and not dc_template.is_offline_storage:
            return f"Rack {instance.rack_label} does not exist in datacenter {dc_template}. Assets must be created on preexisting racks"

        p_connection_set = set()
        for p_connection in instance.power_connections:
            if p_connection in p_connection_set:
                return "Cannot connect two asset power ports to the same PDU port."
            else:
                p_connection_set.add(p_connection)
            char1 = p_connection[0].upper()
            print("PRINGINDISNFISNF")
            print(p_connection)
            num = int(p_connection[1:])
            if char1 == "L":
                pdu_arr = rack.pdu_left
            elif char1 == "R":
                pdu_arr = rack.pdu_right
            else:
                return "Invalid power connection. Please specify left or right PDU."

            if pdu_arr[num - 1] == 1:
                return f"There is already an asset connected at PDU {char1}{num}. Please pick an empty PDU port."

        pattern = re.compile("[0-9]+")
        if pattern.fullmatch(str(instance.rack_position)) is None:
            return "The value for Rack U must be a positive integer."

        instance_bottom = int(instance.rack_position)
        instance_top = instance_bottom + int(model_template.height) - 1

        if instance_top > self.rack_height:
            return "The placement of the instance exceeds the height of the rack."

        instance_list = self.instance_table.get_instances_by_rack(
            instance.rack_label, instance.datacenter_id)
        if instance_list is not None:
            for current_instance in instance_list:
                if current_instance.asset_number == original_asset_number:
                    continue

                model = self.model_table.get_model(current_instance.model_id)
                current_instance_top = current_instance.rack_position + model.height - 1
                if (current_instance.rack_position >= instance_bottom
                        and current_instance.rack_position <= instance_top):
                    return self.return_conflict(current_instance)
                elif (current_instance_top >= instance_bottom
                      and current_instance_top <= instance_top):
                    return self.return_conflict(current_instance)

        connection_validation_result = self.validate_connections(
            instance.network_connections, instance.hostname)
        if connection_validation_result != Constants.API_SUCCESS:
            return connection_validation_result

        return Constants.API_SUCCESS

    def blade_validation(self, instance, original_asset_number, queue=None):
        blade_chassis = self.instance_table.get_instance_by_hostname(
            instance.chassis_hostname)

        print("\n\n")
        print("CHECKING THE QUEUE")
        #  In the case of import instance, need to check queue of instances as well as db
        if queue is not None and blade_chassis is None:
            for item in queue:
                print(item)
                if item.hostname == instance.chassis_hostname:
                    blade_chassis = item

        print("BLADE CHASSIS")
        print(blade_chassis)

        if blade_chassis is None:
            return f"No blade chassis exists with hostname {instance.chassis_hostname}."
        elif blade_chassis.mount_type != Constants.CHASIS_KEY:
            return f"Asset with hostname {instance.chassis_hostname} is not a blade chassis. Blades can only be installed in a blade chassis."

        if blade_chassis.datacenter_id != instance.datacenter_id:
            return f"The blade chassis selected is in datacenter {blade_chassis.datacenter_id}. A blade and its chassis must be in the same datacenter."

        if instance.chassis_slot > 14 or instance.chassis_slot < 1:
            return f"A blade chassis contains 14 slots. Please provide a position between 1 and 14."

        blade_conflict = self.instance_table.get_blade_by_chassis_and_slot(
            instance.chassis_hostname, instance.chassis_slot)
        if (blade_conflict is not None
                and blade_conflict.asset_number != original_asset_number):
            return f"Blade with asset number {blade_conflict.asset_number} already exists at slot {instance.chassis_slot} of chassis with hostname {instance.chassis_hostname}."

        return Constants.API_SUCCESS

    def validate_connections(self, network_connections, hostname):
        # print("validating connections")
        result = ""
        new_connections = {}
        for my_port in network_connections:
            mac_adress = network_connections[my_port][
                Constants.MAC_ADDRESS_KEY]
            connection_hostname = network_connections[my_port][
                "connection_hostname"]
            connection_port = network_connections[my_port]["connection_port"]

            if connection_hostname in new_connections.keys():
                if new_connections[connection_hostname] == connection_port:
                    result += "Cannot make two network connections to the same port."
            elif connection_hostname != "" and connection_port != "":
                new_connections[connection_hostname] = connection_port

            mac_pattern = re.compile(
                "[a-f0-9]{2}:[a-f0-9]{2}:[a-f0-9]{2}:[a-f0-9]{2}:[a-f0-9]{2}:[a-f0-9]{2}"
            )
            if mac_adress != "" and mac_pattern.fullmatch(
                    mac_adress.lower()) is None:
                result += f"Invalid MAC address for port {my_port}. MAC addresses must be 6 byte, colon separated hexidecimal strings (i.e. a1:b2:c3:d4:e5:f6). \n"

            if (connection_hostname == "" or connection_hostname is None) and (
                    connection_port == "" or connection_port is None):
                continue

            if not (connection_hostname != "" and connection_port != ""):
                result += "Connections require both a hostname and connection port."

            other_instance = self.instance_table.get_instance_by_hostname(
                connection_hostname)
            if other_instance is None:
                result += f"The asset with hostname {connection_hostname} does not exist. Connections must be between assets with existing hostnames. \n"
                continue

            if connection_port in other_instance.network_connections:
                if (other_instance.network_connections[connection_port]
                    ["connection_port"] != my_port) and (
                        other_instance.network_connections[connection_port]
                        ["connection_port"] != ""):
                    result += f"The port {connection_port} on asset with hostname {connection_hostname} is connected to another asset. \n"
                    continue
                if (other_instance.network_connections[connection_port]
                    ["connection_hostname"] != hostname) and (
                        other_instance.network_connections[connection_port]
                        ["connection_hostname"] != ""):
                    result += f"The port {connection_port} on asset with hostname {connection_hostname} is already connected to another asset. \n"

        # print("finished connection validation")
        if result == "":
            return Constants.API_SUCCESS
        else:
            return result

    def return_conflict(self, current_instance):
        result = f"The asset placement conflicts with asset with asset number {current_instance.asset_number} "
        result += f"on rack {current_instance.rack_label} at height U{current_instance.rack_position}."
        return result