コード例 #1
0
    def getvendor(self):
        """
        """
        vendor = None

        for commandset in self.commandsets:
            if not (hasstring(commandset['command']) and
                    (hasstring(commandset['result'])
                     or haslist(commandset['result']))):
                continue

            lines = self.command(commandset['command'])

            if hasstring(commandset['result']):
                for line in lines:
                    if line == commandset['result']:
                        vendor = commandset['vendor']
                        break

            elif haslist(commandset['result']):
                for line in lines:
                    if line in commandset['result']:
                        vendor = commandset['vendor']
                        break

            if vendor:
                break

        if not vendor:
            vendor = 'unknown'

        return vendor
コード例 #2
0
    def removeentry(self, branch, find=''):
        """Removes entries from remote host based onto <find>. If <find> is
        note set, it will remove everything under that <branch>. That method
        compares the configuration before and after command execution to detect
        changes and form the bool result.

        :param branch: (str) Branch of commands.
        :param find: (str) Mikrotik CLI filter.
        :return: (bool) True when <propvals> have changed the configuration. If
            for some reason, configuration remains unchanged False will be
            returned, but the <self.errc> will be zero. It will also return
            False in case of error.
        """
        if not hasstring(branch):
            return self.err(1)

        branch = branchfix(branch)

        if not haskey(self.branch, branch, dict):
            return self.err(2, branch)

        if self.branch[branch]['class'] != 'list':
            return False

        if self.branch[branch]['readonly']:
            return False

        if not hasstring(find):
            find = ''

        # Count entries before remove command
        command = ':put [:len [{} find]]'.format(branch)
        entries_c0 = self.command(command)
        if not entries_c0:
            return self.err(3)

        # Remove command
        command = '{} remove [find {}]'.format(branch, find)
        results = self.command(command, hasstdout=False)
        if results:
            return self.err(4, results)

        # Count entries after remove command
        command = ':put [:len [{} find]]'.format(branch)
        entries_c1 = self.command(command)
        if not entries_c1:
            return self.err(5)

        # Compare Before and After remove command
        if entries_c0 != entries_c1:
            return True  # Changed
        return False  # Not changed != Failed
コード例 #3
0
    def upload_file(self, local, remote):
        """Uploads local files to remote host.

        :param local: (str) Local path.
        :param remote: (str) Remote path.
        :return: (bool) True on success, False on failure.
        """
        if not isfile(local):
            return self.err(1, local)

        if not hasstring(remote):
            return self.err(2, remote)

        if self.isdir_remote(remote) and remote[-1] != '/':
            remote += '/'

        if remote[-1] == '/':
            remote += local.split('/')[-1]

        if not self.isdir_remote('/'.join(remote.split('/')[:-1]), True):
            return self.err(3, remote)

        try:
            self.connection.put(local, remote)
            return True

        except Exception:
            _, message = getexcept()
            return self.err(4, message)
コード例 #4
0
    def download_file(self, remote, local):
        """Downloads remote files to local path.

        :param remote: (str) Remote path.
        :param local: (str) Local path.
        :return: (bool) True on success, False on failure.
        """
        if not self.isfile_remote(remote):
            return self.err(1, remote)

        if not hasstring(local):
            return self.err(2, local)

        if isdir(local) and local[-1] != '/':
            local += '/'

        if local[-1] == '/':
            local += remote.split('/')[:-1]

        if not isdir('/'.join(local.split('/')[:-1]), True):
            return self.err(3, local)

        try:
            self.connection.get(remote, local)
            return True

        except Exception:
            _, message = getexcept()
            return self.err(4, message)
コード例 #5
0
    def mkdir_remote(self, data):
        """Creates remote directories.

        :param data: (str) Path.
        :return: (bool) True on success, False on failure.
        """
        if not hasstring(data):
            return False

        if data[-1] == '/':
            data = data[:-1]

        path = data.split('/')
        path_c = len(path)
        index = 1

        for index in range(0, path_c):
            try:
                if stat.S_ISREG(
                        self.connection.stat(
                            '/'.join(path[0:index + 1])).st_mode):
                    return False
            except IOError:
                index -= 1
                break

        if index < path_c - 1:
            try:
                for index in range(index + 1, path_c):
                    self.connection.mkdir('/'.join(path[0:index + 1]))
            except IOError:
                _, message = getexcept()
                return self.err(1, message)

        return True
コード例 #6
0
def wtrim(data):
    """Trim white space.

    :param data: (str) Input to be trimmed.
    :return: (str) The input trimmed.
    """
    if hasstring(data):
        return re.sub(r'\s+', ' ', data).strip()
    return ''
コード例 #7
0
def ifnull(data, payload=''):
    """Equivalent to IFNULL of MySQL.

    :param data: (str) Input to be checked.
    :param payload: (str) Payload to return if input is empty.
    :return: (str) Data or payload.
    """
    if hasstring(data):
        return data
    return payload
コード例 #8
0
def branchfix(data):
    """Applies some fixes to branch part of the command.

    :param data: (str) Branch.
    :return: Branch fixed.
    """
    if hasstring(data):
        return re.sub(r'\s\s+', ' ', data.strip()).replace('/ ', '/')

    return ''
コード例 #9
0
def properties_to_list(data):
    """Converts array of properties to list.

    :param data: (str / list) List or Comma/Space seperated properties.
    :return: (list) List of properties.
    """
    if haslist(data):
        return data

    if hasstring(data):
        return wtrim(data.replace(',', ' ')).split(' ')

    return []
コード例 #10
0
    def getinfo_interfaces(self, interface=None):
        """Meta method. Retrieves information from Router.
        """
        branch = '/interface'
        properties = ['name', 'type', 'mac-address']
        results1 = self.getvalues(branch, properties, find='dynamic!=yes',
                                  iid=True)

        branch = '/ip address'
        properties = ['address', 'interface']
        results2 = self.getvalues(branch, properties)

        results = {}

        if results1:
            for result1 in results1:
                name = None
                if haskey(result1, 'name'):
                    name = result1['name']
                if not hasstring(name):
                    continue

                results[name] = {
                    '.id': result1['.id'],
                    'type': result1['type'],
                    'mac_address': result1['mac-address'],
                    'ip_address': []
                }

                # Bug: PPPoE intrfaces from ISP will have irregular network
                #      address
                if results2:
                    for result2 in results2:
                        if result2['interface'] == name:
                            address = result2['address'].split('/')
                            network = ipaddress.IPv4Network(result2[
                                'address'].decode('utf-8'), False)
                            ip_address = {
                                'address': address[0],
                                'nbits': address[1],
                                'netmask': str(
                                    network.netmask).encode('utf-8'),
                                'network': str(
                                    network.network_address).encode('utf-8'),
                                'broadcast': str(
                                    network.broadcast_address).encode('utf-8')
                            }

                            results[name]['ip_address'].append(ip_address)

        return results
コード例 #11
0
    def __init__(self,
                 host,
                 port=22,
                 username='******',
                 password='',
                 pkey_string='',
                 pkey_file=''):
        """Initializes a SSHCommon object.

        :param host: (str) Remote host. It can be IPv4, IPv6 or hostname.
        :param port: (int) SSH Port.
        :param username: (str) Username.
        :param password: (str) Password.
        :param pkey_string: (file) Private Key.
        :param pkey_file: (file) Private Key Path.
        :return: (obj) SSH Client.
        """
        super(SSHCommon, self).__init__()

        if ishost(host):
            self.host = host
        else:
            self.err(1, host)

        if isport(port):
            self.port = int(port)
        else:
            self.err(2, port)

        if hasstring(username):
            self.username = username
        else:
            self.err(3, username)

        # There is no need to validate the existence of (password OR pkey_file)
        # because the router might have empty password and no public key set.

        if pkey_file:
            if not isfile(pkey_file):
                self.err(4, pkey_file)

        if pkey_string:
            if not ispkey(pkey_string):
                self.err(5)

        self.password = password
        self.pkey_string = pkey_string
        self.pkey_file = pkey_file

        if self.errc() == 0:
            self.status = 0
コード例 #12
0
    def isfile_remote(self, data):
        """Checks if remote file exists.

        :param data: (str) Path.
        :return: (bool) True on success, False on failure.
        """
        if not hasstring(data):
            return False

        try:
            return stat.S_ISREG(self.connection.stat(data).st_mode)

        except IOError:
            return False
コード例 #13
0
    def checkline_falsepos(self, data=''):
        """Checks false-positives. For error messages that begin with the word
        'failure'.

        :param data: (str) Data to be checked.
        :return: (bool) True on success, False on failure.
        """
        if not hasstring(data):
            return self.err(1)

        for regex in self.message_regexes:
            if regex.match(data):
                return True

        return False
コード例 #14
0
    def upload(self, local, remote):
        """Uploads local files or directories to remote host.

        :param local: (str) Local path.
        :param remote: (str) Remote path.
        :return: (bool) True on success, False on failure.
        """
        if not hasstring(remote):
            return self.err(1)

        if isfile(local):
            return self.upload_file(local, remote)

        if isdir(local):
            return self.upload_dir(local, remote)

        return False
コード例 #15
0
    def isdir_remote(self, data, makedirs=False):
        """Checks if remote directory exists.

        :param data: (str) Path.
        :param makedirs: (bool) If True, creates the whole path. Equilevant to
            <mkdir -p>.
        :return: (bool) True on success, False on failure.
        """
        if not hasstring(data):
            return False

        try:
            return stat.S_ISDIR(self.connection.stat(data).st_mode)

        except IOError:
            if makedirs:
                return self.mkdir_remote(data)
コード例 #16
0
    def checkline(self, data=''):
        """Checks the input against a list of common errors.

        :param data: (str) Data to be checked.
        :return: (bool) True on success, False on failure.
        """
        if not hasstring(data):
            return True

        data = data.strip()
        for index, error in enumerate(self.message_errors):
            if data.find(error) == 0:
                if error == 'failure:':
                    return self.checkline_falsepos(data)
                return self.err(index, data)

        return True
コード例 #17
0
def writejson(filename, data):
    """Saves the (dict) data to JSON file.

    :param filename: (str) Filename to write JSON to.
    :param data: (dict) Dict to save to JSON file.
    :return: (bool) True on success, False on failure.
    """
    if not hasstring(filename):
        return False
    if not isdir('/'.join(filename.split('/')[:-1]), True):
        return False
    if not isinstance(data, dict):
        return False
    try:
        with open(filename, 'w') as handler:
            json.dump(data, handler)
    except IOError:
        return getexcept()
    return True
コード例 #18
0
    def commands(self, commands, raw=False, connect=True):
        """Executes a list of command on the remote host using the
        self.command() method.

        :param commands: (list) The list of commands to be executed.
        :param raw: (bool) Returns all results without filtering lines that
            start with #.
        :param connect: (bool) Connects to host, if it is not connected already.
        :return: (list) The execution result.
        """
        status = 0

        if hasstring(commands):
            commands = [commands]
            # maybe if it is only one command, exec it and skip to end
        elif not haslist(commands):
            self.err(1)
            return None

        if self.status < 1 and connect:
            if self.connect():
                status = 1

        if self.status < 1:
            self.err(2, self.status)
            return None

        results = []

        for index, command in enumerate(commands):
            if not command:
                continue

            result = self.command(command, raw, hasstdout=False)
            results.append(result)

            if self.errc():
                return self.err(2, 'commands[{}]: {}'.format(index, command))

        if status == 1:
            self.disconnect()

        return results
コード例 #19
0
def csv_parse(lines):
    """Converts the CSV-expected input to array.

    :param lines: (str / [str]) CSV input - it can either linefeeded string or
        list.
    :return: ([str]) String Array or None on error.
    """
    if not lines:
        return None
    if not haslist(lines):
        if hasstring(lines):
            lines = lines.split('\n')
        else:
            return None
    try:
        return list(csv.reader(lines, delimiter=',', quotechar='"'))
    except Exception:
        getexcept()
    return None
コード例 #20
0
def propvals_to_dict(data):
    """Converts string pairs of properties and values to structured dictionary.

    :param data: (str) Properties and values.
    :return: (dict) Structured dictionary.
    """
    if not hasstring(data):
        return {}

    results = {}

    regex = re.compile(r'(\S+=".+"|\S+=\S+)')
    matches = set(regex.findall(data))

    for match in matches:
        eql = match.find('=')

        results[match[0:eql]] = valuefix(match[eql + 1:])

    return results
コード例 #21
0
    def test_hasstring(self):
        """Test if input is string.
        """

        none0 = None
        int0 = 0
        int1 = 1
        int2 = -1
        str0 = ''
        str1 = 'abcd'
        list0 = []
        list1 = ['ab', 'cd']
        dict0 = {}
        dict1 = {'ab': 'cd'}
        set0 = set()
        set1 = set(['a', 'b', 1, 2])
        tuple0 = ()
        tuple1 = ('a', 'b', 1, 2)

        self.assertFalse(valid.hasstring(none0))
        self.assertFalse(valid.hasstring(int0))
        self.assertFalse(valid.hasstring(int1))
        self.assertFalse(valid.hasstring(int2))
        self.assertFalse(valid.hasstring(str0))
        self.assertTrue(valid.hasstring(str1))  # assertTrue
        self.assertFalse(valid.hasstring(list0))
        self.assertFalse(valid.hasstring(list1))
        self.assertFalse(valid.hasstring(dict0))
        self.assertFalse(valid.hasstring(dict1))
        self.assertFalse(valid.hasstring(set0))
        self.assertFalse(valid.hasstring(set1))
        self.assertFalse(valid.hasstring(tuple0))
        self.assertFalse(valid.hasstring(tuple1))
コード例 #22
0
    def command(self, command, raw=False, connect=True, hasstdout=True):
        """Executes the <command> on the remote host and returns the results.

        :param command: (str) The command that has to be executed.
        :param raw: (bool) Returns all results without filtering lines that
            start with #.
        :param connect: (bool) Connects to host, if it is not connected already.
        :param hasstdout: (bool) Is it expected the command to give output?
        :return: (list) The execution result.
        """
        status = 0

        self.history.append(command)

        if not hasstring(command):
            self.err(1)
            return None

        if self.status < 1 and connect:
            if self.connect():
                status = 1

        if self.status < 1:
            self.err(2, self.status)
            return None

        if self.reseterrors:
            self.err0()

        lines = []

        try:
            # stdin, stdout, stderr = ...
            _, stdout, stderr = self.connection.exec_command(command)
            lines = stdout.read().replace('\r', '').split('\n')

            # Mikrotik CLI is not producing stderr. Linux does.
            if not (hasstring(lines) or haslist(lines)):
                lines = stderr.read().replace('\r', '').split('\n')

        except Exception:
            _, message = getexcept()
            self.err(3, message)

        finally:
            if status == 1:
                self.disconnect()
        ##### fix the logic ^^^ \/\/\/\ of get/except/disconnect

        results = []

        if raw:
            for line in lines:
                results.append(line)

        elif lines:
            for line in lines:
                if line:
                    if line[0] != '#':
                        results.append(line.strip())

        if not haslist(results) and hasstdout:
            self.err(4, command)

        return results
コード例 #23
0
    def getvalues(self, branch, properties, find='', csvout=False, iid=False):
        """Retrieves requested values from remote host.

        :param branch: (str) Branch of commands.
        :param properties: (str / list) List or Comma / Space separated
            properties.
        :param find: (str) Mikrotik CLI filter.
        :param csvout: (bool) Output in CSV File.
        :param iid: (bool) Adds $id to output.
        :return: (list) CSV formatted output.
            Example output if <csvout=True>:
                Return of <self.command>:

                [
                    "value1,value2,value3,...",
                    "value1,value2,value3,...",
                    ...
                ]

            Example output if <csvout=False>:
                Return of <csv_to_list_dict>:

                [
                    {
                        results"variable1": "value1",
                        ...
                    }
                    ...
                ]
        """
        results = []

        if not hasstring(branch):
            self.err(1)
            return None

        branch = branchfix(branch)

        if not haskey(self.branch, branch, dict):
            self.err(2, branch)
            return None

        properties = properties_to_list(properties)

        if not haslist(properties):
            self.err(3)
            return None

        command = ''
        commands = []

        if (self.branch[branch]['class'] == 'settings' or
                (self.branch[branch]['class'] == 'list' and
                 find and find.find('=') < 1)):
            for prop in properties:
                commands.append('[{} get {} {}]'.format(branch, find, prop))

            command = ':put ({})'.format('.",".'.join(commands))

        elif self.branch[branch]['class'] == 'list':
            if iid:
                commands.append('$i')

            for prop in properties:
                commands.append('[{} get $i {}]'.format(branch, prop))

            command = (':foreach i in=[' + branch + ' find ' + find + '] '
                       'do={:put (' + '.",".'.join(commands) + ')}')

        else:
            self.err(4, branch)
            return None

        lines = self.command(command)

        if not lines or self.errc():
            self.err(5, command)
            return None

        if csvout:
            results = lines
        else:
            results = csv_to_listdict(properties, lines, self.branch[branch],
                                      iid)

        return results
コード例 #24
0
    def setvalues(self, branch, propvals='', find=''):
        """Sets requested values to remote host. That method compares the
        configuration before and after command execution to detect changes and
        form the bool result.

        :param branch: (str) Branch of commands.
        :param propvals: (dict) Dictionary of Variables=Values.
        :param find: (str) Mikrotik CLI filter.
        :return: (bool) True when <propvals> have changed the configuration. If
            for some reason, configuration remains unchanged False will be
            returned, but the <self.errc> will be zero. It will also return
            False in case of error.
        """
        if not hasstring(branch):
            return self.err(1)

        branch = branchfix(branch)

        if not haskey(self.branch, branch, dict):
            return self.err(2, branch)

        if self.branch[branch]['readonly']:
            return False

        if not hasstring(propvals):
            return self.err(3)

        # Parse propvals to properties
        propvals_d = propvals_to_dict(propvals)
        if not hasdict(propvals_d):
            return self.err(4)
        properties = []
        for prop in propvals_d:
            properties.append(prop)

        # Create the find command, if find exists
        find_command = ''
        iid = True
        if self.branch[branch]['class'] == 'list':
            if hasstring(find):
                if find.find('=') > 1:
                    find_command = '[find {}] '.format(find)
                else:
                    iid = False
                    find_command = find + ' '

        # Get values before updates
        getvalues0 = self.getvalues(branch, properties, find, False, iid)
        if not getvalues0:
            return self.err(5)

        # Exit if Get is same as update
        if not propvals_diff_getvalues(propvals_d, getvalues0):
            return False   # There are no changes to apply

        # Update command
        command = '{} set {}{}'.format(branch, find_command, propvals)
        results = self.command(command, hasstdout=False)
        if results:
            self.err(6, command)
            return self.err(7, results)

        # Get values after update
        getvalues1 = self.getvalues(branch, properties, find, False, iid)
        if not getvalues1:
            return self.err(8)

        # Compare Before and After update command
        if getvalues0 != getvalues1:
            return True  # Changed
        return False  # Not changed != Failed
コード例 #25
0
    def addentry(self, branch, propvals=''):
        """Adds new entries to remote host. That method compares the
        configuration before and after command execution to detect changes and
        form the bool result.

        :param branch: (str) Branch of commands
        :param propvals: (str) Space seperated pairs of Variable=Value
        :return: (bool) True when <propvals> have changed the configuration. If
            for some reason, configuration remains unchanged False will be
            returned, but the <self.errc> will be zero. It will also return
            False in case of error.
        """
        if not hasstring(branch):
            return self.err(1)

        branch = branchfix(branch)

        if not haskey(self.branch, branch, dict):
            return self.err(2, branch)

        if self.branch[branch]['class'] != 'list':
            return False

        if self.branch[branch]['readonly']:
            return False

        if not hasstring(propvals):
            return self.err(3)

        # Count entries before add command
        command = ':put [:len [{} find]]'.format(branch)
        entries_c0 = self.command(command)
        if not entries_c0:
            return self.err(4)

        # Check if such an entry exists
        if self.branch[branch]['id']:
            propvals_d = propvals_to_dict(propvals)
            prop = self.branch[branch]['id'][0]

            if haskey(propvals_d, prop):
                command = ':put [:len [{} find {}={}]]'.format(branch, prop,
                                                               propvals_d[prop])
                result = self.command(command)

                if result[0] != '0':
                    return False

        # Add Command
        command = '{} add {}'.format(branch, propvals)
        results = self.command(command, hasstdout=False)
        if results:
            if self.checkline_falsepos(results[0]):
                self.err0()
                return False  # Not changed != Failed
            self.err(5, command)
            return self.err(6, results)

        # Count entries after add command
        command = ':put [:len [{} find]]'.format(branch)
        entries_c1 = self.command(command)
        if not entries_c1:
            return self.err(7)

        # Compare Before and After add command
        if entries_c0 != entries_c1:
            return True  # Changed
        return False  # Not changed != Failed