Esempio n. 1
0
    def connect(self,
                auth_provider='tmos',
                verify=False,
                port='443',
                protocol='https',
                retries=3,
                retry_wait=10,
                timeout=30,
                ttl=3600,
                *args,
                **kwargs):
        if self.connected:
            return

        # support sshtunnel
        if 'sshtunnel' in self.connection_info:
            try:
                from unicon.sshutils import sshtunnel
            except ImportError:
                raise ImportError("`unicon` is not installed for `sshtunnel`. "
                                  "Please install by `pip install unicon`.")
            try:
                tunnel_port = sshtunnel.auto_tunnel_add(self.device, self.via)
                if tunnel_port:
                    self.ip = self.device.connections[
                        self.via].sshtunnel.tunnel_ip
                    self.port = tunnel_port
            except AttributeError as e:
                raise AttributeError(
                    "Cannot add ssh tunnel. Connection %s may not have ip/host or port.\n%s"
                    % (self.via, e))
        else:
            self.ip = self.connection_info['ip'].exploded
            self.port = self.connection_info.get('port', port)

        if 'protocol' in self.connection_info:
            protocol = self.connection_info['protocol']

        self.base_url = '{protocol}://{ip}:{port}'.format(protocol=protocol,
                                                          ip=self.ip,
                                                          port=self.port)

        self.username, self.password = get_username_password(self)

        self.header = "Content-Type: application/json"
        self.verify = verify
        self._auth_provider = auth_provider
        self._ttl = ttl

        self._connect(timeout, retries, retry_wait)

        return self._is_connected, self.icr_session
Esempio n. 2
0
    def connect(self, timeout=10, port=443, protocol='https'):
        '''connect to the device via REST

        Arguments
        ---------

            timeout (int): Timeout value
            protocol (str): http or https. Default to https

        Raises
        ------

        Exception
        ---------

            If the connection did not go well

        Note
        ----

        There is no return from this method. If something goes wrong, an
        exception will be raised.


        YAML Example
        ------------

            devices:
                dcnm:
                    os: dcnm
                    connections:
                        vty:
                            protocol : ssh
                            ip : "2.3.4.5"
                            protocol: https
                        rest:
                            class: rest.connector.Rest
                            ip : "2.3.4.5"
                            port: "443"
                            protocol: https
                            credentials:
                                rest:
                                    username: admin
                                    password: cisco123

        Code Example
        ------------

            >>> from pyats.topology import loader
            >>> testbed = loader.load('/users/xxx/xxx/dcnm123.yaml')
            >>> device = testbed.devices['haPrimary']
            >>> device.connect(alias='rest', via='rest')
        '''

        # support sshtunnel
        if 'sshtunnel' in self.connection_info:
            try:
                from unicon.sshutils import sshtunnel
            except ImportError:
                raise ImportError(
                    '`unicon` is not installed for `sshtunnel`. Please install by `pip install unicon`.'
                )
            try:
                tunnel_port = sshtunnel.auto_tunnel_add(self.device, self.via)
                if tunnel_port:
                    ip = self.device.connections[self.via].sshtunnel.tunnel_ip
                    port = tunnel_port
            except AttributeError as e:
                raise AttributeError(
                    "Cannot add ssh tunnel. Connection %s may not have ip/host or port.\n%s"
                    % (self.via, e))
        else:
            # prefer host instead of ip address directly
            try:
                host = self.connection_info['host']
            except KeyError:
                host = self.connection_info['ip'].exploded
            port = self.connection_info.get('port', port)

        if 'protocol' in self.connection_info:
            protocol = self.connection_info['protocol']

        self.url = '{protocol}://{host}:{port}'.format(protocol=protocol,
                                                       host=host,
                                                       port=port)

        self.verify = self.connection_info.get('verify', True)

        username, password = get_username_password(self)

        _data = json.dumps({'expirationTime': 999999})

        login_url = '{url}/rest/logon'.format(url=self.url)

        log.info("Connecting to '{d}' with alias "
                 "'{a}'".format(d=self.device.name, a=self.alias))

        self.session = requests.Session()
        self.session.auth = HTTPBasicAuth(username, password)
        response = self.session.post(login_url,
                                     data=json.dumps(
                                         {'expirationTime': 999999}),
                                     headers=STD_HEADER,
                                     verify=self.verify)
        log.info(response)

        # Make sure it returned requests.codes.ok
        if response.status_code != requests.codes.ok:
            # Something bad happened
            raise RequestException("Connection to '{ip}' has returned the "
                                   "following code '{c}', instead of the "
                                   "expected status code '{ok}'" \
                                   .format(ip=host, c=response.status_code,
                                           ok=requests.codes.ok))

        self.token = response.json()[
            'Dcnm-Token']  # Retrieve the Token from the returned JSONhahhah

        self._is_connected = True
        log.info("Connected successfully to '{d}'".format(d=self.device.name))
        return self.token
Esempio n. 3
0
    def connect(self,
                auth_provider='tmos',
                verify=False,
                port='443',
                protocol='https',
                *args,
                **kwargs):

        if self.connected:
            return

        # support sshtunnel
        if 'sshtunnel' in self.connection_info:
            try:
                from unicon.sshutils import sshtunnel
            except ImportError:
                raise ImportError(
                    '`unicon` is not installed for `sshtunnel`. Please install by `pip install unicon`.'
                )
            try:
                tunnel_port = sshtunnel.auto_tunnel_add(self.device, self.via)
                if tunnel_port:
                    ip = self.device.connections[self.via].sshtunnel.tunnel_ip
                    port = tunnel_port
            except AttributeError as e:
                raise AttributeError(
                    "Cannot add ssh tunnel. Connection %s may not have ip/host or port.\n%s"
                    % (self.via, e))
        else:
            ip = self.connection_info['ip'].exploded
            port = self.connection_info.get('port', port)

        if 'protocol' in self.connection_info:
            protocol = self.connection_info['protocol']

        self.base_url = '{protocol}://{ip}:{port}'.format(protocol=protocol,
                                                          ip=ip,
                                                          port=port)

        self.username, self.password = get_username_password(self)

        self.header = "Content-Type: application/json"
        self.verify = verify

        # URL to authenticate and receive the token
        url = "https://{0}:{1}/mgmt/shared/authn/login".format(
            self.ip, self.port)
        payload = {
            'username': self.username,
            'password': self.password,
            'loginProviderName': auth_provider
        }

        iCRS = iControlRESTSession(self.username,
                                   self.password,
                                   verify=self.verify)

        log.info("Connecting to '{d}' with alias "
                 "'{a}'".format(d=self.device.name, a=self.alias))

        response = iCRS.post(
            url,
            json=payload,
        )

        log.info(response.json())

        if response.status_code not in [200]:
            if b'Configuration Utility restarting...' in response.content:
                time.sleep(30)
                # self.retries += 1
                return self.connect()
            else:
                # self.retries = 0
                return None, response.content

        self.token = response.json()['token']['token']

        log.info(
            "The following toke is used to connect'{t}'".format(t=self.token))

        # Self-link of the token
        timeout_url = "https://{0}:{1}/mgmt/shared/authz/tokens/{2}".format(
            self.ip, self.port, self.token)
        timeout_payload = {"timeout": "3600"}

        token_icr_session = iControlRESTSession(self.username,
                                                self.password,
                                                verify=self.verify,
                                                token_to_use=self.token)

        # Extending the timeout for the token received
        token_icr_session.patch(timeout_url, json=timeout_payload)

        log.info("'{t}' - Token timeout extended to '{time}'".format(
            t=self.token, time=timeout_payload))

        params = dict(username=self.username,
                      password=self.password,
                      verify=self.verify,
                      token_to_use=self.token)

        # creating an object to be used all new requests
        self.icr_session = iControlRESTSession(**params)

        self._is_connected = True

        log.info("Connected successfully to '{d}' using token: '{t}'".format(
            d=self.device.name, t=self.token))

        return self._is_connected, self.icr_session
Esempio n. 4
0
    def connect(self,
                timeout=30,
                port='8080',
                protocol='http',
                default_content_type='json',
                verbose=False):
        '''connect to the device via REST

        Arguments
        ---------

            timeout (int): Timeout value
            port (str): Port number. Default to 8080
            protocol (str): http or https. Default to http
            default_content_type: Default for content type, json or xml

        Raises
        ------

        Exception
        ---------

            If the connection did not go well

        Note
        ----

        There is no return from this method. If something goes wrong, an
        exception will be raised.

        '''

        if self.connected:
            return

        log.debug("Content type: %s" % default_content_type)
        log.debug("Timeout: %s" % timeout)
        self.content_type = default_content_type

        # support sshtunnel
        if 'sshtunnel' in self.connection_info:
            try:
                from unicon.sshutils import sshtunnel
            except ImportError:
                raise ImportError(
                    '`unicon` is not installed for `sshtunnel`. Please install by `pip install unicon`.'
                )
            try:
                tunnel_port = sshtunnel.auto_tunnel_add(self.device, self.via)
                if tunnel_port:
                    ip = self.device.connections[self.via].sshtunnel.tunnel_ip
                    port = tunnel_port
            except AttributeError as e:
                raise AttributeError(
                    "Cannot add ssh tunnel. Connection %s may not have ip/host or port.\n%s"
                    % (self.via, e))
        else:
            ip = self.connection_info['ip'].exploded
            port = self.connection_info.get('port', port)

        if 'protocol' in self.connection_info:
            protocol = self.connection_info['protocol']

        self.base_url = '{protocol}://{ip}:{port}'.format(protocol=protocol,
                                                          ip=ip,
                                                          port=port)

        self.login_url = '{f}/api'.format(f=self.base_url)

        log.info("Connecting to '{d}' with alias "
                 "'{a}'".format(d=self.device.name, a=self.alias))

        username, password = get_username_password(self)

        self.session = requests.Session()
        self.session.auth = (username, password)

        # Connect to the device via requests
        response = self.session.get(self.login_url, timeout=timeout)
        output = response.text
        log.debug("Response: {c} {r}, headers: {h}".format(
            c=response.status_code, r=response.reason, h=response.headers))
        if verbose:
            log.info("Response text:\n%s" % output)

        # Make sure it returned requests.codes.ok
        if response.status_code != requests.codes.ok:
            # Something bad happened
            raise RequestException("Connection to '{ip}:{port}' has returned the "
                                   "following code '{c}', instead of the "
                                   "expected status code '{ok}'"\
                                        .format(ip=ip, port=port, c=response.status_code,
                                                ok=requests.codes.ok))
        self._is_connected = True
        log.info("Connected successfully to '{d}'".format(d=self.device.name))

        return response
Esempio n. 5
0
    def connect(self):
        '''connect

        High-level api: opens the NetConf connection and exchanges
        capabilities. Since topology YAML file is parsed by BaseConnection,
        the following parameters can be specified in your YAML file.

        Parameters
        ----------

        host : `string`
            Hostname or IP address to connect to.
        port : `int`, optional
            By default port is 830, but some devices use the default SSH port
            of 22 so this may need to be specified.
        timeout : `int`, optional
            An optional keyed argument to set timeout value in seconds. By
            default this value is 30 seconds.
        username : `string`
            The username to use for SSH authentication.
        password : `string`
            The password used if using password authentication, or the
            passphrase to use for unlocking keys that require it.
        key_filename : `string`
            a filename where a the private key to be used can be found.
        allow_agent : `boolean`
            Enables querying SSH agent (if found) for keys. The default value
            is True.
        hostkey_verify : `boolean`
            Enables hostkey verification from ~/.ssh/known_hosts. The default
            value is False.
        look_for_keys : `boolean`
            Enables looking in the usual locations for ssh keys
            (e.g. ~/.ssh/id_*). The default value is True.
        ssh_config : `string`
            Enables parsing of an OpenSSH configuration file, if set to its
            path, e.g. ~/.ssh/config or to True. If the value is True,
            ncclient uses ~/.ssh/config. The default value is None.

        Raises
        ------

        Exception
            If the YAML file does not have correct connections section, or
            establishing transport to ip:port is failed, ssh authentication is
            failed, or other transport failures.

        Note
        ----

        There is no return from this method. If something goes wrong, an
        exception will be raised.


        YAML Example::

            devices:
                asr22:
                    type: 'ASR'
                    tacacs:
                        login_prompt: "login:"******"Password:"******"admin"
                    passwords:
                        tacacs: admin
                        enable: admin
                        line: admin
                    connections:
                        a:
                            protocol: telnet
                            ip: "1.2.3.4"
                            port: 2004
                        vty:
                            protocol : telnet
                            ip : "2.3.4.5"
                        netconf:
                            class: yang.connector.Netconf
                            ip : "2.3.4.5"
                            port: 830
                            username: admin
                            password: admin

        Code Example::

            >>> from pyats.topology import loader
            >>> testbed = loader.load('/users/xxx/xxx/asr22.yaml')
            >>> device = testbed.devices['asr22']
            >>> device.connect(alias='nc', via='netconf')
            >>>

        Expected Results::

            >>> device.nc.connected
            True
            >>> for iter in device.nc.server_capabilities:
            ...     print(iter)
            ...
            urn:ietf:params:xml:ns:yang:smiv2:RFC-1215?module=RFC-1215
            urn:ietf:params:xml:ns:yang:smiv2:SNMPv2-TC?module=SNMPv2-TC
            ...
            >>>
        '''

        if self.connected:
            return

        logger.debug(self.session)
        if not self.session.is_alive():
            self._session = transport.SSHSession(self._device_handler)

        # default values
        defaults = {
            'host': None,
            'port': 830,
            'timeout': 30,
            'username': None,
            'password': None,
            'key_filename': None,
            'allow_agent': False,
            'hostkey_verify': False,
            'look_for_keys': False,
            'ssh_config': None,
            }
        defaults.update(self.connection_info)

        # remove items
        disregards = ['class', 'model', 'protocol',
                      'async_mode', 'raise_mode', 'credentials']
        defaults = {k: v for k, v in defaults.items() if k not in disregards}

        # rename ip -> host, cast to str type
        if 'ip' in defaults:
            defaults['host'] = str(defaults.pop('ip'))

        # rename user -> username
        if 'user' in defaults:
            defaults['username'] = str(defaults.pop('user'))

        # check credentials
        if self.connection_info.get('credentials'):
            try:
                defaults['username'] = str(self.connection_info['credentials']['netconf']['username'])
            except Exception:
                pass
            try:
                defaults['password'] = to_plaintext(self.connection_info['credentials']['netconf']['password'])
            except Exception:
                pass

        # support sshtunnel
        if 'sshtunnel' in defaults:
            from unicon.sshutils import sshtunnel
            try:
                tunnel_port = sshtunnel.auto_tunnel_add(self.device, self.via)
                if tunnel_port:
                    defaults['host'] = self.device.connections[self.via] \
                                           .sshtunnel.tunnel_ip
                    defaults['port'] = tunnel_port
            except AttributeError as err:
                raise AttributeError("Cannot add ssh tunnel. Connection %s may "
                                     "not have ip/host or port.\n%s" % (self.via, err))
            del defaults['sshtunnel']

        defaults = {k: getattr(self, k, v) for k, v in defaults.items()}

        try:
            self.session.connect(**defaults)
            logger.info(banner('NETCONF CONNECTED'))
        except Exception:
            if self.session.transport:
                self.session.close()
            raise

        @atexit.register
        def cleanup():
            if self.session.transport:
                self.session.close()
Esempio n. 6
0
    def connect(self, timeout=30, port="8443", protocol='https'):
        '''connect to the device via REST

        Arguments
        ---------

            timeout (int): Timeout value
            port (str): Port number. Default to 8443
            protocol (str): http or https. Default to https

        Raises
        ------

        Exception
        ---------

            If the connection did not go well

        Note
        ----

        There is no return from this method. If something goes wrong, an
        exception will be raised.
        '''

        if self.connected:
            return

        # support sshtunnel
        if 'sshtunnel' in self.connection_info:
            try:
                from unicon.sshutils import sshtunnel
            except ImportError:
                raise ImportError(
                    '`unicon` is not installed for `sshtunnel`. Please install by `pip install unicon`.'
                )
            try:
                tunnel_port = sshtunnel.auto_tunnel_add(self.device, self.via)
                if tunnel_port:
                    ip = self.device.connections[self.via].sshtunnel.tunnel_ip
                    port = tunnel_port
            except AttributeError as e:
                raise AttributeError(
                    "Cannot add ssh tunnel. Connection %s may not have ip/host or port.\n%s"
                    % (self.via, e))
        else:
            ip = self.connection_info['ip'].exploded
            port = self.connection_info.get('port', port)

        if 'protocol' in self.connection_info:
            protocol = self.connection_info['protocol']

        self.base_url = '{protocol}://{ip}:{port}'.format(protocol=protocol,
                                                          ip=ip,
                                                          port=port)

        self.verify = self.connection_info.get('verify', False)

        username, password = get_username_password(self)

        login_action = '/j_security_check'
        headers = {'Content-Type': 'application/x-www-form-urlencoded'}

        # Format data for loginForm
        login_data = {'j_username': username, 'j_password': password}

        # Url for posting login data
        login_url = self.base_url + login_action

        token_url = '{url}/dataservice/client/token'.format(url=self.base_url)

        self.session = requests.session()
        resp = self.session.post(login_url,
                                 data=login_data,
                                 headers=headers,
                                 verify=self.verify,
                                 timeout=timeout)

        if resp.status_code == 200:
            log.info("Login successfully to '{d}'".format(d=self.device.name))
        else:
            raise RequestException(
                "Failed to login '{d}'".format(d=self.device.name))

        login_token = self.session.get(url=token_url,
                                       verify=self.verify,
                                       timeout=timeout)

        if login_token.status_code == 200:
            self.token = login_token.content
        else:
            raise RequestException(
                "Failed to get token for '{d}'".format(d=self.device.name))

        self._is_connected = True
        log.info("Connected successfully to '{d}'".format(d=self.device.name))
        self.default_headers = {
            'X-XSRF-TOKEN': self.token,
            'Content-Type': 'application/json'
        }
Esempio n. 7
0
    def connect(self,
                timeout=30,
                default_content_type='json',
                verbose=False,
                port="443",
                protocol='https'):
        '''connect to the device via REST

        Arguments
        ---------

            timeout (int): Timeout value
            default_content_type: Default for content type, json or xml
            proxies: Specify the proxy to use for connection as seen below.
                    {'http': 'http://proxy.esl.cisco.com:80/',
                    'ftp': 'http://proxy.esl.cisco.com:80/',
                    'https': 'http://proxy.esl.cisco.com:80/',
                    'no': '.cisco.com'}

        Raises
        ------

        Exception
        ---------

            If the connection did not go well

        Note
        ----

        Connecting via RESTCONF does not require contacting the device.
        This does nothing

        '''
        if self.connected:
            return

        log.debug("Content type: %s" % default_content_type)
        log.debug("Timeout: %s" % timeout)
        self.content_type = default_content_type

        # support sshtunnel
        if 'sshtunnel' in self.connection_info:
            try:
                from unicon.sshutils import sshtunnel
            except ImportError:
                raise ImportError(
                    '`unicon` is not installed for `sshtunnel`. Please install by `pip install unicon`.'
                )
            try:
                tunnel_port = sshtunnel.auto_tunnel_add(self.device, self.via)
                if tunnel_port:
                    ip = self.device.connections[self.via].sshtunnel.tunnel_ip
                    port = tunnel_port
            except AttributeError as e:
                raise AttributeError(
                    "Cannot add ssh tunnel. Connection %s may not have ip/host or port.\n%s"
                    % (self.via, e))
        else:
            ip = self.connection_info.ip.exploded
            port = self.connection_info.get('port', port)

        if 'protocol' in self.connection_info:
            protocol = self.connection_info['protocol']

        self.base_url = '{protocol}://{ip}:{port}'.format(protocol=protocol,
                                                          ip=ip,
                                                          port=port)
        self.login_url = '{f}/'.format(f=self.base_url)
        log.info("Connecting to '{d}' with alias "
                 "'{a}'".format(d=self.device.name, a=self.alias))
        username, password = get_username_password(self)
        self.session = requests.Session()
        self.session.auth = (username, password)
        # Connect to the device via requests
        response = self.session.get(self.login_url,
                                    proxies=self.proxies,
                                    timeout=timeout,
                                    verify=False)
        output = response.text
        log.debug("Response: {c} {r}, headers: {h}".format(
            c=response.status_code, r=response.reason, h=response.headers))
        if verbose:
            log.info("Response text:\n%s" % output)

        # Make sure it returned requests.codes.ok
        if response.status_code != requests.codes.ok:
            # Something bad happened
            raise RequestException(
                "Connection to '{ip}:{port}' has returned the "
                "following code '{c}', instead of the "
                "expected status code '{ok}'".format(ip=ip,
                                                     port=port,
                                                     c=response.status_code,
                                                     ok=requests.codes.ok))
        self._is_connected = True
        log.info("Connected successfully to '{d}'".format(d=self.device.name))

        return response
Esempio n. 8
0
    def connect(self, timeout=30, port=443, protocol='https', retries=3, retry_wait=10):
        '''connect to the device via REST

        Arguments
        ---------

            timeout (int): Timeout value

            port (int): TCP port to use (default: 443)

            protocol (str): protocol to use (default: https)

            retries (int): Max retries on request exception (default: 3)

            retry_wait (int): Seconds to wait before retry (default: 10)

        Raises
        ------

        Exception
        ---------

            If the connection did not go well

        Note
        ----

        There is no return from this method. If something goes wrong, an
        exception will be raised.


        YAML Example
        ------------

            devices:
                PE1:
                    connections:
                        a:
                            protocol: telnet
                            ip: "1.2.3.4"
                            port: 2004
                        vty:
                            protocol : telnet
                            ip : "2.3.4.5"
                        rest:
                            class: rest.connector.Rest
                            ip : "2.3.4.5"
                            port: "443"
                            protocol: https
                            credentials:
                                rest:
                                    username: admin
                                    password: admin

        Code Example
        ------------

            >>> from pyats.topology import loader
            >>> testbed = loader.load('/users/xxx/xxx/asr22.yaml')
            >>> device = testbed.devices['asr22']
            >>> device.connect(alias='rest', via='rest')
        '''
        if self.connected:
            return

        # support sshtunnel
        if 'sshtunnel' in self.connection_info:
            try:
                from unicon.sshutils import sshtunnel
            except ImportError:
                raise ImportError(
                    '`unicon` is not installed for `sshtunnel`. Please install by `pip install unicon`.'
                )
            try:
                tunnel_port = sshtunnel.auto_tunnel_add(self.device, self.via)
                if tunnel_port:
                    ip = self.device.connections[self.via].sshtunnel.tunnel_ip
                    port = tunnel_port
            except AttributeError as e:
                raise AttributeError(
                    "Cannot add ssh tunnel. Connection %s may not have ip/host or port.\n%s"
                    % (self.via, e))
        else:
            ip = self.connection_info['ip'].exploded
            port = self.connection_info.get('port', port)
        if 'protocol' in self.connection_info:
            protocol = self.connection_info['protocol']

        self.url = '{protocol}://{ip}:{port}'.format(protocol=protocol,
                                                     ip=ip,
                                                     port=port)

        login_url = '{f}/api/aaaLogin.json'.format(f=self.url)

        username, password = get_username_password(self)

        payload = {
           "aaaUser": {
              "attributes": {
                 "name": username,
                 "pwd": password,
               }
           }
        }

        log.info("Connecting to '{d}' with alias "
                 "'{a}'".format(d=self.device.name, a=self.alias))

        self.session = requests.Session()
        _data = json.dumps(payload)

        for _ in range(retries):
            try:
                # Connect to the device via requests
                if protocol == 'https':
                    response = self.session.post(login_url, data=_data, timeout=timeout, verify=False)
                else:
                    response = self.session.post(login_url, data=_data, timeout=timeout)
                break
            except Exception:
                log.warning('Request to {} failed. Waiting {} seconds before retrying\n'.format(
                             self.device.name, retry_wait), exc_info=True)
                time.sleep(retry_wait)
        else:
            raise ConnectionError('Connection to {} failed'.format(self.device.name))

        log.info(response)

        # Make sure it returned requests.codes.ok
        if response.status_code != requests.codes.ok:
            # Something bad happened
            raise RequestException("Connection to '{ip}' has returned the "
                                   "following code '{c}', instead of the "
                                   "expected status code '{ok}'"
                                   .format(ip=ip, c=response.status_code,
                                           ok=requests.codes.ok))

        # Attach auth to session for future calls
        self.session.auth = HTTPBasicAuth(username, password)

        self._is_connected = True
        log.info("Connected successfully to '{d}'".format(d=self.device.name))
Esempio n. 9
0
    def connect(self, timeout=30, port="19399", protocol='http'):
        '''connect to the device via REST

        Arguments
        ---------

            timeout (int): Timeout value
            port (str): Port number. Default to 19399
            protocol (str): http or https

        Raises
        ------

        Exception
        ---------

            If the connection did not go well

        Note
        ----

        There is no return from this method. If something goes wrong, an
        exception will be raised.


        YAML Example
        ------------

            devices:
                virl:
                    connections:
                        rest:
                            class: rest.connector.Rest
                            ip : "192.168.1.1"
                            port: "80"
                            protocol : http
                            credentials:
                                default:
                                    username: admin
                                    password: cisco123

        Code Example
        ------------

            >>> from pyats.topology import loader
            >>> testbed = loader.load('/users/xxx/xxx/testbed.yaml')
            >>> device = testbed.devices['virl']
            >>> device.connect(alias='rest', via='rest')
        '''

        if self.connected:
            return

        # support sshtunnel
        if 'sshtunnel' in self.connection_info:
            try:
                from unicon.sshutils import sshtunnel
            except ImportError:
                raise ImportError(
                    '`unicon` is not installed for `sshtunnel`. Please install by `pip install unicon`.'
                )
            try:
                tunnel_port = sshtunnel.auto_tunnel_add(self.device, self.via)
                if tunnel_port:
                    ip = self.device.connections[self.via].sshtunnel.tunnel_ip
                    port = tunnel_port
            except AttributeError as e:
                raise AttributeError(
                    "Cannot add ssh tunnel. Connection %s may not have ip/host or port.\n%s"
                    % (self.via, e))
        else:
            ip = self.connection_info['ip'].exploded
            port = self.connection_info.get('port', '19399')

        if 'protocol' in self.connection_info:
            protocol = self.connection_info['protocol']

        self.url = '{protocol}://{ip}:{port}'.format(protocol=protocol,
                                                     ip=ip,
                                                     port=port)

        self.username, self.password = get_username_password(self)
        self.headers = {"Content-Type": "text/xml;charset=UTF-8"}

        log.info("Connecting to '{d}' with alias "
                 "'{a}'".format(d=self.device.name, a=self.alias))

        self.session = requests.Session()

        # Connect to the device via requests
        if protocol == 'https':
            response = self.session.get(self.url + '/roster/rest/test',
                                        auth=(self.username, self.password),
                                        timeout=timeout,
                                        headers=self.headers,
                                        verify=False)
        else:
            response = self.session.get(self.url + '/roster/rest/test',
                                        auth=(self.username, self.password),
                                        timeout=timeout,
                                        headers=self.headers)
        log.info(response)

        # Make sure it returned requests.codes.ok
        if response.status_code != requests.codes.ok:
            # Something bad happened
            raise RequestException("Connection to '{ip}' has returned the "
                                   "following code '{c}', instead of the "
                                   "expected status code '{ok}'"\
                                        .format(ip=ip, c=response.status_code,
                                                ok=requests.codes.ok))
        self._is_connected = True
        log.info("Connected successfully to '{d}'".format(d=self.device.name))