Example #1
0
 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
Example #2
0
 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 __init__(self):
        self.dc_table = DatacenterTable()
        self.instance_table = InstanceTable()
        self.model_table = ModelTable()
        self.rack_table = RackTable()

        self.space_by_rack = {}
        self.space_by_vendor = {}
        self.space_by_model = {}
        self.space_by_owner = {}

        self.rack_count = {}
        self.rack_height = 42
Example #4
0
    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
Example #5
0
 def __get_dc_name_from_asset_num(self, asset_number):
     print("asset number = ", asset_number)
     instance = InstanceTable().get_instance_by_asset_number(asset_number)
     datacenter_id = instance.datacenter_id
     datacenter_name = DatacenterTable().get_datacenter(datacenter_id)
     print("Datacenter name = ", datacenter_name.name)
     return datacenter_name.name
class PermissionsManager:
    def __init__(self):
        self.DCT = DatacenterTable()

    def get_permission_types(self):
        try:
            datacenters = self.DCT.get_all_datacenters()
        except DBWriteException as e:
            raise e
        except:
            raise DBWriteException("Could not get datacenters")

        datacenters_list = [dc.name for dc in datacenters]

        return {
            Constants.PERMISSIONS_KEY: [
                pc.MODEL,
                pc.ASSET,
                pc.POWER,
                pc.AUDIT,
                pc.ADMIN,
                pc.DATACENTERS,
            ],
            Constants.PERMISSIONS_DC_KEY:
            datacenters_list,
        }
Example #7
0
    def add_or_update(self, instance: Instance) -> Tuple[int, int, int]:
        """" Adds a model or updates it if it already exists """
        instance_entry: InstanceEntry = InstanceEntry(instance=instance)
        site = DatacenterTable().get_datacenter(instance.datacenter_id)
        # print(instance.network_connections)
        print(instance.make_json())

        try:
            # result: InstanceEntry = InstanceEntry.query.filter_by(
            #     rack_label=instance.rack_label,
            #     rack_position=instance.rack_position,
            #     datacenter_id=instance.datacenter_id,
            # ).first()

            result: InstanceEntry = InstanceEntry.query.filter_by(
                asset_number=instance.asset_number).first()

            add, update, ignore = False, False, False
            if result is not None:
                # print("SEARCH RESULT")
                # print(result.network_connections)
                # print("NEW THING")
                # print(instance.network_connections)
                # print("EQ")
                # print(result.network_connections == instance.network_connections)
                # print(result.make_instance() == instance)
                if result.make_instance() == instance:
                    ignore = True
                else:
                    # InstanceEntry.query.filter_by(
                    #     rack_label=instance.rack_label,
                    #     rack_position=instance.rack_position,
                    #     datacenter_id=instance.datacenter_id,
                    # ).update(instance_entry.make_json())
                    # update = True
                    # print("UPDATING")
                    # print(instance_entry.make_json())
                    InstanceEntry.query.filter_by(
                        asset_number=instance.asset_number).update(
                            instance_entry.make_json())
                    update = True
            else:
                # Add new instance to database, only if rack exists
                rack_result: RackEntry = RackEntry.query.filter_by(
                    label=instance.rack_label,
                    datacenter_id=instance.datacenter_id).first()
                if (rack_result is not None or site.is_offline_storage
                        or instance.mount_type == Constants.BLADE_KEY):
                    db.session.add(instance_entry)
                else:
                    raise RackDoesNotExistError(rack_label=instance.rack_label)
                add = True
            db.session.commit()

            return int(add), int(update), int(ignore)
        except RackDoesNotExistError:
            raise
Example #8
0
def create_racks():
    """ Create a range of racks """
    returnJSON = createJSON()
    data: JSON = request.get_json()
    # print(json.dumps(request.json, indent=4))
    print(request.get_json())

    try:
        print("datacenter")
        datacenter_name: str = data[Constants.DC_NAME_KEY]
        datacenter_id = get_datacenter_id_by_name(datacenter_name)
        datacenter = DatacenterTable().get_datacenter(datacenter_id)
        if datacenter.is_offline_storage:
            return addMessageToJSON(
                returnJSON, "Cannot add racks to offline storage center")

        print("range")
        start_letter: str = data[Constants.START_LETTER_KEY]
        stop_letter: str = data[Constants.STOP_LETTER_KEY]
        try:
            start_number: int = int(data[Constants.START_NUMBER_KEY])
            stop_number: int = int(data[Constants.STOP_NUMBER_KEY])
        except:
            return addMessageToJSON(
                returnJSON,
                "Rack designations contain letters preceding numbers and may only include letters and numbers",
            )

        print("adding")
        add_rack_range(
            start_letter=start_letter,
            stop_letter=stop_letter,
            start_number=start_number,
            stop_number=stop_number,
            datacenter_id=datacenter_id,
            datacenter_name=datacenter_name,
        )
        return addMessageToJSON(returnJSON, "success")
    except KeyError:
        return addMessageToJSON(returnJSON, "Unable to create racks.")
    except DBWriteException as e:
        return addMessageToJSON(returnJSON, e.message)
    except InvalidRangeError:
        return addMessageToJSON(
            returnJSON,
            "Invalid range of racks to add. Please make sure you provide a valid rack range.",
        )
Example #9
0
    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)
Example #10
0
def delete_racks():
    """ Delete a range of racks """
    data: JSON = request.get_json()
    returnJSON = createJSON()

    try:
        datacenter_name: str = data[Constants.DC_NAME_KEY]
        datacenter_id = get_datacenter_id_by_name(datacenter_name)
        datacenter = DatacenterTable().get_datacenter(datacenter_id)
        if datacenter.is_offline_storage:
            return addMessageToJSON(
                returnJSON, "Cannot delete racks from offline storage center")

        start_letter: str = data[Constants.START_LETTER_KEY]
        stop_letter: str = data[Constants.STOP_LETTER_KEY]
        start_number: int = int(data[Constants.START_NUMBER_KEY])
        stop_number: int = int(data[Constants.STOP_NUMBER_KEY])

        delete_rack_range(
            start_letter=start_letter,
            stop_letter=stop_letter,
            start_number=start_number,
            stop_number=stop_number,
            datacenter_id=datacenter_id,
            datacenter_name=datacenter_name,
        )
        return addMessageToJSON(returnJSON, "success")
    except (KeyError, DBWriteException):
        return addMessageToJSON(returnJSON, "Unable to delete rack.")
    except InvalidRangeError:
        return addMessageToJSON(
            returnJSON,
            "Invalid range of racks to add. Please make sure you provide a valid rack range.",
        )
    except RackNotEmptyError:
        return addMessageToJSON(
            returnJSON,
            "Cannot delete racks that are not empty. Delete all instances on the rack then delete the rack.",
        )
Example #11
0
class DatacenterValidator:
    def __init__(self):
        self.dc_table = DatacenterTable()
        self.rack_table = RackTable()

    def create_dc_validation(self, datacenter):
        if len(datacenter.abbreviation) > 6:
            return "The datacenter abbreviation must be 6 characters or less."

        abbrev_pattern = re.compile("[a-zA-Z]+[a-zA-Z0-9]*")
        if abbrev_pattern.fullmatch(datacenter.abbreviation) is None:
            return "Datacenter abbreviation must start with a letter and only contain letters and numbers."

        name_pattern = re.compile("[a-zA-Z]*[ a-zA-Z0-9-]*[A-Za-z0-9]")
        if name_pattern.fullmatch(datacenter.name) is None:
            return "Datacenter name must start with a letter and only contain letters, numbers, spaces, and hyphens and end in a letter or number."

        if (self.dc_table.get_datacenter_id_by_abbrev(datacenter.abbreviation)
                is not None):
            return "A datacenter with the given abbreviation already exists. Please choose a different abbreviation."

        if self.dc_table.get_datacenter_id_by_name(
                datacenter.name) is not None:
            return "A datacenter with the given name already exists. Please choose a different name."

        return Constants.API_SUCCESS

    def edit_dc_validation(self, datacenter, original_name):
        if len(datacenter.abbreviation) > 6:
            return "The datacenter abbreviation must be 6 characters or less."

        abbrev_pattern = re.compile("[a-zA-Z]+[a-zA-Z0-9]*")
        if abbrev_pattern.fullmatch(datacenter.abbreviation) is None:
            return "Datacenter abbreviation must start with a letter and only contain letters and numbers."

        name_pattern = re.compile("[a-zA-Z]*[ a-zA-Z0-9-]*[A-Za-z0-9]")
        if name_pattern.fullmatch(datacenter.name) is None:
            return "Datacenter name must start with a letter and only contain letters, numbers, spaces, and hyphens and end in a letter or number."

        orig_id = self.dc_table.get_datacenter_id_by_name(original_name)

        abbrev_id = self.dc_table.get_datacenter_id_by_abbrev(
            datacenter.abbreviation)
        if abbrev_id is not None and abbrev_id != orig_id:
            return "A datacenter with the given abbreviation already exists. Please choose a different abbreviation."

        name_id = self.dc_table.get_datacenter_id_by_name(datacenter.name)
        if name_id is not None and name_id != orig_id:
            return "A datacenter with the given name already exists. Please choose a different name."

        return Constants.API_SUCCESS

    def delete_dc_validation(self, dc_name):
        dc_id = self.dc_table.get_datacenter_id_by_name(dc_name)

        dc_racks = self.rack_table.get_rack_by_datacenter(dc_id)
        if dc_racks is not None and dc_racks != []:
            return (
                "All racks in the datacenter must be removed before it can be deleted."
            )

        return Constants.API_SUCCESS
Example #12
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
Example #13
0
from app.dal.instance_table import InstanceTable, RackDoesNotExistError
from app.dal.model_table import ModelTable
from app.data_models.instance import Instance
from app.data_models.model import Model
from app.instances.asset_num_generator import AssetNumGenerator
from app.instances.instance_manager import InstanceManager
from app.instances.instance_validator import InstanceValidator
from app.main.types import JSON
from app.models.model_manager import ModelManager
from app.models.model_validator import ModelValidator
from flask import Blueprint, request

import_export = Blueprint("import_export", __name__)

ASSETNUMGEN = AssetNumGenerator()
DCTABLE = DatacenterTable()
MODELTABLE = ModelTable()
# INSTANCETABLE = InstanceTable()


class FileNotFoundError(Exception):
    """
    Raised when a csv file is not included
    """


class TooFewInputsError(Exception):
    """
    Raised when too few inputs are provided in the csv
    """
 def __init__(self):
     self.DCT = DatacenterTable()
Example #15
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
Example #16
0
def get_datacenter_id_by_name(name):
    datacenter_id = DatacenterTable().get_datacenter_id_by_name(name)
    if datacenter_id is None:
        return -1
    return datacenter_id
class DatacenterManager:
    def __init__(self):
        self.dc_table = DatacenterTable()
        self.validate = DatacenterValidator()

    def get_all_datacenters(self):
        try:
            dc_list = self.dc_table.get_all_datacenters()
            return dc_list
        except:
            raise InvalidInputsError(
                "A failure occured while retrieving datacenter information.")

    def create_datacenter(self, dc_data):
        try:
            try:
                new_datacenter = self.make_datacenter(dc_data)
                if type(new_datacenter) is InvalidInputsError:
                    return new_datacenter
            except InvalidInputsError as e:
                return e.message

            try:
                create_validation_result = self.validate.create_dc_validation(
                    new_datacenter)
            except InvalidInputsError as e:
                return e.message
            if create_validation_result == Constants.API_SUCCESS:
                self.dc_table.add_datacenter(new_datacenter)
            else:
                return InvalidInputsError(create_validation_result)

        except:
            raise InvalidInputsError(
                "An error occurred when attempting to create the datacenter.")

    def edit_datacenter(self, dc_data):
        try:
            print("here1")
            try:
                original_name = self.check_null(
                    dc_data[Constants.NAME_ORIG_KEY])
                print("here2")
                updated_datacenter = self.make_datacenter(dc_data)
                print("here3")
                if type(updated_datacenter) is InvalidInputsError:
                    return updated_datacenter
                print("here4")
            except InvalidInputsError as e:
                return e.message

            try:
                print("here5")
                edit_validation_result = self.validate.edit_dc_validation(
                    updated_datacenter, original_name)
            except InvalidInputsError as e:
                return e.message
            print("here6")
            if edit_validation_result == Constants.API_SUCCESS:
                self.dc_table.edit_datacenter(updated_datacenter,
                                              original_name)
            else:
                return InvalidInputsError(edit_validation_result)
            print("here7")
        except:
            raise InvalidInputsError(
                "An error occurred when attempting to edit the datacenter.")

    def delete_datacenter(self, dc_data):
        try:
            dc_name = self.check_null(dc_data[Constants.DC_NAME_KEY])

            if dc_name == "":
                raise InvalidInputsError(
                    "Must provide a datacenter name to delete")

            try:
                delete_validation_result = self.validate.delete_dc_validation(
                    dc_name)
            except InvalidInputsError as e:
                return e.message
            if delete_validation_result == Constants.API_SUCCESS:
                self.dc_table.delete_datacenter_by_name(dc_name)
            else:
                return InvalidInputsError(delete_validation_result)
        except:
            raise InvalidInputsError(
                "An error occurred when trying to delete the specified asset.")

    def make_datacenter(self, dc_data):
        try:
            print("were1")
            abbreviation = self.check_null(
                dc_data[Constants.DC_ABRV_KEY]).upper()
            print("were2")
            name = self.check_null(dc_data[Constants.DC_NAME_KEY])
            print("were3")
            is_offline_storage = dc_data[Constants.DC_IS_OFFLINE_KEY]
            print("were4")
        except:
            raise InvalidInputsError(
                "Could not read data fields correctly. Client-server error occurred."
            )

        print("were5")
        if abbreviation == "":
            raise InvalidInputsError(
                "Must provide an abbreviation for the datacenter")
        print("were6")
        if name == "":
            raise InvalidInputsError("Must provide a datacenter name")
        print("were7")
        if type(is_offline_storage) != bool:
            raise InvalidInputsError(
                "Must provide a boolean value specifying whether or not the datacenter is an offline storage location"
            )

        return Datacenter(abbreviation, name, is_offline_storage)

    def check_null(self, val):
        if val is None:
            return ""
        else:
            return val
class StatsManager:
    def __init__(self):
        self.dc_table = DatacenterTable()
        self.instance_table = InstanceTable()
        self.model_table = ModelTable()
        self.rack_table = RackTable()

        self.space_by_rack = {}
        self.space_by_vendor = {}
        self.space_by_model = {}
        self.space_by_owner = {}

        self.rack_count = {}
        self.rack_height = 42

    def create_report(self, dc_name):
        self.reset_counters()

        if dc_name is None or dc_name == "":
            rack_list = self.rack_table.get_all_racks()
        else:
            dc_id = self.dc_table.get_datacenter_id_by_name(dc_name)
            rack_list = self.rack_table.get_rack_by_datacenter(dc_id)

        num_racks = len(rack_list)

        if num_racks == 0:
            raise ValueError(
                "Reports require existing racks. Please ensure that the datacenter exists and contains racks."
            )

        for rack in rack_list:
            instance_list = self.instance_table.get_instances_by_rack(
                rack.label, rack.datacenter_id)
            self.iterate_instance(instance_list, rack.label)

        total_space_used = 0
        for key in self.space_by_rack:
            total_space_used += self.space_by_rack[key]
            self.space_by_rack[key] = round(
                (self.space_by_rack[key] /
                 (self.rack_count[key] * self.rack_height)) * 100,
                2,
            )

        all_space = num_racks * self.rack_height
        percent_total_used = round((total_space_used / all_space) * 100, 2)
        percent_total_free = 100 - percent_total_used

        self.space_by_vendor = self.divide_dict_by_space(
            self.space_by_vendor, all_space)
        self.space_by_model = self.divide_dict_by_space(
            self.space_by_model, all_space)
        self.space_by_owner = self.divide_dict_by_space(
            self.space_by_owner, all_space)

        rack_positionsage_json = json.dumps(self.space_by_rack, sort_keys=True)
        vendor_usage_json = json.dumps(self.space_by_vendor, sort_keys=True)
        model_usage_json = json.dumps(self.space_by_model, sort_keys=True)
        owner_usage_json = json.dumps(self.space_by_owner, sort_keys=True)

        returnJSON = {
            "totalUsage": percent_total_used,
            "totalFree": percent_total_free,
            "spaceUsage": rack_positionsage_json,
            "vendorUsage": vendor_usage_json,
            "modelUsage": model_usage_json,
            "ownerUsage": owner_usage_json,
        }

        return returnJSON

    def iterate_instance(self, instance_list, rack_label):
        rack_space_used = 0
        for instance in instance_list:
            model = self.model_table.get_model(instance.model_id)
            rack_space_used += model.height

            if model.vendor in self.space_by_vendor:
                self.space_by_vendor[model.vendor] += model.height
            else:
                self.space_by_vendor[model.vendor] = model.height

            model_name = model.vendor + " " + model.model_number
            if model_name in self.space_by_model:
                self.space_by_model[model_name] += model.height
            else:
                self.space_by_model[model_name] = model.height

            if instance.owner is None or instance.owner == "":
                owner = "No owner listed"
            else:
                owner = instance.owner
            if owner in self.space_by_owner:
                self.space_by_owner[owner] += model.height
            else:
                self.space_by_owner[owner] = model.height

        if rack_label in self.rack_count:
            self.space_by_rack[rack_label] += rack_space_used
            self.rack_count[rack_label] += 1
        else:
            self.space_by_rack[rack_label] = rack_space_used
            self.rack_count[rack_label] = 1

    def divide_dict_by_space(self, dictionary, total_space_used):
        for key in dictionary:
            dictionary[key] = round((dictionary[key] / total_space_used) * 100,
                                    2)

        return dictionary

    def reset_counters(self):
        self.space_by_rack = {}
        self.space_by_vendor = {}
        self.space_by_model = {}
        self.space_by_owner = {}
        self.rack_count = {}

        return
Example #19
0
 def __init__(self):
     self.dc_table = DatacenterTable()
     self.rack_table = RackTable()
Example #20
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
 def __init__(self):
     self.dc_table = DatacenterTable()
     self.validate = DatacenterValidator()
from app.users.authentication import AuthManager

application.debug = True  # isort:skip
init()  # isort:skip

# Create all tables
with application.app_context():
    db.drop_all()
    db.create_all()
    db.session.commit()

    # Datacenter 1
    dc1: Datacenter = Datacenter(abbreviation="DC1",
                                 name="DC1",
                                 is_offline_storage=False)
    DatacenterTable().add_datacenter(datacenter=dc1)

    # Admin user
    encrypted_password = AuthManager().encrypt_pw(password="******")
    datacenters = ["*"]
    priv: Permission = Permission(
        model=True,
        asset=True,
        datacenters=datacenters,
        power=True,
        audit=True,
        admin=True,
    )
    user: User = User(
        username="******",
        display_name="Admin",