import re

from ..utils import hdr_result, version_gte
from .. import enums

CLONE_SPEC_ATTRS = {
    'pc_type',
    'source_snapshot',
    'clone_snapshot',
    'clone_type',
    'clone_role',
    'clone_name',
    'clone_storage_alloc',
    'clone_datastore',
    'clone_vh_id',
}


class PodApiMixin:
    pod_default_props = ['pod_id', 'pod_name', 'pt_id', 'pod_current_state']
    pod_props = ['pod_id', 'pt_id', 'def_im_name', 'def_topology_image', 'def_vlan_map', 'devices', 'device_count',
                 'pod_acl_enabled', 'pod_admin_state', 'pod_adv_settings', 'pod_auto_net_enabled',
                 'pod_auto_net_host_setup', 'pod_auto_net_host_teardown', 'pod_cat', 'pod_csw_base_port',
                 'pod_csw_base_vlan', 'pod_csw_id', 'pod_current_state', 'pod_desc', 'pod_id', 'pod_managed',
                 'pod_name', 'pod_origin', 'pod_res_id', 'pod_task_type', 'pod_task_id', 'pod_task_info', 'pod_uuid',
                 'pt_actions', 'pt_adv_settings', 'pt_apc_port_count', 'pt_apdid', 'pt_as_port_count', 'pt_author',
                 'pt_build', 'pt_copyright', 'pt_csw_port_count', 'pt_desc', 'pt_gpdid', 'pt_id', 'pt_name', 'pt_notes',
                 'pt_org', 'pt_origin', 'pt_pod_max', 'pt_removable', 'pt_tabs', 'pt_url', 'pt_vlan_pool', 'remote_pc',
                 'remote_pc_count', 'sched_image']

    pod_types_default_props = ['pt_id', 'pt_name', 'pt_build']
    pod_types_props = ['pt_id', 'pt_name', 'pt_build', 'pt_gpdid', 'pt_apdid', 'pt_desc', 'pt_index', 'pt_type',
                       'sched_image', 'def_topology_image', 'def_im_name', 'def_vlan_map', 'pt_tabs', 'pt_actions',
                       'pt_origin', 'pt_vlan_pool', 'pt_pod_max', 'pt_notes', 'pt_csw_port_count', 'pt_as_port_count',
                       'pt_apc_port_count', 'pt_build', 'pt_author', 'pt_org', 'pt_copyright', 'pt_url', 'pt_removable',
                       'pt_ae_pod', 'pt_adv_settings', 'remote_pc_count', 'device_count']

    def __pc_clone_specs(self, *, user_pc_clone_specs, remote_pc_list, clone_pod_name):
        # Ensure that user_pc_clone_specs is a list of dicts
        if isinstance(user_pc_clone_specs, dict):
            user_pc_clone_specs = [user_pc_clone_specs] * len(remote_pc_list)
        elif user_pc_clone_specs is None:
            user_pc_clone_specs = [{}] * len(remote_pc_list)

        if len(user_pc_clone_specs) != len(remote_pc_list):
            raise ValueError("`user_pc_clone_specs` and `remote_pc_list` must be the same length.")

        # Ensure that the remote_pc_list is in order
        remote_pc_list = sorted(remote_pc_list, key=lambda e: e['pl_index'])

        # Result of this function
        new_vm_out = []

        for pl_index, vms in enumerate(zip(user_pc_clone_specs, remote_pc_list), 1):
            user_vm, existing_vm = vms
            bad_clone_specs = set(user_vm.keys()) - CLONE_SPEC_ATTRS
            if bad_clone_specs:
                raise ValueError("These keys are not allowed: {}".format(bad_clone_specs))
            new_vm_out.append({
                'pl_index': pl_index,
                'pc_type': user_vm.get('pc_type', existing_vm['pc_type']),
                'source_vm_id': existing_vm['vm_id'],
                'source_snapshot': user_vm.get('source_snapshot', existing_vm['vm_snapshot']),
                'clone_snapshot': user_vm.get('clone_snapshot', existing_vm['vm_snapshot']),
                'clone_type': user_vm.get('clone_type', 'LINKED'),
                'clone_role': user_vm.get('clone_role', 'NORMAL'),
                'clone_name': user_vm.get('clone_name', clone_pod_name + '_' + existing_vm['pc_label']),
                'clone_storage_alloc': user_vm.get('clone_storage_alloc', 'ONDEMAND'),
                'clone_datastore': user_vm.get(
                    'clone_datastore',
                    re.match('^\[(.*)\].*', existing_vm['vm_path']).group(1)),
                'clone_vh_id': user_vm.get('clone_vh_id', existing_vm['vh_id']),
            })

        return new_vm_out

    def pod_add(self, *, pod_id, pt_id, devices=None, pod_acl_enabled=False, pod_adv_settings=None,
                pod_auto_net_enabled=True, pod_auto_net_host_setup=True, pod_auto_net_host_teardown=True,
                pod_cat=enums.PodCategory.PERSISTENT_VM, pod_desc=None,
                pod_name=None, **kwargs):
        """
        This method allows you to add a pod to the NETLAB+ system. Pods created will be OFFLINE after added.

        :param int pod_id: Unique pod identifier.
        :param str pt_id: Pod type identifier.
        :param list devices: Array of devices associated with this pod. This is for real equipment pods only.
        :param bool pod_acl_enabled: Pod access control list enabled.
        :param str pod_adv_settings: Advanced settings for pod configuration.
        :param bool pod_auto_net_enabled: Auto-networking enabled.
        :param bool pod_auto_net_host_setup: Auto-networking host setup enabled.
        :param bool pod_auto_net_host_teardown: Auto-networking host teardown enabled.
        :param PodCategory pod_cat: Pod category type.
        :param str pod_desc: Pod description.
        :param str pod_name: Pod name.

        :return: The addition succeeded.
        :rtype: 'OK'
        """

        method = "pod.add"
        param = {
            'pod_id': pod_id,
            'pt_id': pt_id,
        }

        if devices is not None:
            param['devices'] = devices
        if pod_acl_enabled is not None:
            param['pod_acl_enabled'] = pod_acl_enabled
        if pod_adv_settings is not None:
            param['pod_adv_settings'] = pod_adv_settings
        param['pod_auto_net_enabled'] = pod_auto_net_enabled
        param['pod_auto_net_host_setup'] = pod_auto_net_host_setup
        param['pod_auto_net_host_teardown'] = pod_auto_net_host_teardown
        param['pod_cat'] = pod_cat
        if pod_desc is not None:
            param['pod_desc'] = pod_desc
        if pod_name is not None:
            param['pod_name'] = pod_name

        param.update(kwargs)
        return self.call(method, **param)

    def pod_get(self, *, pod_id, properties="default", **kwargs):
        """

        :param int pod_id: Unique pod identifier.
        :param properties: Properties consist of any combination of properties listed below under Properties.
                           You may also use **"all"** to include all properties, or **"default"** for a smaller subset
                           of properties.
        :type properties: list[str] or 'all' or 'default'

        :return: Returns a dictionary object containing the following properties:
        :rtype: dict

        **Properties**

        pod_id
            Unique pod identifier.
        pt_id
            Pod typed identifier.
        def_im_name
            Default image name.
        def_topology_image
            Default topology image URL.
        def_vlan_map
            Default VLAN mapping.
        devices
            List of devices in pod type.
        device_count
            Count of devices in pod type.
        pod_acl_enabled
            Pod access control list enabled.
        pod_admin_state
            Administrative pod states.
        pod_adv_settings
            A list of all advanced settings.
        pod_auto_net_enabled
            Automatic VM networking enabled.
        pod_auto_net_host_setup
            Automatic Host networking setup enabled.
        pod_auto_net_host_teardown
            Automatic Host networking teardown enabled.
        pod_cat
            Pod category type. :py:class:`netlab.enums.PodCategory`
        pod_csw_base_port
            Pod control switch base port.
        pod_csw_base_vlan
            Pod control switch base VLAN.
        pod_csw_id
            Pod control switch id.
        pod_current_state
            Pod current state status.
        pod_desc
            Pod description.
        pod_id
            Unique pod identifier.
        pod_managed
            Pod is managed.
        pod_name
            Pod name.
        pod_origin
            Pod origin.
        pod_res_id
            Current pod reservation identifier.
        pod_task_type
            Current pod task type.
        pod_task_id
            Current pod task identifier.
        pod_task_info
            Current pod task information.
        pod_uuid
            Globally unique pod identifier.
        pt_actions
            Pod type actions enabled.
        pt_adv_settings
            Long string of advanced settings.
        pt_apc_port_count
            Switched outlet port count requirement.
        pt_apdid
            Author identifier for pod design.
        pt_as_port_count
            Access server port count requirement.
        pt_author
            Pod design author.
        pt_build
            Pod build number.
        pt_copyright
            Pod design copyright.
        pt_csw_port_count
            Control switch port count requirement.
        pt_desc
            Pod type description.
        pt_gpdid
            Global unique identifier.
        pt_id
            Pod type identifier.
        pt_name
            Pod type name.
        pt_notes
            Pod type notes.
        pt_org
            Pod design organization.
        pt_origin
            Pod type origin.
        pt_pod_max
            Max pod count of this type.
        pt_removable
            Pod type is removable.
        pt_tabs
            Pod type tabs enabled.
        pt_url
            Pod design support URL.
        pt_vlan_pool
            Pod type vlan pool.
        remote_pc
            List of remote PCs in pod type with their expanded properties. See Remote PC table below.
        remote_pc_count
            Count of remote PCs in pod type.
        reservations
            The number of reservations scheduled or in progress for this pod.
        sched_image
            Scheduler image URL.
        """

        method = "pod.get"

        if properties == 'all':
            properties = self.pod_props
        if properties == 'default':
            properties = self.pod_default_props

        res_param = {
            'pod_id': pod_id,
            'properties': properties,
        }
        res_param.update(kwargs)
        return self.call(method, **res_param)

    def pod_list(self, *, pod_cat=None, **kwargs):
        """
        List pods on the system.

        :param PodCategory pod_cat: Pod category type.
        :return: Returns a list of dictionary objects containing the following properties:
        :rtype: list[dict]

        **Properties**

        def_topology_image
            Default topology image URL.
        pod_acl_enabled
            Pod access control lists enabled for this pod.
        pod_admin_state
            Pod administrative state. :py:class:`netlab.enums.PodAdminState`
        pod_cat
            Pod category. :py:class:`netlab.enums.PodCategory`
        pod_current_state
            Pod current state. :py:class:`netlab.enums.PodCurrentState`
        pod_desc
            Pod description.
        pod_dyn_vlan
            Pod is using dynamic vlans.
        pod_id
            Unique pod identifier.
        pod_managed
            Pod is managed (currently always True).
        pod_name
            Pod name.
        pod_uuid
            Pod name.
        pod_res_id
            Current pod reservation identifier.
        pt_apdid
            Author identifier for pod design.
        pt_desc
            Pod type description.
        pt_gpdid
            Global unique identifier.
        pt_id
            Pod type identifier.
        pt_name
            Pod type name.
        sched_image
            Scheduler image URL.
        """
        method = "pod.list"

        res_param = {}
        if pod_cat is not None:
            res_param['pod_cat'] = pod_cat

        res_param.update(kwargs)
        return self.call(method, **res_param)

    def pod_list_used_ids(self, **kwargs):
        """
        List the used pod_id's on the system.

        :return: Returns a list of pod_ids
        :rtype: list[int]
        """

        method = "pod.list.used_ids"
        return list(map(int, self.call(method, **kwargs)))

    def pod_pc_get(self, *, pod_id=None, pl_index=None, pc_id=None, **kwargs):
        """
        Get information regarding a pod pc.

        :param int pod_id: Pod ID
        :param int pl_index: PC Pod index
        :param int pc_id: unique pc identifier

        :return: On success, returns the following properties:
        :rtype: dict

        **Properties**

        pc_icon
            Icon identifier. :py:class:`netlab.enums.PCIcon`
        pc_id
            Unique pc identifier.
        pc_label
            PC label.
        pc_online
            PC online.
        pc_origin
            PC origin.
        pc_os_id
            PC operating system identifier.
        pc_os_name
            NETLAB+ operating system name.
        pc_pod_id
            pod_id pc is associated to.
        pc_pod_index
            Index of pods.
        pc_revert_to_scrub
            Revert pc during scrub.
        pc_type
            PC type. :py:class:`netlab.enums.PCType`
        pc_vnc_ip_addr
            IP Address of vnc (ESXi host) server.
        pc_vnc_port
            Port number VNC is running on.
        pc_vnc_use_copy_rect
            Always Allow VNC to use copy rect encoding.
        pl_icon
            Pod design PC icon identifier. :py:class:`netlab.enums.PLIcon`
        pl_index
            PC pod index.
        pl_label
            Remote pc label.
        pt_id
            Pod type identifier.
        vdc_id
            Agent hostname/IP address.
        vdc_name
            VM Datacenter name.
        vh_bios_date
            Timestamp of BIOS creation date.
        vh_bios_version
            BIOS version installed.
        vh_com_path
            Virtual host communication path. :py:class:`netlab.enums.VirtualHostComPath`
        vh_cpu_cores
            Total count of CPU cores on virtual host.
        vh_cpu_mhz
            Speed in MHz of CPU.
        vh_cpu_model
            CPU model identification.
        vh_cpu_threads
            Number of threads CPUs can handle.
        vh_cpu_threads
            Number of threads CPUs can handle.
        vh_date_modified
            Timestamp of virtual host information has been modified.
        vh_date_tested
            Timestamp of virtual host test communication.
        vh_id
            Virtual host identifier.
        vh_inside_ipv4_addr
            Inside interface ipv4 address.
        vh_inside_vswitch_0
            Virtual host inside vSwitch.
        vh_last_test_status
            Status of last virtual host test communication.
        vh_memory_mb
            Total RAM in MB of virtual host.
        vh_name
            Virtual host hostname.
        vh_online
            Virtual host is online.
        vh_os_build
            Operating system build number.
        vh_os_description
            Operating system description.
        vh_os_name
            Operating system name.
        vh_os_type
            Operating system type.
        vh_os_vendor
            Operating system vendor.
        vh_os_version
            Operating system version.
        vh_outside_ipv4_addr
            Outside interface ipv4 address.
        vh_pra_enabled
            Proactive resource awareness is enabled.
        vh_pra_max_cpu
            Max number of CPUs for PRA.
        vh_pra_max_mem_mb
            Max amount of RAM in MB for PRA.
        vh_pra_max_vm
            Max amount of virtual machines for PRA.
        vh_sys_model
            System model.
        vh_sys_service_tag
            System service tag.
        vh_sys_vendor
            System vendor.
        vh_uuid
            Virtual host unique global identifier.
        vhg_id
            Virtual host group identifier.
        vm_alloc_cpu_n
            Total amount of allocated CPUs.
        vm_alloc_mem_mb
            Total amount of allocated RAM.
        vm_auto_display
            Virtual machine auto display port configuration.
        vm_auto_network
            Virtual machine auto networking.
        vm_auto_settings
            Virtual machine auto settings.
        vm_child_count
            Total count of children VMs.
        vm_comments
            Virtual machine comments from VMX.
        vm_date_added
            Timestamp of virtual machine added to NETLAB+ inventory.
        vm_id
            Virtual machine identifier.
        vm_name
            Virtual machine name.
        vm_netlab_os_id
            NETLAB+ match to vm_vendor_os_id.
        vm_parent_id
            Virtual machine parent identifier, if VM is a link clone.
        vm_parent_snapname
            Virtual machine parent snapshot name, if VM is a link clone.
        vm_path
            Virtual machine path on datastore.
        vm_power_state
            Virtual machine power state.
        vm_role
            Virtual machine role in NETLAB+ inventory. :py:class:`netlab.enums.VirtualMachineRole`
        vm_runtime_vh_id
            Unique identifier of runtime virtual host.
        vm_sanity_checks
            Sanity checks.
        vm_shutdown_pref
            Shutdown preference.
        vm_snapshot
            Virtual machine snapshot name.
        vm_uuid
            Virtual machine unique identifier.
        vm_vendor_os_id
            Vendor operating system identifier.
        vm_vendor_os_name
            Vendor operating system name.
        """

        method = "pod.pc.get"

        res_param = {}
        if pod_id is not None:
            res_param['pod_id'] = pod_id
        if pl_index is not None:
            res_param['pl_index'] = pl_index
        if pc_id is not None:
            res_param['pc_id'] = pc_id

        res_param.update(kwargs)
        return self.call(method, **res_param)

    def pod_state_change(self, *, pod_id, state, **kwargs):
        """
        Sets pod state to either ONLINE, OFFLINE or RESUME.

        :param int pod_id: Pod ID
        :param PodState state: PC Pod State

        :return: The change succeeded.
        :rtype: 'OK'
        """

        method = "pod.state.change"

        res_param = {
            'pod_id': pod_id,
            'state': state,
        }

        res_param.update(kwargs)
        return self.call(method, **res_param)

    def pod_types_get(self, *, pt_id, properties="default", **kwargs):
        """
        Returns the build, id and name of installed pods on queried system.

        :param str pt_id: Pod Type ID is a alpha/num hash with prefix.
        :param properties: Properties consist of any combination of properties listed below under Properties.
                           You may also use **"all"** to include all properties, or **"default"** for a smaller subset
                           of properties.
        :type properties: list[str] or 'default' or 'all'

        :return: Returns all or selected properties from the list initially passed for return.
        :rtype: list

        **Properties**

        pt_id
            Pod type identifier.
        pt_name
            Account email.
        pt_build
            Account Family(Last) name.
        pt_gpdid
            Global unique identifer.
        pt_apdid
            Short unique identifier.
        pt_desc
            Pod type description.
        pt_index
            Pod type index.
        pt_type
            Pod type id
        sched_image
            Location of scheduler image.
        def_topology_image
            Location of the default topology image.
        def_im_name
            Default image map name.
        def_vlan_map
            Default VLAN map.
        pt_tabs
            Pod type tabs.
        pt_actions
            Pod type actions.
        pt_origin
            Pod type origin.
        pt_vlan_pool
            Pod type vlan pool.
        pt_pod_max
            Max amount of pod types allowed on system.
        pt_notes
            Pod type notes.
        pt_csw_port_count
            Number of consecutive ports on control switch required by pod type.
        pt_as_port_count
            Number of access server ports required by pod type.
        pt_apc_port_count
            Number of switched outlet ports required by pod type.
        pt_build
            Pod type build number.
        pt_author
            Pod type author.
        pt_org
            Pod type organization.
        pt_copyright
            Pod type copyright.
        pt_url
            Pod type URL address.
        pt_removable
            Allows pod type to be uninstalled.
        pt_ae_pod
            Pod type is for NETLAB+ AE.
        pt_adv_settings
            Pod type advanced settings.
        hotspots
            Returns array of hotspot information for **pt_id**.
        remote_pc_layout
            Returns array of remote PC layout information for **pt_id**.
        remote_pc_count
            Get a count of the remote PCs per pod type.
        device_layout
            Returns array of device layout information for **pt_id**.
        device_count
            Get count of the devices for pod type.
        pod_cat_values
            Returns array of pod categories.
        cables
            Returns array of cable connections for pod type.
        """

        method = "pod.types.get"

        if properties == 'all':
            properties = self.pod_types_props
        if properties == 'default':
            properties = self.pod_types_default_props

        res_param = {
            'pt_id': pt_id,
            'properties': properties
        }

        res_param.update(kwargs)
        return self.call(method, **res_param)

    def pod_types_list(self, *, properties="default", **kwargs):
        """
        Obtain a list of pod types.

        :param properties: Properties consist of any combination of properties listed below under Properties.
                           You may also use **"all"** to include all properties, or **"default"** for a smaller subset
                           of properties.
        :type properties: list[str] or 'default' or 'all'
        :return: On success, returns [rt_id, pt_build, pt_name] by default, or the following properties if requested.
        :rtype: list

        **Properties**

        pt_id
            Pod type identifier.
        pt_name
            Account email.
        pt_build
            Account Family(Last) name.
        pt_gpdid
            Global unique identifer.
        pt_apdid
            Short unique identifier.???
        pt_desc
            Pod type description.
        pt_index
            Pod type index.
        pt_type
            ???
        sched_image
            Location of scheduler image.
        def_topology_image
            Location of the default topology image.
        def_im_name
            Default name ???
        def_vlan_map
            Default VLAN map.
        pt_tabs
            Pod type tabs.
        pt_actions
            Pod type actions.
        pt_origin
            Pod type origin.
        pt_vlan_pool
            Pod type vlan pool.
        pt_pod_max
            Max amount of pod types allowed on system.
        pt_notes
            Pod type notes.
        pt_csw_port_count
            Number of consecutive ports on control switch required by pod type.
        pt_as_port_count
            Number of access server ports required by pod type.
        pt_apc_port_count
            Number of switched outlet ports required by pod type.
        pt_build
            Pod type build number.
        pt_author
            Pod type author.
        pt_org
            Pod type organization.
        pt_copyright
            Pod type copyright.
        pt_url
            Pod type URL address.
        pt_removable
            Allows pod type to be uninstalled.
        pt_ae_pod
            Is pod type for NETLAB+ AE???.
        pt_adv_settings
            Pod type advanced settings.
        remote_pc_count
            Get count of the remote PCs per pod type.
        device_count
            Get count of the devices per pod type.

        """
        method = "pod.types.list"

        if properties == 'all':
            properties = self.pod_types_props
        if properties == 'default':
            properties = self.pod_types_default_props

        return self.call(method, properties=properties, **kwargs)

    def pod_remove_task(self, *, pod_id, remove_vms=enums.RemoveVMS.NONE, severity_level=enums.HDRSeverity.WARN,
                        **kwargs):
        """
        :param int pod_id: Pod unique identifier.
        :param RemoveVMS remove_vms: Specifies the method for removing the virtual machines.
        :param HDRSeverity severity_level: Display detailed events for the task only at this severity level or
            higher.

        :return: HDR result
        :rtype: HDRStatus
        """

        method = "pod.remove.task"

        params = {
            'pod_id': pod_id,
            'remove_vms': remove_vms,
            'notify_progress': False,
            'notify_complete': False,
        }

        params.update(kwargs)
        results = self.call(method, **params)

        if version_gte(self._server_version, '17.1.3'):
            return hdr_result(results, severity_level)
        else:
            return results

    def pod_clone_task(self, *, source_pod_id, clone_pod_id, clone_pod_name, pc_clone_specs=None,
                       severity_level=enums.HDRSeverity.WARN, **kwargs):
        """
        Clone a pod.

        :param int source_pod_id: Source pod unique identifier.
        :param int clone_pod_id: Cloned pod unique identifier
        :param str clone_pod_name: Cloned unique pod name.
        :param pc_clone_specs: Array of PC clone specifications. Any unpassed params will be taken from the existing
            pods. If a dict is passed, the params will be used on every pod.
        :type pc_clone_specs: list or dict
        :param HDRSeverity severity_level: Display detailed events for the task only at this severity level or
            higher.
        :return: HDR result
        :rtype: HDRStatus

        **pc_clone_specs**

        The format requires a list of dictionaries.
        One dictionary per each PC in the pod, e.g. `[{pc.clone.spec.1}, {pc.clone.spec.2}]`.
        The following is a list of the keys, the expected value type and a key description.

        **PC Specs: (Obtained via remote_pc lookup - no need to adjust)**

        :pc_type: str : [AVMI, ABSENT]
        :source_snapshot: str : default(vm_snapshot)
        :clone_snapshot: str : default(vm_snapshot)
        :clone_type: str : default(LINKED) [LINKED, FULL]
        :clone_role: str : default(NORMAL) [MASTER, NORMAL, PERSISTENT]
        :clone_name: str : default({clone_pod_name}_{pc_label})
        :clone_storage_alloc: str : default(ONDEMAND) [ONDEMAND, PREALLOCATED]
        :clone_datastore: Datastore in which to clone.
        :clone_vh_id: int : RemotePC vh_id

        **Example:**

        ::

            pc_clone_specs = [{
                    "clone_role":"NORMAL",
                    "clone_type":"LINKED",
                    "source_snapshot":"GOLDEN_MASTER",
                    "pc_type":"AVMI",
                    "clone_vh_id":1,
                    "clone_name":"Test_Linux_Install_pod04_CentOS Server",
                    "clone_storage_alloc":"ONDEMAND",
                    "clone_datastore":"local",
                    "clone_snapshot":"GOLDEN_MASTER"
                }, {
                    "clone_role":"NORMAL",
                    "clone_type":"LINKED",
                    "source_snapshot":"GOLDEN_MASTER",
                    "pc_type":"AVMI",
                    "clone_vh_id":1,
                    "clone_name":"Test_Linux_Install_pod04_Ubuntu Workstation",
                    "clone_storage_alloc":"ONDEMAND",
                    "clone_datastore":"local",
                    "clone_snapshot":"GOLDEN_MASTER"
                }]

        """

        method = "pod.clone.task"
        remote_pc = self.pod_get(pod_id=source_pod_id, properties=['remote_pc'])

        # Transferring to internal pc_clone_specs builder.
        pc_clone_specs = self.__pc_clone_specs(clone_pod_name=clone_pod_name,
                                               user_pc_clone_specs=pc_clone_specs,
                                               remote_pc_list=remote_pc['remote_pc'])

        results = self.call(method, source_pod_id=source_pod_id,
                            clone_pod_id=clone_pod_id, clone_pod_name=clone_pod_name,
                            pc_clone_specs=pc_clone_specs, **kwargs)

        if version_gte(self._server_version, '17.1.4'):
            return hdr_result(results, severity_level)
        else:
            return results

    def pod_acl_add(self, *, com_id, pod_id, acc_id=None, cls_id=None, team=None, **kwargs):
        """
        This method allows you to add access controls to a pod on the NETLAB+ system.

        :param int com_id: Community identifier.
        :param int pod_id: Pod identifier.
        :param int acc_id: Account identifier.
        :param int cls_id: Class identifier.
        :param str team: Team assignment.
        :return: Pod access control list UUID.
        :rtype: str
        """

        args = {
            'com_id': com_id,
            'pod_id': pod_id,
            'acc_id': acc_id,
            'cls_id': cls_id,
            'team': team,
        }
        args.update(kwargs)
        return self.call('pod.acl.add', **args)

    def pod_acl_admin_add(self, *, acc_id, pod_id, **kwargs):
        """
        This method allows you add additional pod access control administrators.

        :param int acc_id: Account identifier.
        :param int pod_id: Pod identifier.
        :return: acc_id was authorized for pod_id.
        :rtype: 'OK'
        """

        return self.call('pod.acl.admin.add', acc_id=acc_id, pod_id=pod_id, **kwargs)

    def pod_acl_admin_auth(self, *, acc_id, pod_id, **kwargs):
        """
        This method returns true if a specified instructor account is authorized to manage POD
        ACLs on a specified pod.

        :param int acc_id: Account identifier.
        :param int pod_id: Pod identifier.

        :return: acc_id is authorized for pod_id.
        :rtype: bool
        """

        return bool(int(self.call('pod.acl.admin.auth', acc_id=acc_id, pod_id=pod_id, **kwargs)))

    def pod_acl_admin_list(self, *, pod_id, **kwargs):
        """
        This method allows you to retrieve a list of pod access controls per pod.

        :param int pod_id: Pod identifier.
        :return: Pod access control list.
        :rtype: list
        """

        return self.call('pod.acl.admin.list', pod_id=pod_id, **kwargs)

    def pod_acl_admin_pods(self, *,  acc_id, **kwargs):
        """
        This method allows you to retrieve a list of pods a user has access to.

        :param int acc_id: Account identifier.
        :return: list
        """

        return self.call('pod.acl.admin.pods', acc_id=acc_id, **kwargs)

    def pod_acl_admin_remove(self, *, acc_id, pod_id, **kwargs):
        """
        This method allows you to remove authorization for an account.

        :param int acc_id: Account identifier.
        :param int pod_id: Pod identifier.
        :return: Pod authorization was successfully removed.
        :rtype: 'OK'
        """

        return self.call('pod.acl.admin.remove', acc_id=acc_id, pod_id=pod_id, **kwargs)

    def pod_acl_challenge(self, *, com_id, pod_id, acc_id=None, cls_id=None, team=None, **kwargs):
        """
        This method allows you to test against a pod access control list.

        :param int com_id: Community identifier.
        :param int pod_id: Pod identifier.
        :param int acc_id: Account identifier.
        :param int cls_id: Class identifier.
        :param str team: Team assignment.
        :return: If matching rule found, pod access control list UUID.
        :rtype: uuid
        """

        return self.call('pod.acl.challenge', com_id=com_id, pod_id=pod_id, acc_id=acc_id, cls_id=cls_id,
                         team=team, **kwargs)

    def pod_acl_get(self, *, pacl_uuid, **kwargs):
        """
        This method allows you to retrieve a pod access control list.

        :param int pacl_uuid: Pod access control list UUID.
        :return: Pod access control list.
        :rtype: list
        """

        return self.call('pod.acl.get', pacl_uuid=pacl_uuid, **kwargs)

    def pod_acl_list(self, *, pod_id, **kwargs):
        """
        This method allows you to retrieve a list of pod access controls per pod.

        :param int pod_id: Pod identifier.
        :return: Pod access control list.
        :rtype: list
        """

        return self.call('pod.acl.list', pod_id=pod_id, **kwargs)

    def pod_acl_remove(self, *, pacl_uuid, **kwargs):
        """
        This method allows you to remove a pod access control list.

        :param int pacl_uuid: Pod access control list UUID.
        :return: Pod was successfully removed.
        :rtype: 'OK'
        """

        return self.call('pod.acl.remove', pacl_uuid=pacl_uuid, **kwargs)
