Example #1
0
    def __init__(self,
                 ip_address,
                 username='******',
                 password=None,
                 key=None,
                 connection_timeout=600,
                 retry_interval=10):
        self.client_log = cclogging.getLogger(
            cclogging.get_object_namespace(self.__class__))

        # Verify the IP address has a valid format
        try:
            IP(ip_address)
        except ValueError:
            raise InvalidAddressFormat(ip_address)

        # Verify the server can be pinged before attempting to connect
        PingClient.ping_until_reachable(ip_address,
                                        timeout=connection_timeout,
                                        interval_time=retry_interval)

        self.ip_address = ip_address
        self.username = username
        self.password = password

        self.client = WinRMClient(username=username,
                                  password=password,
                                  host=ip_address)
        connected = self.client.connect_with_retries()
        if not connected:
            raise WinRMConnectionException(ip_address=ip_address)
    def __init__(self, ip_address, username='******',
                 password=None, key=None, connection_timeout=600,
                 retry_interval=10):
        self.client_log = cclogging.getLogger(
            cclogging.get_object_namespace(self.__class__))

        # Verify the IP address has a valid format
        try:
            IP(ip_address)
        except ValueError:
            raise InvalidAddressFormat(ip_address)

        # Verify the server can be pinged before attempting to connect
        PingClient.ping_until_reachable(ip_address,
                                        timeout=connection_timeout,
                                        interval_time=retry_interval)

        self.ip_address = ip_address
        self.username = username
        self.password = password

        self.client = WinRMClient(
            username=username, password=password, host=ip_address)
        connected = self.client.connect_with_retries()
        if not connected:
            raise WinRMConnectionException(ip_address=ip_address)
Example #3
0
class WindowsClient(RemoteInstanceClient):

    DEFAULT_XEN_CLIENT_PATH = 'C:\\Program Files\\Citrix\\XenTools'

    def __init__(self,
                 ip_address,
                 username='******',
                 password=None,
                 key=None,
                 connection_timeout=600,
                 retry_interval=10):
        self.client_log = cclogging.getLogger(
            cclogging.get_object_namespace(self.__class__))

        # Verify the IP address has a valid format
        try:
            IP(ip_address)
        except ValueError:
            raise InvalidAddressFormat(ip_address)

        # Verify the server can be pinged before attempting to connect
        PingClient.ping_until_reachable(ip_address,
                                        timeout=connection_timeout,
                                        interval_time=retry_interval)

        self.ip_address = ip_address
        self.username = username
        self.password = password

        self.client = WinRMClient(username=username,
                                  password=password,
                                  host=ip_address)
        connected = self.client.connect_with_retries()
        if not connected:
            raise WinRMConnectionException(ip_address=ip_address)

    def can_authenticate(self):
        """
        Verifies that a connection was made to the remote server

        @return: Whether the connection was successful
        @rtype: bool
        """

        return self.client.is_connected()

    def get_hostname(self):
        """
        Gets the host name of the server

        @return: The host name of the server
        @rtype: string
        """

        output = self.client.execute_command('hostname')
        if output.std_out:
            return output.std_out.strip('\r\n').lower()

    def get_allocated_ram(self):
        """
        Returns the amount of RAM the server has

        @return: The RAM size in MB
        @rtype: string
        """

        command = ('powershell gwmi Win32_ComputerSystem '
                   '-Property TotalPhysicalMemory')
        output = self.client.execute_command(command)
        if not output.std_out:
            return None
        system_info = self._convert_powershell_list_to_dict(output.std_out)
        return int(system_info.get('TotalPhysicalMemory', 0)) / (1024 * 1024)

    def get_disk_size(self, disk_path):
        """
        Returns the size of a given disk

        @return: The disk size in GB
        @rtype: int
        """

        disks = self.get_all_disks()
        return disks.get(disk_path)

    def get_number_of_cpus(self):
        """
        Return the number of CPUs assigned to the server

        @return: The number of CPUs a server has
        @rtype: int
        """
        command = ('powershell gwmi Win32_ComputerSystem '
                   '-Property NumberOfLogicalProcessors')
        output = self.client.execute_command(command)
        if not output.std_out:
            return None
        cpu_info = self._convert_powershell_list_to_dict(output.std_out)
        return int(cpu_info.get('NumberOfLogicalProcessors', 0))

    def get_uptime(self):
        """
        Get the uptime time of the server.

        @return: The uptime of the server in seconds
        @rtype: int
        """
        output = self.client.execute_command(
            'powershell [Management.ManagementDateTimeConverter]::'
            'ToDateTime((gwmi Win32_OperatingSystem).'
            'LastBootUpTime)')
        if not output.std_out:
            return None
        output = output.std_out.strip()
        last_boot = parse(output)
        now = self._get_system_current_datetime()
        diff = now - last_boot
        return diff.total_seconds()

    def get_local_users(self):
        """
        Get a list of the local user accounts on the server

        @return: A list of users
        @rtype: List of strings
        """

        command = ('powershell gwmi Win32_UserAccount')
        output = self.client.execute_command(command)
        if not output:
            return None
        raw_output = output.std_out.split('\r\n\r\n')
        raw_users = [user for user in raw_output if user]
        user_list = []
        for user in raw_users:
            user_info = self._convert_powershell_list_to_dict(user)
            user_list.append(user_info.get('Name'))
        return user_list

    def create_file(self, file_name, file_content, file_path):
        """
        Creates a new file with the provided content.

        @param file_name: File name
        @type file_name: string
        @param file_content: File content
        @type file_content: String
        @rtype: FileDetails
        """
        self.client.execute_command(
            'echo {file_content} >> {file_path}\\{file_name}'.format(
                file_content=file_content,
                file_path=file_path,
                file_name=file_name))
        return FileDetails(None, file_content, file_path)

    def get_filesystem_permissions(self, path):
        """
        Returns a list of users with access to a file or directory.

        @param path: Path to the file or directory
        @type path: string
        @return: A list of users
        @rtype: List of strings
        """

        command = ('powershell "&{{ (Get-Acl {path}).Access | ForEach-Object '
                   '{{$_.IdentityReference.ToString() }} }}"'.format(
                       path=path))
        output = self.client.execute_command(command).std_out
        return output.splitlines() if output else None

    def get_md5sum_for_remote_file(self, file_location, file_name):
        """
        @summary: Gets the md5sum of file on the server
        """
        if not file_location.endswith("\\"):
            file_location = file_location + "\\"
        command = ('powershell "Get-Content {file_location}{file_name} | '
                   'Get-FileHash -Algorithm MD5"'.format(
                       file_location=file_location, file_name=file_name))

        response = self.client.execute_command(command)
        if not response.std_out:
            raise Exception("Unable to execute remote file md5 hash")

        stdout = response.std_out.strip()
        lines = stdout.splitlines()
        try:
            data_line = lines[2]
        except:
            raise Exception(
                "Unexpected response format from remote file md5 hash")

        try:
            match = re.match("^MD5\s*(\S*)", data_line)
            groups = match.groups()
            return groups[0]
        except:
            raise Exception(
                "Unable to find md5 hash in remote file md5 hash response")

    def get_file_details(self, file_path):
        """
        Retrieves the contents of a file and its permissions.

        @param file_path: Path to the file
        @type file_path: string
        @return: File details including permissions and content
        @rtype: FileDetails
        """

        file_permissions = self.get_filesystem_permissions(path=file_path)

        file_contents = self.client.execute_command(
            'type {file_path}'.format(file_path=file_path)).std_out
        return FileDetails(file_permissions, file_contents.rstrip("\n"),
                           file_path)

    def is_file_present(self, file_path):
        """
        Verifies if the given file is present.

        @param file_path: Path to the file
        @type file_path: string
        @return: True if File exists, False otherwise
        @rtype: bool
        """

        cmd = '(if exist {file_path} (echo True) else (echo False))'.format(
            file_path=file_path)
        output = self.client.execute_command(cmd)
        if not output.std_out:
            return None
        file_exists = output.std_out
        return file_exists.rstrip('\n').strip().lower() == "true"

    def mount_disk(self, source_path, destination_path):
        """
        Mounts a disk to specified destination.

        @param source_path: Path to file source
        @type source_path: string
        @param destination_path: Path to mount destination
        @type destination_path: string
        """

        command = ('powershell Set-Partition -DiskNumber '
                   '{disk} -PartitionNumber 2 '
                   '-NewDriveLetter {drive}').format(disk=source_path,
                                                     drive=destination_path)
        output = self.client.execute_command(command)
        return output.std_out

    def unmount_disk(self, disk_path):
        command = (
            'powershell Set-Disk -Number {disk_path} -IsOffline $true'.format(
                disk_path=disk_path))
        return self.client.execute_command(command)

    def get_xen_user_metadata(self, xen_client_path=DEFAULT_XEN_CLIENT_PATH):
        """
        Retrieves the user-metadata section from the XenStore.

        @return: The contents of the user-metadata
        @rtype: dict
        """

        client = '{path}\\xenstore_client.exe'.format(path=xen_client_path)

        # Check if there is user metadata set
        command = '"{client}" dir vm-data'.format(client=client)
        output = self.client.execute_command(command)
        if not output.std_out:
            return {}

        # If user-metadata is not one of the directories returned,
        # then there is no metadata
        meta_dirs = output.std_out.splitlines()
        if 'user-metadata' not in meta_dirs:
            return {}

        command = '"{client}" dir vm-data/user-metadata'.format(client=client)
        output = self.client.execute_command(command)
        if output.std_out:
            keys = output.std_out.splitlines()
            metadata = {}
            for key in keys:
                cmd = '"{client}" read vm-data/user-metadata/{key}'.format(
                    client=client, key=key)
                output = self.client.execute_command(cmd)
                if output.std_out:
                    metadata[key] = output.std_out.replace('"', '')
            return metadata
        return {}

    def get_xenstore_disk_config_value(self,
                                       xen_client_path=DEFAULT_XEN_CLIENT_PATH
                                       ):
        """
        Returns the XenStore value for disk config.

        @return: Whether the virtual machine uses auto disk config
        @rtype: bool
        """

        client = '{path}\\xenstore_client.exe'.format(path=xen_client_path)
        command = '"{client}" read vm-data/auto-disk-config'.format(
            client=client)

        output = self.client.execute_command(command)
        if output.std_out:
            return output.std_out.lower() == 'true'

    def create_directory(self, path):
        """
        Creates a directory at the specified path.

        @param path: Directory path
        @type path: string
        """

        command = ('powershell New-Item -ItemType directory '
                   '-Path {path}'.format(path=path))
        output = self.client.execute_command(command)
        return output.std_out if output.std_out else None

    def is_directory_present(self, directory_path):
        """
        Check if given directory exists.

        @param directory_path: Path to the directory
        @type directory_path: string
        @return: Result of directory check
        @rtype: bool
        """

        command = 'powershell Test-Path {directory_path}'.format(
            directory_path=directory_path)
        output = self.client.execute_command(command)

        if not output:
            return False
        return output.std_out.strip().lower() == 'true'

    def get_directory_details(self, dir_path):
        """
        Retrieves informational data about a directory.

        @param dir_path: Path to the directory
        @type dir_path: string
        @return: Directory details
        @rtype: DirectoryDetails
        """
        permissions = self.get_filesystem_permissions(dir_path)
        size = self._get_directory_size(dir_path)
        return DirectoryDetails(name=dir_path,
                                size=size,
                                absolute_permissions=permissions)

    def get_all_disks(self):
        """
        Returns a list of all block devices for a server.

        @return: The accessible block devices
        @rtype: dict
        """
        command = 'powershell "&{ Get-Disk | Format-List }"'
        output = self.client.execute_command(command)
        if not output.std_out:
            return None
        raw_output = output.std_out.split('\r\n\r\n')
        raw_disks = [disk for disk in raw_output if disk]

        disks = {}
        for disk in raw_disks:
            disk_info = self._convert_powershell_list_to_dict(disk)
            disks[disk_info['Number']] = int(disk_info['Size'].split()[0])
        return disks

    def get_all_disk_details(self):
        """
        Returns all details for all block devices for a server.

        @return: The accessible block devices
        @rtype: dict
        """
        command = 'powershell "&{ Get-Disk | Format-List }"'
        output = self.client.execute_command(command)
        if not output.std_out:
            return None
        raw_output = output.std_out.split('\r\n\r\n')
        raw_disks = [disk for disk in raw_output if disk]

        disks = []
        for disk in raw_disks:
            disk_info = self._convert_powershell_list_to_dict(disk)
            disks.append(disk_info)

        return disks

    def format_disk(self, disk, filesystem_type):
        """
        Formats a disk to the provided filesystem type.

        @param disk: The path to the disk to be formatted
        @type disk: string
        @param filesystem_type: The filesystem type to format the disk to
        @type filesystem_type: string

        @return: Output of command execution
        @rtype: string
        """
        command = ('powershell Set-Disk -Number {disk} '
                   '-IsOffline $false').format(disk=disk)
        self.client.execute_command(command)

        command = ('powershell Set-Disk -Number {disk} '
                   '-IsReadOnly $false').format(disk=disk)
        self.client.execute_command(command)

        command = ('powershell Clear-Disk -Number {disk} '
                   '-RemoveData -Confirm:$false').format(disk=disk)
        self.client.execute_command(command)

        command = 'powershell Initialize-Disk -Number {disk}'.format(disk=disk)
        self.client.execute_command(command)

        command = ('powershell "&{{ New-Partition -DiskNumber {disk} '
                   '-UseMaximumSize | Format-Volume -FileSystem {disk_type} '
                   '-Confirm:$false }}').format(disk=disk,
                                                disk_type=filesystem_type)
        output = self.client.execute_command(command)
        return output.std_out

    def get_disk_fstype(self, drive_letter):
        command = ('powershell "Get-Volume -DriveLetter {0} | '
                   'Select -ExpandProperty FileSystem"'.format(drive_letter))
        output = self.client.execute_command(command)
        return output.std_out

    def generate_mountpoint(self):
        """ Returns the next available drive letter """
        command = ('powershell "ls function:[c-z]: -n | ?{!(test-path $_)} '
                   '| select -First 1"')

        output = self.client.execute_command(command)
        return output.std_out[0]

    def get_distribution_and_version(self):
        """
        Gets the distribution and version of a server
        @return: Full name of the distribution
        @rtype: str
        """
        output = self.client.execute_command(
            'powershell gwmi win32_operatingsystem |% caption')
        if output.std_out:
            return output.std_out
        else:
            return ''

    def filesystem_sync(self):
        # This works as a non-powershell command, but it may not
        # be installed on all versions of windows.
        self.client.execute_command('sync')

    # gwmi

    @staticmethod
    def _convert_powershell_list_to_dict(response):
        data = {}
        for line in response.splitlines():
            if line and ':' in line:
                key, value = line.split(':', 1)
                if key is not None:
                    key = key.strip()
                    value = value.strip() if value else None
                    data[key] = value
                else:
                    continue
        return data

    def _get_directory_size(self, path):
        """
        Returns of the size all files under a directory

        @param path: Path to the directory
        @type path: string
        @return: The size of all files in bytes
        @rtype: FileDetails
        """

        command = ('powershell "&{{ (Get-ChildItem {path} -recurse | '
                   'Measure-Object -property length -sum).Sum }}"').format(
                       path=path)
        output = self.client.execute_command(command)
        if not output.std_out:
            return 0

        size_in_bytes = int(output.std_out) / 8.0
        return size_in_bytes

    def _get_system_current_datetime(self):
        """
        Get the current time from the server.

        @return: The current time for the server
        @rtype: DateTime
        """

        command = 'powershell Get-Date'
        output = self.client.execute_command(command)
        if not output.std_out:
            return None
        return parse(output.std_out)
class WindowsClient(RemoteInstanceClient):

    DEFAULT_XEN_CLIENT_PATH = 'C:\\Program Files\\Citrix\\XenTools'

    def __init__(self, ip_address, username='******',
                 password=None, key=None, connection_timeout=600,
                 retry_interval=10):
        self.client_log = cclogging.getLogger(
            cclogging.get_object_namespace(self.__class__))

        # Verify the IP address has a valid format
        try:
            IP(ip_address)
        except ValueError:
            raise InvalidAddressFormat(ip_address)

        # Verify the server can be pinged before attempting to connect
        PingClient.ping_until_reachable(ip_address,
                                        timeout=connection_timeout,
                                        interval_time=retry_interval)

        self.ip_address = ip_address
        self.username = username
        self.password = password

        self.client = WinRMClient(
            username=username, password=password, host=ip_address)
        connected = self.client.connect_with_retries()
        if not connected:
            raise WinRMConnectionException(ip_address=ip_address)

    def can_authenticate(self):
        """
        Verifies that a connection was made to the remote server

        @return: Whether the connection was successful
        @rtype: bool
        """

        return self.client.is_connected()

    def get_hostname(self):
        """
        Gets the host name of the server

        @return: The host name of the server
        @rtype: string
        """

        output = self.client.execute_command('hostname')
        if output.std_out:
            return output.std_out.strip('\r\n').lower()

    def get_allocated_ram(self):
        """
        Returns the amount of RAM the server has

        @return: The RAM size in MB
        @rtype: string
        """

        command = ('powershell gwmi Win32_ComputerSystem '
                   '-Property TotalPhysicalMemory')
        output = self.client.execute_command(command)
        if not output.std_out:
            return None
        system_info = self._convert_powershell_list_to_dict(output.std_out)
        return int(system_info.get('TotalPhysicalMemory', 0))/(1024 * 1024)

    def get_disk_size(self, disk_path):
        """
        Returns the size of a given disk

        @return: The disk size in GB
        @rtype: int
        """

        disks = self.get_all_disks()
        return disks.get(disk_path)

    def get_number_of_cpus(self):
        """
        Return the number of CPUs assigned to the server

        @return: The number of CPUs a server has
        @rtype: int
        """
        command = ('powershell gwmi Win32_ComputerSystem '
                   '-Property NumberOfLogicalProcessors')
        output = self.client.execute_command(command)
        if not output.std_out:
            return None
        cpu_info = self._convert_powershell_list_to_dict(output.std_out)
        return int(cpu_info.get('NumberOfLogicalProcessors', 0))

    def get_uptime(self):
        """
        Get the uptime time of the server.

        @return: The uptime of the server in seconds
        @rtype: int
        """
        output = self.client.execute_command(
            'powershell [Management.ManagementDateTimeConverter]::'
            'ToDateTime((gwmi Win32_OperatingSystem).'
            'LastBootUpTime)')
        if not output.std_out:
            return None
        output = output.std_out.strip()
        last_boot = parse(output)
        now = self._get_system_current_datetime()
        diff = now - last_boot
        return diff.total_seconds()

    def get_local_users(self):
        """
        Get a list of the local user accounts on the server

        @return: A list of users
        @rtype: List of strings
        """

        command = ('powershell gwmi Win32_UserAccount')
        output = self.client.execute_command(command)
        if not output:
            return None
        raw_output = output.std_out.split('\r\n\r\n')
        raw_users = [user for user in raw_output if user]
        user_list = []
        for user in raw_users:
            user_info = self._convert_powershell_list_to_dict(user)
            user_list.append(user_info.get('Name'))
        return user_list

    def create_file(self, file_name, file_content, file_path):
        """
        Creates a new file with the provided content.

        @param file_name: File name
        @type file_name: string
        @param file_content: File content
        @type file_content: String
        @rtype: FileDetails
        """
        self.client.execute_command(
            'echo {file_content} >> {file_path}\\{file_name}'.format(
                file_content=file_content, file_path=file_path,
                file_name=file_name))
        return FileDetails(None, file_content, file_path)

    def get_filesystem_permissions(self, path):
        """
        Returns a list of users with access to a file or directory.

        @param path: Path to the file or directory
        @type path: string
        @return: A list of users
        @rtype: List of strings
        """

        command = (
            'powershell "&{{ (Get-Acl {path}).Access | ForEach-Object '
            '{{$_.IdentityReference.ToString() }} }}"'.format(path=path))
        output = self.client.execute_command(command).std_out
        return output.splitlines() if output else None

    def get_md5sum_for_remote_file(self, file_location, file_name):
        """
        @summary: Gets the md5sum of file on the server
        @param filepath: The path name including file name
        @type filepath: String
        """
        if not file_location.endswith("\\"):
            file_location = file_location + "\\"
        command = (
            'powershell "Get-Content {file_location}{file_name} | '
            'Get-FileHash -Algorithm MD5"'.format(
                file_location=file_location, file_name=file_name))

        response = self.client.execute_command(command)
        if not response.std_out:
            raise Exception("Unable to execute remote file md5 hash")

        stdout = response.std_out.strip()
        lines = stdout.splitlines()
        try:
            data_line = lines[2]
        except:
            raise Exception(
                "Unexpected response format from remote file md5 hash")

        try:
            match = re.match("^MD5\s*(\S*)", data_line)
            groups = match.groups()
            return groups[0]
        except:
            raise Exception(
                "Unable to find md5 hash in remote file md5 hash response")

    def get_file_details(self, file_path):
        """
        Retrieves the contents of a file and its permissions.

        @param file_path: Path to the file
        @type file_path: string
        @return: File details including permissions and content
        @rtype: FileDetails
        """

        file_permissions = self.get_filesystem_permissions(path=file_path)

        file_contents = self.client.execute_command(
            'type {file_path}'.format(file_path=file_path)).std_out
        return FileDetails(
            file_permissions, file_contents.rstrip("\n"), file_path)

    def is_file_present(self, file_path):
        """
        Verifies if the given file is present.

        @param file_path: Path to the file
        @type file_path: string
        @return: True if File exists, False otherwise
        @rtype: bool
        """

        cmd = '(if exist {file_path} (echo True) else (echo False))'.format(
            file_path=file_path)
        output = self.client.execute_command(cmd)
        if not output.std_out:
            return None
        file_exists = output.std_out
        return file_exists.rstrip('\n').strip().lower() == "true"

    def mount_disk(self, source_path, destination_path):
        """
        Mounts a disk to specified destination.

        @param source_path: Path to file source
        @type source_path: string
        @param destination_path: Path to mount destination
        @type destination_path: string
        """

        command = (
            'powershell Set-Partition -DiskNumber '
            '{disk} -PartitionNumber 2 '
            '-NewDriveLetter {drive}').format(disk=source_path,
                                              drive=destination_path)
        output = self.client.execute_command(command)
        return output.std_out

    def unmount_disk(self, disk_path):
        command = (
            'powershell Set-Disk -Number {disk_path} -IsOffline $true'.format(
                disk_path=disk_path))
        return self.client.execute_command(command)

    def get_xen_user_metadata(self, xen_client_path=DEFAULT_XEN_CLIENT_PATH):
        """
        Retrieves the user-metadata section from the XenStore.

        @return: The contents of the user-metadata
        @rtype: dict
        """

        client = '{path}\\xenstore_client.exe'.format(
            path=xen_client_path)

        # Check if there is user metadata set
        command = '"{client}" dir vm-data'.format(client=client)
        output = self.client.execute_command(command)
        if not output.std_out:
            return {}

        # If user-metadata is not one of the directories returned,
        # then there is no metadata
        meta_dirs = output.std_out.splitlines()
        if 'user-metadata' not in meta_dirs:
            return {}

        command = '"{client}" dir vm-data/user-metadata'.format(client=client)
        output = self.client.execute_command(command)
        if output.std_out:
            keys = output.std_out.splitlines()
            metadata = {}
            for key in keys:
                cmd = '"{client}" read vm-data/user-metadata/{key}'.format(
                    client=client, key=key)
                output = self.client.execute_command(cmd)
                if output.std_out:
                    metadata[key] = output.std_out.replace('"', '')
            return metadata
        return {}

    def get_xenstore_disk_config_value(
            self, xen_client_path=DEFAULT_XEN_CLIENT_PATH):
        """
        Returns the XenStore value for disk config.

        @return: Whether the virtual machine uses auto disk config
        @rtype: bool
        """

        client = '{path}\\xenstore_client.exe'.format(
            path=xen_client_path)
        command = '"{client}" read vm-data/auto-disk-config'.format(
            client=client)

        output = self.client.execute_command(command)
        if output.std_out:
            return output.std_out.lower() == 'true'

    def create_directory(self, path):
        """
        Creates a directory at the specified path.

        @param path: Directory path
        @type path: string
        """

        command = (
            'powershell New-Item -ItemType directory '
            '-Path {path}'.format(path=path))
        output = self.client.execute_command(command)
        return output.std_out if output.std_out else None

    def is_directory_present(self, directory_path):
        """
        Check if given directory exists.

        @param directory_path: Path to the directory
        @type directory_path: string
        @return: Result of directory check
        @rtype: bool
        """

        command = 'powershell Test-Path {directory_path}'.format(
            directory_path=directory_path)
        output = self.client.execute_command(command)

        if not output:
            return False
        return output.std_out.strip().lower() == 'true'

    def get_directory_details(self, dir_path):
        """
        Retrieves informational data about a directory.

        @param dir_path: Path to the directory
        @type dir_path: string
        @return: Directory details
        @rtype: DirectoryDetails
        """
        permissions = self.get_filesystem_permissions(dir_path)
        size = self._get_directory_size(dir_path)
        return DirectoryDetails(
            name=dir_path, size=size, absolute_permissions=permissions)

    def get_all_disks(self):
        """
        Returns a list of all block devices for a server.

        @return: The accessible block devices
        @rtype: dict
        """
        command = 'powershell "&{ Get-Disk | Format-List }"'
        output = self.client.execute_command(command)
        if not output.std_out:
            return None
        raw_output = output.std_out.split('\r\n\r\n')
        raw_disks = [disk for disk in raw_output if disk]

        disks = {}
        for disk in raw_disks:
            disk_info = self._convert_powershell_list_to_dict(disk)
            disks[disk_info['Number']] = int(disk_info['Size'].split()[0])
        return disks

    def get_all_disk_details(self):
        """
        Returns all details for all block devices for a server.

        @return: The accessible block devices
        @rtype: dict
        """
        command = 'powershell "&{ Get-Disk | Format-List }"'
        output = self.client.execute_command(command)
        if not output.std_out:
            return None
        raw_output = output.std_out.split('\r\n\r\n')
        raw_disks = [disk for disk in raw_output if disk]

        disks = []
        for disk in raw_disks:
            disk_info = self._convert_powershell_list_to_dict(disk)
            disks.append(disk_info)

        return disks

    def format_disk(self, disk, filesystem_type):
        """
        Formats a disk to the provided filesystem type.

        @param disk: The path to the disk to be formatted
        @type disk: string
        @param filesystem_type: The filesystem type to format the disk to
        @type filesystem_type: string

        @return: Output of command execution
        @rtype: string
        """
        command = (
            'powershell Set-Disk -Number {disk} '
            '-IsOffline $false').format(disk=disk)
        self.client.execute_command(command)

        command = (
            'powershell Set-Disk -Number {disk} '
            '-IsReadOnly $false').format(disk=disk)
        self.client.execute_command(command)

        command = (
            'powershell Clear-Disk -Number {disk} '
            '-RemoveData -Confirm:$false').format(disk=disk)
        self.client.execute_command(command)

        command = 'powershell Initialize-Disk -Number {disk}'.format(disk=disk)
        self.client.execute_command(command)

        command = ('powershell "&{{ New-Partition -DiskNumber {disk} '
                   '-UseMaximumSize | Format-Volume -FileSystem {disk_type} '
                   '-Confirm:$false }}').format(disk=disk,
                                                disk_type=filesystem_type)
        output = self.client.execute_command(command)
        return output.std_out

    def get_disk_fstype(self, drive_letter):
        command = (
            'powershell "Get-Volume -DriveLetter {0} | '
            'Select -ExpandProperty FileSystem"'.format(drive_letter))
        output = self.client.execute_command(command)
        return output.std_out

    def generate_mountpoint(self):
        """ Returns the next available drive letter """
        command = (
            'powershell "ls function:[c-z]: -n | ?{!(test-path $_)} '
            '| select -First 1"')

        output = self.client.execute_command(command)
        return output.std_out[0]

    def get_distribution_and_version(self):
        """
        Gets the distribution and version of a server
        @return: Full name of the distribution
        @rtype: str
        """
        output = self.client.execute_command(
            'powershell gwmi win32_operatingsystem |% caption')
        if output.std_out:
            return output.std_out
        else:
            return ''

    def filesystem_sync(self):
        # This works as a non-powershell command, but it may not
        # be installed on all versions of windows.
        self.client.execute_command('sync')

    # gwmi

    @staticmethod
    def _convert_powershell_list_to_dict(response):
        data = {}
        for line in response.splitlines():
            if line and ':' in line:
                key, value = line.split(':', 1)
                if key is not None:
                    key = key.strip()
                    value = value.strip() if value else None
                    data[key] = value
                else:
                    continue
        return data

    def _get_directory_size(self, path):
        """
        Returns of the size all files under a directory

        @param path: Path to the directory
        @type path: string
        @return: The size of all files in bytes
        @rtype: FileDetails
        """

        command = (
            'powershell "&{{ (Get-ChildItem {path} -recurse | '
            'Measure-Object -property length -sum).Sum }}"').format(path=path)
        output = self.client.execute_command(command)
        if not output.std_out:
            return 0

        size_in_bytes = int(output.std_out)/8.0
        return size_in_bytes

    def _get_system_current_datetime(self):
        """
        Get the current time from the server.

        @return: The current time for the server
        @rtype: DateTime
        """

        command = 'powershell Get-Date'
        output = self.client.execute_command(command)
        if not output.std_out:
            return None
        return parse(output.std_out)