ucampurestorage.lib.pureconnect — ucampurestorage 1.0.1 documentation

# Source code for ucampurestorage.lib.pureconnect

    import re
    from ucampurestorage.lib.httpclient import HttpClient
    from ucampurestorage.lib.process import Process
    import logging

    LOG = logging.getLogger(__name__)

    def totalbytes(value: str) -> int:
        valid_unit = ["k", "m", "g", "t", "p"]
        cap = int(value[:-1])
        unit = value[-1].lower()
        if unit not in valid_unit:
            raise "Invalid capacity unit"
        else:
            return converttobytes(cap, unit)

    def converttobytes(value, fromtype, bsize=1024):
        """
        Converts megabytes to bytes, etc.
        sample code:
            print('mb= ' + str(bytesto(314575262000000, 'm')))
        sample output:
            mb= 300002347.946
        """
        unit = {"k": 1, "m": 2, "g": 3, "t": 4, "p": 5}
        result = float(value)
        for i in range(unit[fromtype]):
            result = result * bsize
        return result

    class PureConnection:
        """
        Pure Storage API interface.

        Handles calls to Pure Storage Management via the REST API interface.
        """

        def __init__(
            self, host: any, port: int, user: str, password: any, token: any, verify: bool
        ):
            """
            This creates a connection to Dell Storage Manager.

            :param host: IP address of the Pure Storage.
            :param port: Port the Pure Storage is listening on.
            :param user: User account to login with.
            :param password: Password.
            :param token: valid token.
            :param verify: Boolean indicating whether certificate verification should be turned on or not.
            """
            self.notes = "Created via Pure Storage REST API"
            self.client = HttpClient(host, port, user, password, token, verify)
            self.controllers = None
            self.arrays = None
            self.volumes = None
            self.hosts = None
            self.volumegroups = None
            self.hostgroups = None
            self.volumesnapshots = None
            self.targetports = None
            self.networkports = None
            self.volumeconnections = None
            self.protectiongroups = None
            self.protectiongroupsnapshots = None
            self.protectiongroupvolmembers = None
            self.pods = None
            self.remotepods = None
            self.destroyed_volume = None
            self.clone_volumes = None
            self.localmps = None

        def _get_json(self, blob):
            """
            Returns a dict from the JSON of a REST response.

            :param blob: The response from a REST call.
            :returns: JSON or None on error.
            """
            try:
                return blob.json()
            except AttributeError:
                LOG.error("ERROR: invalid json")
            return None

        def _check_result(self, rest_response):
            """
            Checks and logs API responses.
            :param rest_response: The result from a REST API call.
            :returns: ``True`` if success, ``False`` otherwise.
            """
            if 200 <= rest_response.status_code < 300:
                return True
            return False

        def run_list_object(self, object_type):
            """
            Returns the result of get object functions and updates the corresponding data structures.

            :param object_type: the object type to list.
            :return: couple of (result of object get function, object data structure)
            """
            function_switcher = {
                "arrays": (self.get_arrays, "arrays"),
                "controllers": (self.get_controllers, "controllers"),
                "hosts": (self.get_hosts, "hosts"),
                "volumes": (self.get_volumes, "volumes"),
                "destroyed_volumes": (self.get_vol_destroyed, "destroyed_volumes"),
                "volumegroups": (self.get_volumegroups, "volumegroups"),
                "hostgroups": (self.get_hostgroups, "hostgroups"),
                "volumesnapshots": (self.get_volumesnapshots, "volumesnapshots"),
                "targetports": (self.get_targetports, "targetports"),
                "networkports": (self.get_networkports, "networkports"),
                "volumeconnections": (self.get_volumeconnections, "volumeconnections"),
                "protectiongroups": (self.get_protectiongroups, "protectiongroups"),
                "protectiongroupsnapshots": (
                    self.get_protectiongroupsnapshots,
                    "protectiongroupsnapshots",
                ),
                "protectiongroupvolmembers": (
                    self.get_protectiongroupvolmembers,
                    "protectiongroupvolmembers",
                ),
                "pods": (self.get_pods, "pods"),
                "remotepods": (self.get_remotepods, "remotepods"),
                "localmps": (self.get_local_mountpoints, "localmps"),
            }
            (get_object_func, data_structure) = function_switcher.get(object_type)
            result = get_object_func()
            return (result, getattr(self, data_structure)) if result else (result, None)

        def get_arrays(self):
            """
            Gets all arrays and updates `self.arrays`.
            :returns: `True` for success, `False` on error.
            """
            path = "/arrays"
            response = self.client.get(path)
            if not self._check_result(response):
                return False
            self.arrays = {}
            stdout = self._get_json(response)
            for i in range(len(stdout["items"])):
                self.arrays[stdout["items"][i]["name"]] = {}
                self.arrays[stdout["items"][i]["name"]]["id"] = stdout["items"][i]["id"]
                self.arrays[stdout["items"][i]["name"]]["os"] = stdout["items"][i]["os"]
                self.arrays[stdout["items"][i]["name"]]["version"] = stdout["items"][i][
                    "version"
                ]
                self.arrays[stdout["items"][i]["name"]]["console_lock_enabled"] = stdout[
                    "items"
                ][i]["console_lock_enabled"]
                self.arrays[stdout["items"][i]["name"]]["scsi_timeout"] = stdout["items"][
                    i
                ]["scsi_timeout"]
                self.arrays[stdout["items"][i]["name"]]["capacity"] = stdout["items"][i][
                    "capacity"
                ]
                self.arrays[stdout["items"][i]["name"]]["space"] = stdout["items"][i][
                    "space"
                ]
                self.arrays[stdout["items"][i]["name"]]["encryption"] = stdout["items"][i][
                    "encryption"
                ]
                self.arrays[stdout["items"][i]["name"]]["eradication_config"] = stdout[
                    "items"
                ][i]["eradication_config"]
                self.arrays[stdout["items"][i]["name"]]["ntp_servers"] = stdout["items"][i][
                    "ntp_servers"
                ]
            return True

        def get_controllers(self):
            """
            Gets all controllers and updates `self.controllers`.
            :returns: `True` for success, `False` on error.
            """
            path = "/controllers"
            response = self.client.get(path)
            if not self._check_result(response):
                return False
            self.controllers = {}
            stdout = self._get_json(response)

            for i in range(len(stdout["items"])):
                self.controllers[stdout["items"][i]["name"]] = {}
                self.controllers[stdout["items"][i]["name"]]["status"] = stdout["items"][i][
                    "status"
                ]
                self.controllers[stdout["items"][i]["name"]]["model"] = stdout["items"][i][
                    "model"
                ]
                self.controllers[stdout["items"][i]["name"]]["mode"] = stdout["items"][i][
                    "mode"
                ]
                self.controllers[stdout["items"][i]["name"]]["version"] = stdout["items"][
                    i
                ]["version"]
            return True

        def get_volumes(self, name=None, wwn=None):
            """
            Gets all volumes and updates `self.volumes`.
            :returns: `True` for success, `False` on error.
            """
            path = "/volumes"
            if name:
                path = f"/volumes?names={name}"

            if wwn:
                if self.process.is_wwn_serial_valid(wwn):
                    name = self.get_vol_name_by_devwwn(wwn)
                    path = f"/volumes?names={name}"
                else:
                    LOG.error(f"Specified WWN is incorrect - \"{wwn}\"")
                    return False

            response = self.client.get(path)
            if not self._check_result(response):
                return False
            self.volumes = {}
            stdout = self._get_json(response)
            for i in range(len(stdout["items"])):
                self.volumes[stdout["items"][i]["name"]] = {}
                self.volumes[stdout["items"][i]["name"]]["name"] = stdout["items"][i][
                    "name"
                ]
                self.volumes[stdout["items"][i]["name"]]["created"] = stdout["items"][i][
                    "created"
                ]
                self.volumes[stdout["items"][i]["name"]]["provisioned"] = stdout["items"][
                    i
                ]["provisioned"]
                self.volumes[stdout["items"][i]["name"]]["id"] = stdout["items"][i]["id"]
                self.volumes[stdout["items"][i]["name"]]["serial"] = stdout["items"][i][
                    "serial"
                ]
                self.volumes[stdout["items"][i]["name"]]["subtype"] = stdout["items"][i][
                    "subtype"
                ]
                self.volumes[stdout["items"][i]["name"]]["destroyed"] = stdout["items"][i][
                    "destroyed"
                ]
                self.volumes[stdout["items"][i]["name"]]["connection_count"] = stdout[
                    "items"
                ][i]["connection_count"]
                self.volumes[stdout["items"][i]["name"]]["source"] = stdout["items"][i][
                    "source"
                ]
                self.volumes[stdout["items"][i]["name"]]["space"] = stdout["items"][i][
                    "space"
                ]
                self.volumes[stdout["items"][i]["name"]][
                    "host_encryption_key_status"
                ] = stdout["items"][i]["host_encryption_key_status"]
                self.volumes[stdout["items"][i]["name"]]["pod"] = stdout["items"][i]["pod"]
                self.volumes[stdout["items"][i]["name"]]["volume_group"] = stdout["items"][
                    i
                ]["volume_group"]
                self.volumes[stdout["items"][i]["name"]][
                    "requested_promotion_state"
                ] = stdout["items"][i]["requested_promotion_state"]
                self.volumes[stdout["items"][i]["name"]]["promotion_status"] = stdout[
                    "items"
                ][i]["promotion_status"]
                self.volumes[stdout["items"][i]["name"]]["priority_adjustment"] = stdout[
                    "items"
                ][i]["priority_adjustment"]
            return True

        def get_hosts(self, name=None):
            """
            Gets all hosts and updates `self.hosts`.
            :returns: `True` for success, `False` on error.
            """
            path = "/hosts"
            if name:
                path = f"/hosts?names={name}"
            response = self.client.get(path)
            if not self._check_result(response):
                return False
            self.hosts = {}
            stdout = self._get_json(response)
            for i in range(len(stdout["items"])):
                self.hosts[stdout["items"][i]["name"]] = {}
                self.hosts[stdout["items"][i]["name"]]["is_local"] = stdout["items"][i][
                    "is_local"
                ]
                self.hosts[stdout["items"][i]["name"]]["personality"] = stdout["items"][i][
                    "personality"
                ]
                self.hosts[stdout["items"][i]["name"]]["host_group"] = stdout["items"][i][
                    "host_group"
                ]
                self.hosts[stdout["items"][i]["name"]]["vlan"] = stdout["items"][i]["vlan"]
                self.hosts[stdout["items"][i]["name"]]["preferred_arrays"] = stdout[
                    "items"
                ][i]["preferred_arrays"]
                self.hosts[stdout["items"][i]["name"]]["wwns"] = stdout["items"][i]["wwns"]
                self.hosts[stdout["items"][i]["name"]]["connection_count"] = stdout[
                    "items"
                ][i]["connection_count"]
                self.hosts[stdout["items"][i]["name"]]["iqns"] = stdout["items"][i]["iqns"]
                self.hosts[stdout["items"][i]["name"]]["nqns"] = stdout["items"][i]["nqns"]
                self.hosts[stdout["items"][i]["name"]]["chap"] = stdout["items"][i]["chap"]
                self.hosts[stdout["items"][i]["name"]]["port_connectivity"] = stdout[
                    "items"
                ][i]["port_connectivity"]
            return True

        def get_volumegroups(self):
            """
            Gets all volume-groups and updates `self.volumegroups`.
            :returns: `True` for success, `False` on error.
            """
            path = "/volume-groups"
            response = self.client.get(path)
            if not self._check_result(response):
                return False
            self.volumegroups = {}
            stdout = self._get_json(response)
            for i in range(len(stdout["items"])):
                self.volumegroups[stdout["items"][i]["name"]] = {}
                self.volumegroups[stdout["items"][i]["name"]]["id"] = stdout["items"][i][
                    "id"
                ]
                self.volumegroups[stdout["items"][i]["name"]]["destroyed"] = stdout[
                    "items"
                ][i]["destroyed"]
                self.volumegroups[stdout["items"][i]["name"]]["time_remaining"] = stdout[
                    "items"
                ][i]["time_remaining"]
                self.volumegroups[stdout["items"][i]["name"]]["volume_count"] = stdout[
                    "items"
                ][i]["volume_count"]
                self.volumegroups[stdout["items"][i]["name"]]["space"] = stdout["items"][i][
                    "space"
                ]
                self.volumegroups[stdout["items"][i]["name"]]["qos"] = stdout["items"][i][
                    "qos"
                ]
                self.volumegroups[stdout["items"][i]["name"]][
                    "priority_adjustment"
                ] = stdout["items"][i]["priority_adjustment"]
            return True

        def get_hostgroups(self):
            """
            Gets all host-groups and updates `self.hostgroups`.
            :returns: `True` for success, `False` on error.
            """
            path = "/host-groups"
            response = self.client.get(path)
            if not self._check_result(response):
                return False
            self.hostgroups = {}
            stdout = self._get_json(response)
            for i in range(len(stdout["items"])):
                self.hostgroups[stdout["items"][i]["name"]] = {}
                self.hostgroups[stdout["items"][i]["name"]]["is_local"] = stdout["items"][
                    i
                ]["is_local"]
                self.hostgroups[stdout["items"][i]["name"]]["host_count"] = stdout["items"][
                    i
                ]["host_count"]
                self.hostgroups[stdout["items"][i]["name"]]["connection_count"] = stdout[
                    "items"
                ][i]["connection_count"]
                self.hostgroups[stdout["items"][i]["name"]]["space"] = stdout["items"][i][
                    "space"
                ]
            return True

        def get_volumesnapshots(self, name=None):
            """
            Gets all volume-snapshots and updates `self.volumesnapshots`.
            :returns: `True` for success, `False` on error.
            """
            path = "/volume-snapshots"
            if name:
                path = f"/volume-snapshots?source_names={name}"
            response = self.client.get(path)
            if not self._check_result(response):
                return False
            self.volumesnapshots = {}
            stdout = self._get_json(response)
            for i in range(len(stdout["items"])):
                self.volumesnapshots[stdout["items"][i]["name"]] = {}
                self.volumesnapshots[stdout["items"][i]["name"]]["id"] = stdout["items"][i][
                    "id"
                ]
                self.volumesnapshots[stdout["items"][i]["name"]]["serial"] = stdout[
                    "items"
                ][i]["serial"]
                self.volumesnapshots[stdout["items"][i]["name"]]["created"] = stdout[
                    "items"
                ][i]["created"]
                self.volumesnapshots[stdout["items"][i]["name"]]["time_remaining"] = stdout[
                    "items"
                ][i]["time_remaining"]
                self.volumesnapshots[stdout["items"][i]["name"]]["provisioned"] = stdout[
                    "items"
                ][i]["provisioned"]
                self.volumesnapshots[stdout["items"][i]["name"]]["destroyed"] = stdout[
                    "items"
                ][i]["destroyed"]
                self.volumesnapshots[stdout["items"][i]["name"]]["source"] = stdout[
                    "items"
                ][i]["source"]
                self.volumesnapshots[stdout["items"][i]["name"]]["pod"] = stdout["items"][
                    i
                ]["pod"]
                self.volumesnapshots[stdout["items"][i]["name"]]["space"] = stdout["items"][
                    i
                ]["space"]
                self.volumesnapshots[stdout["items"][i]["name"]]["volume_group"] = stdout[
                    "items"
                ][i]["volume_group"]
            return True

        def get_targetports(self):
            """
            Gets all ports and updates `self.target-ports`.
            :returns: `True` for success, `False` on error.
            """
            path = "/ports"
            response = self.client.get(path)
            if not self._check_result(response):
                return False
            self.targetports = {}
            stdout = self._get_json(response)
            for i in range(len(stdout["items"])):
                self.targetports[stdout["items"][i]["name"]] = {}
                self.targetports[stdout["items"][i]["name"]]["nqn"] = stdout["items"][i][
                    "nqn"
                ]
                self.targetports[stdout["items"][i]["name"]]["iqn"] = stdout["items"][i][
                    "iqn"
                ]
                self.targetports[stdout["items"][i]["name"]]["wwn"] = stdout["items"][i][
                    "wwn"
                ]
                self.targetports[stdout["items"][i]["name"]]["portal"] = stdout["items"][i][
                    "portal"
                ]
                self.targetports[stdout["items"][i]["name"]]["failover"] = stdout["items"][
                    i
                ]["failover"]
            return True

        def get_networkports(self):
            """
            Gets all networkports and updates `self.target-ports`.
            :returns: `True` for success, `False` on error.
            """
            path = "/network-interfaces"
            response = self.client.get(path)
            if not self._check_result(response):
                return False
            self.networkports = {}
            stdout = self._get_json(response)
            for i in range(len(stdout["items"])):
                self.networkports[stdout["items"][i]["name"]] = {}
                self.networkports[stdout["items"][i]["name"]]["enabled"] = stdout["items"][
                    i
                ]["enabled"]
                self.networkports[stdout["items"][i]["name"]]["speed"] = stdout["items"][i][
                    "speed"
                ]
                self.networkports[stdout["items"][i]["name"]]["interface_type"] = stdout[
                    "items"
                ][i]["interface_type"]
                self.networkports[stdout["items"][i]["name"]]["fc"] = stdout["items"][i][
                    "fc"
                ]
                self.networkports[stdout["items"][i]["name"]]["eth"] = stdout["items"][i][
                    "eth"
                ]
                self.networkports[stdout["items"][i]["name"]]["services"] = stdout["items"][
                    i
                ]["services"]
            return True

        def get_volumeconnections(self, name=None):
            """
            Gets all connections and updates `self.volumeconnections`.
            :returns: `True` for success, `False` on error.
            """
            path = "/connections"
            if name:
                path = f"/connections?volume_names={name}"
            response = self.client.get(path)
            if not self._check_result(response):
                return False
            self.volumeconnections = {}
            stdout = self._get_json(response)
            for i in range(len(stdout["items"])):
                host_id = (
                    stdout["items"][i]["host"]["name"]
                    + "_ID:"
                    + str(stdout["items"][i]["lun"])
                )
                self.volumeconnections[host_id] = {}
                self.volumeconnections[host_id]["host"] = stdout["items"][i]["host"]
                self.volumeconnections[host_id]["lun"] = stdout["items"][i]["lun"]
                self.volumeconnections[host_id]["volume"] = stdout["items"][i]["volume"]
                self.volumeconnections[host_id]["host_group"] = stdout["items"][i][
                    "host_group"
                ]
                self.volumeconnections[host_id]["protocol_endpoint"] = stdout["items"][i][
                    "protocol_endpoint"
                ]
            return True

        def get_protectiongroups(self):
            """
            Gets all networkports and updates `self.target-ports`.
            :returns: `True` for success, `False` on error.
            """
            path = "/protection-groups"
            response = self.client.get(path)
            if not self._check_result(response):
                return False
            self.protectiongroups = {}
            stdout = self._get_json(response)
            for i in range(len(stdout["items"])):
                self.protectiongroups[stdout["items"][i]["name"]] = {}
                self.protectiongroups[stdout["items"][i]["name"]]["is_local"] = stdout[
                    "items"
                ][i]["is_local"]
                self.protectiongroups[stdout["items"][i]["name"]]["destroyed"] = stdout[
                    "items"
                ][i]["destroyed"]
                self.protectiongroups[stdout["items"][i]["name"]]["host_count"] = stdout[
                    "items"
                ][i]["host_count"]
                self.protectiongroups[stdout["items"][i]["name"]][
                    "host_group_count"
                ] = stdout["items"][i]["host_group_count"]
                self.protectiongroups[stdout["items"][i]["name"]]["target_count"] = stdout[
                    "items"
                ][i]["target_count"]
                self.protectiongroups[stdout["items"][i]["name"]]["pod"] = stdout["items"][
                    i
                ]["pod"]
                self.protectiongroups[stdout["items"][i]["name"]][
                    "snapshot_schedule"
                ] = stdout["items"][i]["snapshot_schedule"]
                self.protectiongroups[stdout["items"][i]["name"]][
                    "replication_schedule"
                ] = stdout["items"][i]["replication_schedule"]
                self.protectiongroups[stdout["items"][i]["name"]][
                    "source_retention"
                ] = stdout["items"][i]["source_retention"]
                self.protectiongroups[stdout["items"][i]["name"]][
                    "target_retention"
                ] = stdout["items"][i]["target_retention"]
                self.protectiongroups[stdout["items"][i]["name"]]["volume_count"] = stdout[
                    "items"
                ][i]["volume_count"]
                self.protectiongroups[stdout["items"][i]["name"]]["space"] = stdout[
                    "items"
                ][i]["space"]
            return True

        def get_protectiongroupsnapshots(self):
            """
            Gets all networkports and updates `self.target-ports`.
            :returns: `True` for success, `False` on error.
            """
            path = "/protection-group-snapshots"
            response = self.client.get(path)
            if not self._check_result(response):
                return False
            self.protectiongroupsnapshots = {}
            stdout = self._get_json(response)
            for i in range(len(stdout["items"])):
                self.protectiongroupsnapshots[stdout["items"][i]["name"]] = {}
                self.protectiongroupsnapshots[stdout["items"][i]["name"]][
                    "created"
                ] = stdout["items"][i]["created"]
                self.protectiongroupsnapshots[stdout["items"][i]["name"]][
                    "destroyed"
                ] = stdout["items"][i]["destroyed"]
                self.protectiongroupsnapshots[stdout["items"][i]["name"]][
                    "suffix"
                ] = stdout["items"][i]["suffix"]
                self.protectiongroupsnapshots[stdout["items"][i]["name"]][
                    "time_remaining"
                ] = stdout["items"][i]["time_remaining"]
                self.protectiongroupsnapshots[stdout["items"][i]["name"]][
                    "source"
                ] = stdout["items"][i]["source"]
                self.protectiongroupsnapshots[stdout["items"][i]["name"]]["pod"] = stdout[
                    "items"
                ][i]["pod"]
                self.protectiongroupsnapshots[stdout["items"][i]["name"]][
                    "eradication_config"
                ] = stdout["items"][i]["eradication_config"]
                self.protectiongroupsnapshots[stdout["items"][i]["name"]]["space"] = stdout[
                    "items"
                ][i]["space"]
            return True

        def get_protectiongroupvolmembers(self):
            """
            Gets all volumes in protection-groups and updates `self.protectiongroupvolmembers`.
            :returns: `True` for success, `False` on error.
            """
            path = "/protection-groups/volumes"
            response = self.client.get(path)
            if not self._check_result(response):
                return False
            self.protectiongroupvolmembers = {}
            stdout = self._get_json(response)
            for i in range(len(stdout["items"])):
                if (
                    stdout["items"][i]["group"]["name"]
                    not in self.protectiongroupvolmembers.keys()
                ):
                    self.protectiongroupvolmembers[stdout["items"][i]["group"]["name"]] = {}
                    self.protectiongroupvolmembers[stdout["items"][i]["group"]["name"]][
                        "member"
                    ] = []
                self.protectiongroupvolmembers[stdout["items"][i]["group"]["name"]][
                    "member"
                ].append(stdout["items"][i]["member"])
            return True

        def get_pods(self):
            """
            Gets all pods and updates `self.pods`.
            :returns: `True` for success, `False` on error.
            """
            path = "/pods"
            response = self.client.get(path)
            if not self._check_result(response):
                return False
            self.pods = {}
            stdout = self._get_json(response)
            for i in range(len(stdout["items"])):
                self.pods[stdout["items"][i]["name"]] = {}
                self.pods[stdout["items"][i]["name"]]["id"] = stdout["items"][i]["id"]
                self.pods[stdout["items"][i]["name"]]["destroyed"] = stdout["items"][i][
                    "destroyed"
                ]
                self.pods[stdout["items"][i]["name"]]["mediator"] = stdout["items"][i][
                    "mediator"
                ]
                self.pods[stdout["items"][i]["name"]]["promotion_status"] = stdout["items"][
                    i
                ]["promotion_status"]
                self.pods[stdout["items"][i]["name"]]["requested_promotion_state"] = stdout[
                    "items"
                ][i]["requested_promotion_state"]
                self.pods[stdout["items"][i]["name"]]["failover_preferences"] = stdout[
                    "items"
                ][i]["failover_preferences"]
                self.pods[stdout["items"][i]["name"]]["mediator_version"] = stdout["items"][
                    i
                ]["mediator_version"]
                self.pods[stdout["items"][i]["name"]]["link_source_count"] = stdout[
                    "items"
                ][i]["link_source_count"]
                self.pods[stdout["items"][i]["name"]]["link_target_count"] = stdout[
                    "items"
                ][i]["link_target_count"]
                self.pods[stdout["items"][i]["name"]]["footprint"] = stdout["items"][i][
                    "footprint"
                ]
                self.pods[stdout["items"][i]["name"]]["time_remaining"] = stdout["items"][
                    i
                ]["time_remaining"]
                self.pods[stdout["items"][i]["name"]]["array_count"] = stdout["items"][i][
                    "array_count"
                ]
                self.pods[stdout["items"][i]["name"]]["source"] = stdout["items"][i][
                    "source"
                ]
                self.pods[stdout["items"][i]["name"]]["arrays"] = stdout["items"][i][
                    "arrays"
                ]
                self.pods[stdout["items"][i]["name"]]["space"] = stdout["items"][i]["space"]
                self.pods[stdout["items"][i]["name"]]["eradication_config"] = stdout[
                    "items"
                ][i]["eradication_config"]
            return True

        def get_remotepods(self):
            """
            Gets all pods and updates `self.pods`.
            :returns: `True` for success, `False` on error.
            """
            path = "/remote-pods"
            response = self.client.get(path)
            if not self._check_result(response):
                return False
            self.remotepods = {}
            stdout = self._get_json(response)
            for i in range(len(stdout["items"])):
                self.remotepods[stdout["items"][i]["name"]] = {}
                self.remotepods[stdout["items"][i]["name"]]["id"] = stdout["items"][i]["id"]
                self.remotepods[stdout["items"][i]["name"]]["arrays"] = stdout["items"][i][
                    "arrays"
                ]
            return True

        def get_vol_name_by_devwwn(self, wwn):
            fetch_all_vol = self.get_volumes()
            if fetch_all_vol:
                for key, value in self.volumes.items():
                    if value["serial"].lower() == wwn[-24:].lower():
                        return key
                return False

        def get_vol_destroyed(self):
            self.destroyed_volumes = {}
            fetch_all_vol = self.get_volumes()
            if fetch_all_vol:
                for name, info in self.volumes.items():
                    if self.volumes[name]["destroyed"]:
                        self.destroyed_volumes[name] = info
                return True
            else:
                return False

        def get_vol_clones(self, src_name, src_wwn):
            if src_wwn:
                if self.process.is_wwn_serial_valid(src_wwn):
                    src_name = self.get_vol_name_by_devwwn(src_wwn)
                else:
                    LOG.error(f"Specified WWN is incorrect - \"{src_wwn}\"")
                    return False
            self.clone_volumes = {}
            fetch_all_vol = self.get_volumes()
            if fetch_all_vol:
                for name, info in self.volumes.items():
                    if self.volumes[name]["source"]["name"] == src_name:
                        self.clone_volumes[name] = info
                return True
            else:
                return False

    class SystemTask(PureConnection):
        def __init__(
            self, host: any, port: int, user: str, password: any, token: any, verify: bool
        ):
            super().__init__(host, port, user, password, token, verify)
            self.process = Process()

        def get_mountpoint_info(self, mountpoint=None):
            """
            Returns details of volume with WWN `wwn` or mounted on `mountpoint`.

            :param name: volume's `name`.
            :param mountpoint: `mountpoint` where the volume is mounted.
            :returns: str wwn, `None`, otherwise.
            """
            if mountpoint is None:
                return None

            if mountpoint is not None:
                mpath = self.process.get_mpath_from_mountpoint(mountpoint)
                if mpath is None:
                    return None

                wwn = self.process.get_wwn_from_mpath(mpath)
                if wwn is None:
                    return None

            return wwn

        def get_local_server_name_on_array(self):
            """Returns local server ID, or `None` on error."""
            hostname = self.process.get_hostname()
            server_name = hostname.split(".")[0]

            server_status = self.get_hosts(server_name)
            if not server_status:
                LOG.error(f"HOST: {server_name} API Call failed")
            return (server_name, self.hosts)

        def generate_clone_name(self, volume_name):
            """
            Generates a clone name from a volume name.

            :param volume_name: the volume name from which to generate a clone name.
            :returns: clone name, `None`, otherwise.
            """
            # remove clone name suffix from volume name
            original_name = re.sub(r"_\d+$", "", volume_name)

            # get list of volumes starting with 'orig_volname'
            self.get_volumes()
            lst = []
            for vol in self.volumes.values():
                if vol["name"].startswith(original_name):
                    lst.append(vol["name"])

            if len(lst) == 0:
                # LOG.error('Clone name cannot be generated as no volumes is found.')
                return None

            # get indexes in the volume list
            volname_with_index = re.compile(r".*(_\d+)$")
            volindex_lst = [
                int(volname_.replace(f"{original_name}_", ""))
                for volname_ in lst
                if volname_with_index.match(volname_)
            ]
            volindex_lst.sort()
            if len(volindex_lst) == 0:
                clone_index = 1
            else:
                clone_index = volindex_lst[-1] + 1

            return f"{original_name}_{clone_index}"

        def get_local_mountpoints(self):
            self.localmps = {}
            mps = self.process.get_local_mountpoints()
            if not mps:
                LOG.error("mpuntpount info not fetched")
                return False
            for mp in mps:
                self.localmps[mp] = {}
                self.localmps[mp]["fsdev"] = self.process.get_fsdevice_from_mountpoint(mp)
                self.localmps[mp]["mpath"] = self.process.get_mpath_from_mountpoint(mp)
                self.localmps[mp]["wwn"] = self.process.get_wwn_from_mpath(
                    self.localmps[mp]["mpath"]
                )
                self.localmps[mp]["alias"] = self.process.get_mpath_from_wwn(
                    self.localmps[mp]["wwn"]
                )
                self.localmps[mp]["name"] = (
                    self.get_vol_name_by_devwwn(self.localmps[mp]["wwn"])
                    if self.get_vol_name_by_devwwn(self.localmps[mp]["wwn"])
                    else None
                )
            return True

    class PureAdvanceConnection(SystemTask):
        # def run_volume_operation

        def create_volume(self, size: str, name: str) -> bool:
            """Create volume.

            Args:
                name (str): Name of the volume to be create on PureStorage Array
                size (str): Capacity of volume Eg: "1T", "300G", "20M"

            Returns:
                boolean: `True` for success, `False` on error.
            """
            path = "/volumes"
            data = {
                "names": name,
                "provisioned": totalbytes(size),
            }
            response = self.client.post(path, data)
            if not self._check_result(response):
                return False
            return True

        def delete_volume(self, name: str, wwn: str) -> bool:
            """Delete volume.

            Args:
                name (str): Name of the volume to be deleted on PureStorage Array
                wwn (str): WWN of the volume to be deleted on PureStorage Array

            Returns:
                boolean: `True` for success, `False` on error.
            """
            path = "/volumes"
            if wwn:
                if self.process.is_wwn_serial_valid(wwn):
                    name = self.get_vol_name_by_devwwn(wwn)
                else:
                    LOG.error(f"Specified WWN is incorrect - \"{wwn}\"")
                    return False

            data = {
                "names": name,
                "destroyed": True,
            }
            response = self.client.patch(path, data)
            if not self._check_result(response):
                return False
            return True

        def eradicate_volume(self, name: str) -> bool:
            """Eradicate volume.

            Args:
                name (str): Name of the volume to be eradicate on PureStorage Array

            Returns:
                boolean: `True` for success, `False` on error.
            """
            path = "/volumes"
            data = {
                "names": name,
            }
            response = self.client.delete(path, data)
            if not self._check_result(response):
                return False
            return True

        def connect_volume(self, hostname: str, volname: str, **kwargs) -> bool:
            """Connect volume.

            Args:
                hostname (str): Name of the host specified on PureStorage Array with which volume need to be connect
                volname (str): Name of the volume specified on PureStorage Array which need to be connect

            Returns:
                boolean: `True` for success, `False` on error.
            """
            path = "/connections"
            data = {
                "host_names": hostname,
                "volume_names": volname,
            }
            if kwargs["lunid"] is not None:
                data["lun"] = int(kwargs["lunid"])
            response = self.client.post(path, data)
            if not self._check_result(response):
                LOG.error(f"Failure : Connection of volume {volname} with {hostname}")
                return False
            return True

        def disconnect_volume(self, hostname: str, volname: str) -> bool:
            """Remove the connection of volume from .

            Args:
                hostname (str): Name of the host specified on PureStorage Array with which volume need to be connect
                volname (str): Name of the volume specified on PureStorage Array which need to be connect

            Returns:
                boolean: `True` for success, `False` on error.
            """
            path = "/connections"
            data = {
                "host_names": hostname,
                "volume_names": volname,
            }
            response = self.client.delete(path, data)
            if not self._check_result(response):
                return False
            return True

        def map_volume(self, name, mountpoint=None, new_vol=False):
            """Map volume to local server and mount it to `mountpoint`.

            Args:
                name (str): Name of the volume to map to local server.
                mountpoint (str): Name of the mountpoint on local server to be mounted
                new_vol (bool): Is the volume name newly created in PureStorage Array

            Returns:
                boolean: `True` for success, `False` on error.
            """
            # check if the volume exist
            volume_exist = self.get_volumes(name)
            if not volume_exist:
                LOG.error(f"Volume: {name} - doesnot exists on the Array")
                return False

            # check if mount point already used and get wwn number of the volume using this mountpoint
            vol_wwn = self.get_mountpoint_info(mountpoint)
            if vol_wwn is not None:
                LOG.error(f"Mount Point already in use by volume : {vol_wwn}")
                return False

            # check if the volume is already mapped to any servers
            vol_mappings = self.get_volumeconnections(name)
            if not vol_mappings:
                LOG.error(f"Volume: {name} API Call failed for connection validation")
                return False

            if bool(self.volumeconnections):
                LOG.error(f"Volume is already connected to: \n {self.volumeconnections}")
                return False

            # get local server details from the Pure Array and map the volume to it
            (hostname, server_name) = self.get_local_server_name_on_array()
            if not bool(server_name):
                LOG.error(f"{hostname} doesn't exist on the Array")
                return False

            # connect the volume to host
            if not self.connect_volume(hostname, name, lunid=None):
                return False

            # rescan scsi bus
            if not self.process.rescan_scsibus():
                return False

            # fetch the serialnumber of volume
            volume_detail = self.get_volumes(name)
            if volume_detail:
                dev_serial = self.volumes[name]["serial"]
            wwn = self.process.get_wwid_from_wwn(dev_serial)

            # add multipath alias of the volume
            if not self.process.add_multipath_alias(wwn):
                return False

            # reload multipath
            if not self.process.reload_multipathd():
                return False

            # if mountpoint is not specified then nothing to do
            if mountpoint is None:
                LOG.info("Mountpoint is not specified.")
                return True

            # if mountpoint specified doesn't exist then make directory
            if not self.process.is_dir_exist(mountpoint):
                if not self.process.create_dir(mountpoint):
                    LOG.error(f"Direcory {mountpoint} creation failed")
                    return False

            # if a mountpoint is specified, update fstab file and mount the volume
            # If the volume is new then partition and create the directory id not already created\
            #  for specified mountpoint
            new_alias = self.process.get_mpath_from_wwn(wwn)
            if new_alias is not None:
                new_dev = f"/dev/mapper/{new_alias}"
                new_fsdevice = f"/dev/mapper/{new_alias}p1"

                # if the new volume is created and never formated
                if new_vol:
                    # create partion
                    if not self.process.create_partition(new_dev):
                        LOG.error(f"partion creation failed for {new_dev}")
                        return False
                    if not self.process.update_system_partition(new_dev):
                        LOG.error(f"Partition of system not updated: {new_dev}")
                        return False
                    # Format partition to the ext4
                    if not self.process.format_partition_with_ext4(new_fsdevice):
                        LOG.error(f"Formating of the Partition failed: {new_fsdevice}")
                        return False

                if not self.process.add_entry_in_fstab(new_fsdevice, mountpoint):
                    LOG.error("%s cannot be added to /etc/fstab", new_fsdevice)
                    return False
                if not self.process.daemon_reload():
                    return False
            else:
                LOG.error("Alias for the volume with WWN %s does not exist.", mountpoint)
                return False

            # mount target_mountpoint if it's not mounted
            # found it gets mounted after reload multipath, is it an expected behaviour?
            if not self.process.mountpoint_exists(mountpoint):
                if not self.process.mount(mountpoint):
                    LOG.error('Mount operation has failed. Use "-new" for new volume')
                    return False

            return True

        def unmap_volume(self, name=None, wwn=None, mp=None):
            """
            Unmaps a Pure Storage volume with `WWN`. Unmounting the volume should be performed manually.

            :param name: `name` of the volume to unmap.
            :param wwn: `wwn` of the volume to unmap.
            :param mp: `mountpoint` of the localsever to unmap.
            :returns: `True` for success, `False` on error.
            """
            # check if volume name exists on Pure Storage
            if name:
                volume_exist = self.get_volumes(name)
                if not volume_exist:
                    return False

            # fetch wwn number of the device if input is name
            if name:
                if volume_exist:
                    dev_serial = self.volumes[name]["serial"]
                wwn = self.process.get_wwid_from_wwn(dev_serial)

            # fetch the wwn number of device if input is mountpoint
            if mp:
                mpath = self.process.get_mpath_from_mountpoint(mp)
                wwn = self.process.get_wwn_from_mpath(mpath)

            # check wwn of user input and fetch the name of the device
            if wwn:
                wwn = self.process.get_wwid_from_wwn(wwn)
                name = self.get_vol_name_by_devwwn(wwn)

            # Check if the host exist on the Pure Storage
            (hostname, server_name) = self.get_local_server_name_on_array()
            if not bool(server_name):
                LOG.error(f"{hostname} doesn't exist on the Pure Array")
                return False

            # check if the volume is already mapped to any servers
            vol_mappings = self.get_volumeconnections(name)
            if not vol_mappings:
                LOG.error(f"Volume: {name} API Call failed for connection validation")
                return False

            if not bool(self.volumeconnections):
                LOG.error(f"Volume {name} not connected with host {hostname}")
                return False

            # check if volume is visible to local server
            alias = self.process.get_mpath_from_wwn(wwn)
            if alias is None:
                return False

            # device_name variable declariation
            # To perform unmount and remove entry from fstab
            device_name = f"/dev/mapper/{alias}p1"

            # make sure volume is not mounted
            # unmount the filesystem if it is mounted
            if self.process.is_wwn_mounted(wwn) is not None:
                LOG.warning(
                    "Volume with WWN %s is mounted on local server. Unmount it first.", wwn
                )
                mountpoint = self.process.get_mountpoint_from_devicename(device_name)
                if self.process.mountpoint_exists(mountpoint):
                    if not self.process.umount(mountpoint):
                        return False

            # remove volume from fstab config file
            if self.process.is_device_in_fstab(device_name):
                if not self.process.remove_entry_from_fstab(device_name):
                    return False
                if not self.process.daemon_reload():
                    return False
            else:
                LOG.error("%s is not in /etc/fstab.", device_name)

            # remove (wwid, alias) entry from multipath.conf
            if not self.process.remove_multipath_entry(wwn, alias):
                return False

            # reload multipathd
            if not self.process.reload_multipathd():
                return False

            # delete raw devices of multipath device alias
            raw_devices = self.process.get_multipath_raw_devices(wwn)
            if raw_devices is None:
                LOG.warning(
                    "No raw devices (%s) for multipath device %s.", raw_devices, alias
                )
            else:
                result_delete_rawdevs = []

                for rawdev in raw_devices:
                    result = self.process.delete_raw_devices(rawdev)
                    result_delete_rawdevs.append(result)

                if False in result_delete_rawdevs:
                    LOG.error(
                        "Failed to delete the raw devices %s of %s. Result: %s.",
                        raw_devices,
                        alias,
                        result_delete_rawdevs,
                    )
                    return False

                LOG.info(
                    "The raw devices %s of %s have been deleted. Result: %s.",
                    raw_devices,
                    alias,
                    result_delete_rawdevs,
                )

            # reload multipathd
            if not self.process.reload_multipathd():
                return False

            # unmap the volume from the local server
            return self.disconnect_volume(hostname, name)

        def replace_volume(self, src_mountpoint, dst_mountpoint, unmount_src_mp):
            """
            Clones the volume mounted on `src_mountpoint` and mount it on `dst_mountpoint`,
            the volume that was mounted on `dst_mountpoint` will be deleted.

            :param src_mountpoint: mountpoint of the volume to be cloned.
            :param dst_mountpoint: mountpoint of the clone volume.
            :param unmount_src_mp: If True, unmount the source mountpoint, otherwise, not.
            :return: `True` for success, `False` on error.
            """
            # check details of vols on source and destination mountpoints
            # volume details: (wwn, volid, volname, volFolderName, volFolderId, scName)
            src_vol_wwn = self.get_mountpoint_info(mountpoint=src_mountpoint)
            if src_vol_wwn is None:
                return False

            dst_vol_wwn = self.get_mountpoint_info(mountpoint=dst_mountpoint)
            if dst_vol_wwn is None:
                return False

            # Fetch name from the wwn
            src_vol_name = self.get_vol_name_by_devwwn(src_vol_wwn)
            if not src_vol_name:
                return False
            dst_vol_name = self.get_vol_name_by_devwwn(dst_vol_wwn)
            if not dst_vol_name:
                return False

            # unmount the volume to be cloned, call it "the source volume"
            if unmount_src_mp:
                if not self.process.umount(src_mountpoint):
                    return False

            # unmap the volume used by dst_mountpoint
            if not self.unmap_volume(dst_vol_name):
                return False

            # delete destination volume
            # TODO: with eradication
            if not self.delete_volume(dst_vol_name):
                return False

            # create a clone of source volume and mount it on dst_moutpoint
            clone_name = self.generate_clone_name(src_vol_name)
            if not self.clone_volume(clone_name, src_vol_name, dst_mountpoint):
                return False

            # mount source volume if needed
            if unmount_src_mp:
                if not self.process.mount(src_mountpoint):
                    return False

            return True

        def create_host(self, iqn: str, name: str, personality: str) -> bool:
            """Create host.

            Args:
                name (str): Name of the Host to be create on PureStorage Array
                iqn (str): iqn of Host Eg: ""
                personality (str): personality of the host

            Returns:
                boolean: `True` for success, `False` on error.
            """
            personality_detail = personality if personality != "none" else None
            path = "/hosts"
            iqns = []
            data = {
                "names": name,
                "personality": personality_detail,
            }
            if iqn:
                iqns.append(iqn)
                data["iqns"] = iqns
            response = self.client.post(path, data)
            if not self._check_result(response):
                LOG.error(f"Host Creation Failed: {name}")
                return False
            return True

        def delete_host(self, name: str) -> bool:
            """Delete host.

            Args:
                name (str): Name of the host to be deleted on PureStorage Array

            Returns:
                boolean: `True` for success, `False` on error.
            """
            path = f"/hosts?names={name}"
            response = self.client.delete(path)
            if not self._check_result(response):
                return False
            return True

        def create_snapshot(self, name: str, suffix: str) -> bool:
            """Create Snapshot of the volume.

            Args:
                name (str): Name of the volume for which snapshot to be create on PureStorage Array
                suffix (str): suffix of the snapshot

            Returns:
                boolean: `True` for success, `False` on error.
            """
            path = "/volume-snapshots"
            data = {
                "source_names": name,
                "suffix": suffix,
            }
            response = self.client.post(path, data)
            if not self._check_result(response):
                return False
            return True

        def delete_snapshot(self, name: str) -> bool:
            """Delete snapshot.

            Args:
                name (str): Name of the snapshot to be deleted on PureStorage Array

            Returns:
                boolean: `True` for success, `False` on error.
            """
            path = "/volume-snapshots"
            data = {"names": name, "destroyed": True}
            response = self.client.patch(path, data)
            if not self._check_result(response):
                return False
            return True

        def clone_volume(self, cln_name: str, src_wwn: str, src_name: str, mt_pt: str = None) -> bool:
            """Create clone volume.

            Args:
                cln_name (str): Name of the clone volume to be create on PureStorage Array
                src_wwn (str): WWN of the source volume for which clone need to be created on PureStorage Array
                src_name (str): Name of the source volume for which clone need to be created on PureStorage Array
                mt_pt (str): Name of the mountpoint on local server to be mounted

            Returns:
                boolean: `True` for success, `False` on error.
            """
            path = "/volumes"
            if src_wwn:
                if self.process.is_wwn_serial_valid(src_wwn):
                    src_name = self.get_vol_name_by_devwwn(src_wwn)
                else:
                    LOG.error(f"Specified WWN is incorrect - \"{src_wwn}\"")
                    return False
            data = {
                "names": cln_name,
                "source": {"name": src_name},
            }

            # check if the volume name of source exist
            volume_exist = self.get_volumes(src_name)
            if not volume_exist or self.volumes is None:
                # check if the provide name is a snapshot name.
                vol_name = src_name.split(".")[0]
                snapshot_exist = self.get_volumesnapshots(vol_name)
                if not snapshot_exist or src_name not in self.volumesnapshots.keys():
                    LOG.error(f'Source volume "{src_name}" is not present on Array')
                    return False

            # check if the volume name of clone exist
            volume_exist = self.get_volumes(cln_name)
            if volume_exist and self.volumes is not None:
                LOG.error(f'Clone volume "{cln_name}" already exists')
                return False

            # if mt_pt is provided then check otherwise create the clone volume
            if mt_pt:
                # Check the provided mointpoint is already created, if not created then create.
                if not self.process.is_dir_exist(mt_pt):
                    LOG.info(f'"{mt_pt}" was not present so, created new dir "{mt_pt}"')
                    self.process.create_dir(mt_pt)

                # Check the provided mountpoint is empty
                if not self.process.is_dir_empty(mt_pt):
                    return False

                # Check the provided mountpoint exist
                if self.process.mountpoint_exists(mt_pt):
                    LOG.error(f'Specified Target mountpoint "{mt_pt}" is already mounted')
                    return False

            # Create clone of the volume
            response = self.client.post(path, data)
            if not self._check_result(response):
                return False

            # if the mt_pt is provided and pre-check was fine then mount on the system.
            if mt_pt:
                self.map_volume(cln_name, mt_pt)

            return True