示例#1
0
def main():
    options = parseargs()
    # Make connection to CVP
    client = CvpClient()
    client.connect([options.cvp], options.user, options.passw)
    resp = client.api.get_cvp_info()
    if resp is None:
        print "Failed to get CVP info. Exiting"
        sys.exit(1)

    dev_info = client.api.get_device_by_name(options.device)
    if dev_info is None:
        print "Unable to find device %s. Exiting" % options.device
        sys.exit(1)
    print "FOUND DEVICE INFO"

    dev_configlets = client.api.get_configlets_by_device_id(
        dev_info["systemMacAddress"])
    if dev_configlets is None:
        print "Found no configlets for device %s. Exiting" % options.device
        sys.exit(1)
    print "FOUND DEVICE CONFIGLETS"
    reconcile_configlet = None
    for configlet in dev_configlets:
        if "reconciled" in configlet and configlet["reconciled"] is True:
            reconcile_configlet = configlet

    if reconcile_configlet is None:
        print "No reconcile configlet for device %s. Exiting" % options.device
        sys.exit(1)
    print "FOUND DEVICE RECONCILE CONFIGLET"
    # Call function to parse interfaces and non-interfaces from Recocile Configlet
    automation_configlet_dict, reconcile_configlet_list = parse_reconcile_config(
        reconcile_configlet["config"])
    # Call function to update automation configlets with necessary ports
    configlet_upload(client, options.device, automation_configlet_dict)
    # Update Reconcile Configlet to remove interfaces moved to automation
    print "UPDATING RECONCILE CONFIGLET TO REMOVE INTERFACES"
    reconcile_config = ""
    for line in reconcile_configlet_list:
        reconcile_config += str(line + "\n")
    data = {
        "name": reconcile_configlet["name"],
        "config": reconcile_config,
        "key": reconcile_configlet["key"],
        "reconciled": True
    }
    resp = client.post(
        "/provisioning/updateReconcileConfiglet.do?netElementId=" +
        dev_info["systemMacAddress"],
        data=data)
示例#2
0
 def _change_passwd(self, nodes, username, old_password, new_password):
     ''' Helper method to change the user password on CVP.
     '''
     # Create a new connection to handle the request.
     clnt = CvpClient(filename='/tmp/TestCvpClient.log')
     clnt.connect(nodes, username, old_password)
     data = {
         'user': {
             'userId': username,
             'password': new_password,
             'email': '*****@*****.**',
             'userStatus': 'Enabled',
         },
         'roles': ['network-admin']
     }
     result = clnt.post("/user/updateUser.do?userId=%s" % username, data)
     self.assertEqual('success', result['data'])
示例#3
0
 def _change_passwd(self, nodes, username, old_password, new_password):
     ''' Helper method to change the user password on CVP.
     '''
     # Create a new connection to handle the request.
     clnt = CvpClient(filename='/tmp/TestCvpClient.log')
     clnt.connect(nodes, username, old_password)
     data = {
         'user': {
             'userId': username,
             'password': new_password,
             'email': '*****@*****.**',
             'userStatus': 'Enabled',
         },
         'roles': [
             'network-admin'
         ]
     }
     result = clnt.post("/user/updateUser.do?userId=%s" % username, data)
     self.assertEqual('success', result['data'])
示例#4
0
class TestCvpClient(DutSystemTest):
    ''' Test cases for the CvpClient class.
    '''
    # pylint: disable=too-many-public-methods
    NEW_PASSWORD = '******'

    def setUp(self):
        ''' Instantiate the CvpClient class.
            Log messages to the /tmp/TestCvpClient.log
        '''
        super(TestCvpClient, self).setUp()
        self.clnt = CvpClient(filename='/tmp/TestCvpClient.log')
        self.assertIsNotNone(self.clnt)

    def tearDown(self):
        ''' Destroy the CvpClient class.
        '''
        super(TestCvpClient, self).tearDown()
        self.clnt = None

    def _change_passwd(self, nodes, username, old_password, new_password):
        ''' Helper method to change the user password on CVP.
        '''
        # Create a new connection to handle the request.
        clnt = CvpClient(filename='/tmp/TestCvpClient.log')
        clnt.connect(nodes, username, old_password)
        data = {
            'user': {
                'userId': username,
                'password': new_password,
                'email': '*****@*****.**',
                'userStatus': 'Enabled',
            },
            'roles': ['network-admin']
        }
        result = clnt.post("/user/updateUser.do?userId=%s" % username, data)
        self.assertEqual('success', result['data'])

    def test_clnt_init(self):
        ''' Verify CvpClient init
        '''
        clnt = CvpClient()
        self.assertIsNotNone(clnt)
        self.assertEqual(clnt.log.getEffectiveLevel(), logging.INFO)

    def test_clnt_init_syslog(self):
        ''' Verify CvpClient init with syslog argument
        '''
        clnt = CvpClient(syslog=True)
        self.assertIsNotNone(clnt)

    def test_clnt_init_syslog_filename(self):
        ''' Verify CvpClient init with syslog and filename argument
        '''
        logfile = '/tmp/foo'
        clnt = CvpClient(syslog=True, logger='cvpracTmp', filename=logfile)
        self.assertIsNotNone(clnt)
        os.remove(logfile)

    def test_clnt_init_log_level(self):
        ''' Verify CvpClient init with setting log level
        '''
        clnt = CvpClient(log_level='DEBUG')
        self.assertIsNotNone(clnt)
        self.assertEqual(clnt.log.getEffectiveLevel(), logging.DEBUG)

    def test_set_log_level(self):
        self.clnt.set_log_level('DEBUG')
        self.assertEqual(self.clnt.log.getEffectiveLevel(), logging.DEBUG)
        self.clnt.set_log_level('INFO')
        self.assertEqual(self.clnt.log.getEffectiveLevel(), logging.INFO)

    def test_set_log_level_invalid_value(self):
        self.clnt.set_log_level('blahblah')
        self.assertEqual(self.clnt.log.getEffectiveLevel(), logging.INFO)

    def test_connect_http_good(self):
        ''' Verify http connection succeeds to a single CVP node
            Uses default protocol and port.
        '''
        dut = self.duts[0]
        self.clnt.connect([dut['node']], dut['username'], dut['password'])

    def test_connect_https_good(self):
        ''' Verify https connection succeeds to a single CVP node
            Uses https protocol and port.
        '''
        dut = self.duts[0]
        self.clnt.connect([dut['node']],
                          dut['username'],
                          dut['password'],
                          protocol='https')

    def test_connect_username_bad(self):
        ''' Verify connect fails with bad username.
        '''
        dut = self.duts[0]
        with self.assertRaises(CvpLoginError):
            self.clnt.connect([dut['node']], 'username', dut['password'])

    def test_connect_password_bad(self):
        ''' Verify connect fails with bad password.
        '''
        dut = self.duts[0]
        with self.assertRaises(CvpLoginError):
            self.clnt.connect([dut['node']], dut['username'], 'password')

    def test_connect_node_bad(self):
        ''' Verify connection fails to a single bogus CVP node
        '''
        with self.assertRaises(CvpLoginError):
            self.clnt.connect(['bogus'],
                              'username',
                              'password',
                              connect_timeout=1)

    def test_connect_non_cvp_node(self):
        ''' Verify connection fails to a non-CVP node
        '''
        with self.assertRaises(CvpLoginError):
            self.clnt.connect(['localhost'], 'username', 'password')

    def test_connect_all_nodes_bad(self):
        ''' Verify connection fails to a single bogus CVP node
        '''
        with self.assertRaises(CvpLoginError):
            self.clnt.connect(['bogus1', 'bogus2', 'bogus3'],
                              'username',
                              'password',
                              connect_timeout=1)

    def test_connect_n1_bad_n2_good(self):
        ''' Verify connect succeeds even if one node is bad
        '''
        dut = self.duts[0]
        self.clnt.connect(['bogus', dut['node']],
                          dut['username'],
                          dut['password'],
                          connect_timeout=2)

    def test_connect_nodes_arg_bad(self):
        ''' Verify non-list nodes argument raises a TypeError
        '''
        with self.assertRaises(TypeError):
            self.clnt.connect('bogus', 'username', 'password')

    def test_connect_port_bad(self):
        ''' Verify non-http protocol with default port raises a TypeError
        '''
        dut = self.duts[0]
        with self.assertRaises(ValueError):
            self.clnt.connect([dut['node']],
                              dut['username'],
                              dut['password'],
                              protocol='bogus')

    def test_get_not_connected(self):
        ''' Verify get with no connection raises a ValueError
        '''
        with self.assertRaises(ValueError):
            self.clnt.get('/bogus')

    def test_get(self):
        ''' Verify get of CVP info
        '''
        dut = self.duts[0]
        self.clnt.connect([dut['node']], dut['username'], dut['password'])
        result = self.clnt.get('/cvpInfo/getCvpInfo.do')
        self.assertIn('version', result)

    def test_get_recover_session(self):
        ''' Verify client(get) recovers session after logout
        '''
        dut = self.duts[0]
        self.clnt.connect([dut['node']], dut['username'], dut['password'])
        result = self.clnt.post('/login/logout.do', None)
        self.assertIn('data', result)
        self.assertEqual('success', result['data'])
        result = self.clnt.get('/cvpInfo/getCvpInfo.do')
        self.assertIn('version', result)

    def test_get_recover_session_bg(self):
        ''' Verify client(get) recovers session after logout for bad/good node
        '''
        dut = self.duts[0]
        self.clnt.connect(['bogus', dut['node']], dut['username'],
                          dut['password'])
        result = self.clnt.post('/login/logout.do', None)
        self.assertIn('data', result)
        self.assertEqual('success', result['data'])
        result = self.clnt.get('/cvpInfo/getCvpInfo.do')
        self.assertIn('version', result)

    def test_get_cvp_error(self):
        ''' Verify get of bad CVP request returns an error
        '''
        dut = self.duts[0]
        self.clnt.connect([dut['node']], dut['username'], dut['password'])
        with self.assertRaises(CvpRequestError):
            self.clnt.get('/aaa/getServerById.do')

    def test_get_cvp_url_bad(self):
        ''' Verify get with bad URL returns an error
        '''
        dut = self.duts[0]
        self.clnt.connect([dut['node']], dut['username'], dut['password'])
        with self.assertRaises(CvpApiError):
            self.clnt.get('/aaa/bogus.do')

    def test_get_handle_timeout(self):
        ''' Verify get with bad URL returns an error
        '''
        dut = self.duts[0]
        self.clnt.connect([dut['node']], dut['username'], dut['password'])
        with self.assertRaises(Timeout):
            self.clnt.get('/tasks/getTasks.do', timeout=0.0001)

    def test_get_except_fail_reconnect(self):
        ''' Verify exception raised if session fails and cannot be
            re-established.
        '''
        dut = self.duts[0]
        nodes = ['bogus', dut['node']]
        self.clnt.connect(nodes, dut['username'], dut['password'])
        # Change the password for the CVP user so that a session reconnect
        # to any node will fail
        self._change_passwd(nodes, dut['username'], dut['password'],
                            self.NEW_PASSWORD)

        try:
            # Logout to end the current session and force a reconnect for the
            # next request.
            result = self.clnt.post('/login/logout.do', None)
            self.assertIn('data', result)
            self.assertEqual('success', result['data'])

        except Exception as error:
            # Should not have had an exception.  Restore the CVP password
            # and re-raise the error.
            self._change_passwd(nodes, dut['username'], self.NEW_PASSWORD,
                                dut['password'])
            raise error
        try:
            # Try a get request and expect a CvpSessionLogOutError
            result = self.clnt.get('/cvpInfo/getCvpInfo.do')
        except CvpSessionLogOutError as error:
            pass
        except Exception as error:
            # Unexpected error, restore password and re-raise the error.
            self._change_passwd(nodes, dut['username'], self.NEW_PASSWORD,
                                dut['password'])
            raise error
        # Restore password
        self._change_passwd(nodes, dut['username'], self.NEW_PASSWORD,
                            dut['password'])

    def test_post_not_connected(self):
        ''' Verify post with no connection raises a ValueError
        '''
        with self.assertRaises(ValueError):
            self.clnt.post('/bogus', None)

    def test_post(self):
        ''' Verify post of CVP info
        '''
        dut = self.duts[0]
        self.clnt.connect([dut['node']], dut['username'], dut['password'])
        result = self.clnt.post('/login/logout.do', None)
        self.assertIn('data', result)
        self.assertEqual('success', result['data'])

    def test_post_recover_session(self):
        ''' Verify client(post) recovers session after logout
        '''
        dut = self.duts[0]
        self.clnt.connect([dut['node']], dut['username'], dut['password'])
        result = self.clnt.post('/login/logout.do', None)
        self.assertIn('data', result)
        self.assertEqual('success', result['data'])
        result = self.clnt.post('/login/logout.do', None)
        self.assertEqual('success', result['data'])

    def test_post_recover_session_bg(self):
        ''' Verify client(post) recovers session after logout for bad/good node
        '''
        dut = self.duts[0]
        self.clnt.connect(['bogus', dut['node']], dut['username'],
                          dut['password'])
        result = self.clnt.post('/login/logout.do', None)
        self.assertIn('data', result)
        self.assertEqual('success', result['data'])
        result = self.clnt.post('/login/logout.do', None)
        self.assertEqual('success', result['data'])

    def test_post_cvp_bad_schema(self):
        ''' Verify post with bad schema returns an error
        '''
        dut = self.duts[0]
        self.clnt.connect([dut['node']], dut['username'], dut['password'])
        with self.assertRaises(CvpApiError):
            self.clnt.post('/aaa/saveAAADetails.do', None)

    def test_post_cvp_url_bad(self):
        ''' Verify post with bad URL returns an error
        '''
        dut = self.duts[0]
        self.clnt.connect([dut['node']], dut['username'], dut['password'])
        with self.assertRaises(CvpApiError):
            self.clnt.post('/aaa/bogus.do', None)

    def test_post_except_fail_reconn(self):
        ''' Verify exception raised if session fails and cannot be
            re-established.
        '''
        dut = self.duts[0]
        nodes = ['bogus', dut['node']]
        self.clnt.connect(nodes, dut['username'], dut['password'])
        # Change the password for the CVP user so that a session reconnect
        # to any node will fail
        self._change_passwd(nodes, dut['username'], dut['password'],
                            self.NEW_PASSWORD)

        try:
            # Logout to end the current session and force a reconnect for the
            # next request.
            result = self.clnt.post('/login/logout.do', None)
            self.assertIn('data', result)
            self.assertEqual('success', result['data'])

        except Exception as error:
            # Should not have had an exception.  Restore the CVP password
            # and re-raise the error.
            self._change_passwd(nodes, dut['username'], self.NEW_PASSWORD,
                                dut['password'])
            raise error
        try:
            # Try a post request and expect a CvpSessionLogOutError
            result = self.clnt.post('/login/logout.do', None)
        except CvpSessionLogOutError as error:
            pass
        except Exception as error:
            # Unexpected error, restore password and re-raise the error.
            self._change_passwd(nodes, dut['username'], self.NEW_PASSWORD,
                                dut['password'])
            raise error
        # Restore password
        self._change_passwd(nodes, dut['username'], self.NEW_PASSWORD,
                            dut['password'])
示例#5
0
class TestCvpClient(DutSystemTest):
    ''' Test cases for the CvpClient class.
    '''
    # pylint: disable=too-many-public-methods
    # pylint: disable=invalid-name
    def setUp(self):
        ''' Instantiate the CvpClient class and connect to the CVP node.
            Log messages to the /tmp/TestCvpClient.log
        '''
        super(TestCvpClient, self).setUp()
        self.clnt = CvpClient(filename='/tmp/TestCvpClient.log')
        self.assertIsNotNone(self.clnt)
        self.assertIsNone(self.clnt.last_used_node)
        dut = self.duts[0]
        cert = False
        if 'cert' in dut:
            cert = dut['cert']
        self.clnt.connect([dut['node']], dut['username'], dut['password'], 10,
                          cert=cert)
        self.api = self.clnt.api
        self.assertIsNotNone(self.api)

        # Verify that there is at least one device in the inventory
        err_msg = 'CVP node must contain at least one device'
        result = self.api.get_inventory()
        self.assertIsNotNone(result)
        self.assertGreaterEqual(len(result), 1, msg=err_msg)
        self.device = result[0]

        # Get the container for the device on the list and
        # use that container as the parent container.
        result = self.api.search_topology(self.device['fqdn'])
        self.assertIsNotNone(result)
        dev_container = result['netElementContainerList']
        self.assertGreaterEqual(len(dev_container), 1)
        info = dev_container[0]
        result = self.api.search_topology(info['containerName'])
        self.assertIsNotNone(result)
        self.container = result['containerList'][0]

        # Get the configlets assigned to the device.  There must be at least 1.
        err_msg = 'CVP node device must have at least one configlet assigned'
        key = info['netElementKey']
        result = self.api.get_configlets_by_device_id(key)
        self.assertGreaterEqual(len(result), 1, msg=err_msg)
        self.dev_configlets = result

    def tearDown(self):
        ''' Destroy the CvpClient class.
        '''
        super(TestCvpClient, self).tearDown()
        self.api = None
        self.clnt = None

    def _get_next_task_id(self):
        ''' Return the next task id.

            Returns:
                task_id (str): Task ID
        '''
        # Get all the tasks and the task id of the next task created is
        # the length + 1.
        results = self.api.get_tasks()
        self.assertIsNotNone(results)
        return str(int(results['total']) + 1)

    def _create_task(self):
        ''' Create a task by making a simple change to a configlet assigned
            to the device.

            Returns:
                (task_id, config)
                task_id (str): Task ID
                config (str): Previous configlets contents
        '''
        task_id = self._get_next_task_id()

        # Update the lldp time in the first configlet in the list.
        configlet = None
        for conf in self.dev_configlets:
            if conf['netElementCount'] == 1:
                configlet = conf
                break
        if configlet is None:
            configlet = self.dev_configlets[0]

        config = configlet['config']
        org_config = config

        match = re.match(r'lldp timer (\d+)', config)
        if match is not None:
            value = int(match.group(1)) + 1
            repl = 'lldp timer %d' % value
            config = re.sub(match.group(0), repl, config)
        else:
            value = 13
            config = ('lldp timer %d\n' % value) + config
        configlet['config'] = config

        # Updating the configlet will cause a task to be created to apply
        # the change to the device.
        self.api.update_configlet(config, configlet['key'], configlet['name'])

        # Wait 30 seconds for task to get created
        cnt = 30
        if self.clnt.apiversion is None:
            self.api.get_cvp_info()
        if self.clnt.apiversion == 'v2':
            # Increase timeout by 30 sec for CVP 2018.2 and beyond
            cnt += 30
        while cnt > 0:
            time.sleep(1)
            result = self.api.get_task_by_id(task_id)
            if result is not None:
                break
            cnt -= 1
        err_msg = 'Timeout waiting for task id %s to be created' % task_id
        self.assertGreater(cnt, 0, msg=err_msg)

        return task_id, org_config

    def test_api_get_cvp_info(self):
        ''' Verify get_cvp_info and verify setting of client last_used_node
            parameter
        '''
        result = self.api.get_cvp_info()
        self.assertIsNotNone(result)
        self.assertIn('version', result)
        self.assertIn(self.clnt.last_used_node, self.clnt.url_prefix)
        self.assertEqual(self.clnt.version, result['version'])
        self.assertIsNotNone(self.clnt.apiversion)

    def test_api_check_compliance(self):
        ''' Verify check_compliance
        '''
        key = self.device['key']
        ntype = self.device['type']
        result = self.api.check_compliance(key, ntype)
        self.assertEqual(result['complianceCode'], '0000')
        self.assertEqual(result['complianceIndication'], 'NONE')

    def test_api_task_operations(self):
        ''' Verify get_task_by_id, get_task_by_status, add_note_to_task,
             get_logs_by_id, and cancel_task
        '''
        (task_id, org_config) = self._create_task()

        # Test get_task_by_id
        result = self.api.get_task_by_id(task_id)
        self.assertIsNotNone(result)
        self.assertEqual(result['workOrderId'], task_id)

        # Test get_task_by_status
        results = self.api.get_tasks_by_status('PENDING')
        # More than one task may be returned.
        found = False
        for result in results:
            actual_task_id = result['workOrderId']
            if actual_task_id == task_id:
                found = True
                break
        err_msg = 'Task id: %s not in list of PENDING tasks' % task_id
        self.assertTrue(found, msg=err_msg)

        # Test add_note_to_task
        note = 'Test Generated'
        self.api.add_note_to_task(task_id, note)

        # Verify task operations
        result = self.api.get_task_by_id(task_id)
        self.assertIsNotNone(result)
        self.assertEqual(result['note'], note)
        self.assertEqual(result['workOrderUserDefinedStatus'], 'Pending')

        result = self.api.check_compliance(self.device['key'],
                                           self.device['type'])
        self.assertEqual(result['complianceCode'], '0001')

        # Test cancel_task
        self.api.cancel_task(task_id)
        time.sleep(1)
        result = self.api.get_task_by_id(task_id)
        self.assertIsNotNone(result)
        self.assertEqual(result['workOrderUserDefinedStatus'], 'Cancelled')

        result = self.api.check_compliance(self.device['key'],
                                           self.device['type'])
        self.assertEqual(result['complianceCode'], '0001')

        # Get the task logs
        result = self.api.get_logs_by_id(task_id)
        self.assertIsNotNone(result)

        result = self.api.check_compliance(self.device['key'],
                                           self.device['type'])
        self.assertEqual(result['complianceCode'], '0001')

        # Restore the configlet to what it was before the task was created.
        task_id = self._get_next_task_id()
        configlet = None
        for conf in self.dev_configlets:
            if conf['netElementCount'] == 1:
                configlet = conf
                break
        if configlet is None:
            configlet = self.dev_configlets[0]
        self.api.update_configlet(org_config, configlet['key'],
                                  configlet['name'])
        time.sleep(2)
        # Cancel task
        self.api.cancel_task(task_id)

        # Check compliance
        self.test_api_check_compliance()

    def test_api_validate_config(self):
        ''' Verify valid config returns True
        '''
        config = 'interface ethernet1\n description test'
        result = self.api.validate_config(self.device['key'], config)
        self.assertEqual(result, True)

    def test_api_validate_config_error(self):
        ''' Verify an invalid config returns False
        '''
        config = 'interface ethernet1\n typocommand test'
        result = self.api.validate_config(self.device['key'], config)
        self.assertEqual(result, False)

    def test_api_get_task_by_id_bad(self):
        ''' Verify get_task_by_id with bad task id
        '''
        result = self.api.get_task_by_id(10000000)
        self.assertIsNone(result)

    def test_api_get_task_by_id_fmt_bad(self):
        ''' Verify get_task_by_id with bad task id
        '''
        result = self.api.get_task_by_id('BOGUS')
        self.assertIsNone(result)

    def test_api_get_tasks_by_s_bad(self):
        ''' Verify get_tasks_by_status
        '''
        result = self.api.get_tasks_by_status('BOGUS')
        self.assertIsNotNone(result)

    def test_api_get_configlets(self):
        ''' Verify get_configlets
        '''
        result = self.api.get_configlets()

        # Format the configlet lists into name keyed dictionaries
        dev_cfglts = {}
        for cfglt in self.dev_configlets:
            dev_cfglts.update({cfglt['name']: cfglt})

        rslt_cfglts = {}
        for cfglt in result['data']:
            rslt_cfglts.update({cfglt['name']: cfglt})

        # Make sure the device configlets are all returned by the
        # get_configlets call
        for cfglt_name in dev_cfglts:
            self.assertIn(cfglt_name, rslt_cfglts)
            self.assertDictEqual(dev_cfglts[cfglt_name],
                                 rslt_cfglts[cfglt_name])

    def test_api_get_configlet_builder(self):
        ''' Verify get_configlet_builder
        '''
        cfglt = self.api.get_configlet_by_name('SYS_TelemetryBuilderV2')
        result = self.api.get_configlet_builder(cfglt['key'])

        # Verify the following keys and types are
        # returned by the request
        exp_data = {
            u'isAssigned': bool,
            u'formList': list,
            u'main_script': dict,
        }
        # Handle unicode type for Python 2 vs Python 3
        if sys.version_info.major < 3:
            exp_data[u'name'] = (unicode, str)
        else:
            exp_data[u'name'] = str
        for key in exp_data:
            self.assertIn(key, result['data'])
            self.assertIsInstance(result['data'][key], exp_data[key])

    def test_api_get_configlet_by_name(self):
        ''' Verify get_configlet_by_name
        '''
        configlet = None
        for conf in self.dev_configlets:
            if conf['netElementCount'] == 1:
                configlet = conf
                break
        if configlet is None:
            configlet = self.dev_configlets[0]
        result = self.api.get_configlet_by_name(configlet['name'])
        self.assertIsNotNone(result)
        self.assertEqual(result['key'], configlet['key'])

    def test_api_get_configlets_by_container_id(self):
        ''' Verify get_configlets_by_container_id
        '''
        result = self.api.get_configlets_by_container_id(
            self.container['key']
        )

        # Verify the following keys and types are returned by the request
        exp_data = {
            'configletList': list,
            'total': int,
            'configletMapper': dict,
        }
        for key in exp_data:
            self.assertIn(key, result)
            self.assertIsInstance(result[key], exp_data[key])

    def test_api_get_configlets_by_netelement_id(self):
        ''' Verify get_configlets_by_netelement_id
        '''
        result = self.api.get_configlets_by_netelement_id(
            self.device['key']
        )

        # Verify the following keys and types are returned by the request
        exp_data = {
            'configletList': list,
            'total': int,
            'configletMapper': dict,
        }
        for key in exp_data:
            self.assertIn(key, result)
            self.assertIsInstance(result[key], exp_data[key])

    def test_api_get_applied_devices(self):
        ''' Verify get_applied_devices
        '''
        for cfglt in self.dev_configlets:
            result = self.api.get_applied_devices(cfglt['name'])

            # Verify the following keys and types are
            # returned by the request
            exp_data = {
                'data': list,
                'total': int,
            }
            for key in exp_data:
                self.assertIn(key, result)
                self.assertIsInstance(result[key], exp_data[key])

    def test_api_get_applied_containers(self):
        ''' Verify get_applied_containers
        '''
        for cfglt in self.dev_configlets:
            result = self.api.get_applied_containers(cfglt['name'])

            # Verify the following keys and types are
            # returned by the request
            exp_data = {
                'data': list,
                'total': int,
            }
            for key in exp_data:
                self.assertIn(key, result)
                self.assertIsInstance(result[key], exp_data[key])

    def test_api_get_configlet_history(self):
        ''' Verify get_configlet_history
        '''
        key = None
        for conf in self.dev_configlets:
            if conf['netElementCount'] == 1:
                key = conf['key']
                break
        if key is None:
            key = self.dev_configlets[0]['key']
        result = self.api.get_configlet_history(key)
        self.assertIsNotNone(result)

    def test_api_get_device_by_name(self):
        ''' Verify get_device_by_name
        '''
        result = self.api.get_device_by_name(self.device['fqdn'])
        self.assertIsNotNone(result)
        self.assertEqual(result, self.device)

    def test_api_get_device_configuration(self):
        ''' Verify get_device_configuration
        '''
        result = self.api.get_device_configuration(self.device['key'])
        self.assertIsNotNone(result)
        config_lines = result.splitlines()
        for line in config_lines:
            if 'hostname' in line:
                self.assertEqual(line, 'hostname %s' % self.device['fqdn'])

    def test_api_get_device_by_name_bad(self):
        ''' Verify get_device_by_name with bad fqdn
        '''
        result = self.api.get_device_by_name('bogus_host_name')
        self.assertIsNotNone(result)
        self.assertEqual(result, {})

    def test_api_get_device_by_name_substring(self):
        ''' Verify get_device_by_name with partial fqdn returns nothing
        '''
        result = self.api.get_device_by_name(self.device['fqdn'][1:])
        self.assertIsNotNone(result)
        self.assertEqual(result, {})

    def _create_configlet(self, name, config):
        # Delete the configlet in case it was left by previous test run
        try:
            result = self.api.get_configlet_by_name(name)
            self.api.delete_configlet(name, result['key'])
        except CvpApiError:
            pass

        # Add the configlet
        key = self.api.add_configlet(name, config)
        self.assertIsNotNone(key)
        return key

    def test_api_add_delete_configlet(self):
        ''' Verify add_configlet and delete_configlet
        '''
        name = 'test_configlet'
        config = 'lldp timer 9'

        # Add the configlet
        key = self._create_configlet(name, config)

        # Verify configlet was added
        result = self.api.get_configlet_by_name(name)
        self.assertIsNotNone(result)
        self.assertEqual(result['name'], name)
        self.assertEqual(result['config'], config)
        self.assertEqual(result['key'], key)

        # Delete the configlet
        self.api.delete_configlet(name, key)

        # Verify configlet was deleted
        with self.assertRaises(CvpApiError):
            self.api.get_configlet_by_name(name)

    def test_api_add_note_to_configlet(self):
        ''' Verify add_note_to_configlet
        '''
        name = 'test_configlet_with_note_%d' % time.time()
        config = 'lldp timer 9'

        # Add the configlet
        key = self._create_configlet(name, config)

        # Add a note to the configlet
        note = 'Updated by cvprac test'
        result = self.api.add_note_to_configlet(key, note)

        # Verify note was added to configlet
        result = self.api.get_configlet_by_name(name)
        self.assertIsNotNone(result)
        self.assertEqual(result['name'], name)
        self.assertEqual(result['config'], config)
        self.assertEqual(result['key'], key)
        self.assertEqual(result['note'], note)
        # Delete test configlet
        self.api.delete_configlet(name, key)

    def _execute_task(self, task_id):
        ''' Execute a task and wait for it to complete.
        '''
        # Test add_note_to_task
        self.api.add_note_to_task(task_id, 'Test Generated')

        self.api.execute_task(task_id)

        # Verify task executed within 30 seconds
        cnt = 30
        while cnt > 0:
            time.sleep(1)
            result = self.api.get_task_by_id(task_id)
            status = result['workOrderUserDefinedStatus']
            if status == 'Completed' or status == 'Failed':
                break
            cnt -= 1
        err_msg = 'Execution for task id %s failed' % task_id
        self.assertNotEqual(status, 'Failed', msg=err_msg)
        err_msg = 'Timeout waiting for task id %s to execute' % task_id
        self.assertGreater(cnt, 0, msg=err_msg)

    def test_api_execute_task(self):
        ''' Verify execute_task
        '''
        # Create task and execute it
        (task_id, _) = self._create_task()
        self._execute_task(task_id)

        # Check compliance
        self.test_api_check_compliance()

    def test_api_get_containers(self):
        ''' Verify get containers
        '''
        result = self.api.get_containers()
        self.assertIsNotNone(result)
        total = result['total']
        self.assertEqual(len(result['data']), total)

    def test_api_no_container_by_name(self):
        ''' Verify searching for a container name that doesn't exist returns
            None
        '''
        container = self.api.get_container_by_name('NonExistentContainer')
        self.assertIsNone(container)

    def test_api_get_devices_in_container(self):
        ''' Verify searching for devices in a container returns only the
            devices under the given container name.
        '''
        # Get All Devices
        all_devices = self.api.get_inventory()

        # Grab key of container to test from first device in inventory
        device = all_devices[0]
        parent_cont = device['parentContainerId']

        # Make list of all devices from full inventory that are in the
        # same container as the first device
        devices_in_container = []
        for dev in all_devices:
            if dev['parentContainerId'] == parent_cont:
                devices_in_container.append(dev)

        # Get the name of the container for the container key from
        # the first device
        all_containers = self.api.get_containers()
        container_name = None
        for container in all_containers['data']:
            if container['key'] == parent_cont:
                container_name = container['name']

        result = self.api.get_devices_in_container(container_name)
        self.assertEqual(result, devices_in_container)

    def test_api_containers(self):
        ''' Verify add_container, get_container_by_name and delete_container
        '''
        name = 'CVPRACTEST'
        parent = self.container
        # Verify create container
        self.api.add_container(name, parent['name'], parent['key'])

        # Verify get container for exact container name returns only that
        # container
        container = self.api.get_container_by_name(name)
        self.assertIsNotNone(container)
        self.assertEqual(container['name'], name)

        # Verify finding created container using search topology
        result = self.api.search_topology(name)
        self.assertEqual(len(result['containerList']), 1)
        container = result['containerList'][0]
        self.assertEqual(container['name'], name)
        key = container['key']
        # Verify newly created container has no devices in it
        new_cont_devices = self.api.get_devices_in_container(name)
        self.assertEqual(new_cont_devices, [])

        # Verify move device to container
        device = self.api.get_inventory()[0]
        orig_cont = self.api.get_parent_container_for_device(
            device['key'])
        if orig_cont['key'] != 'undefined_container':
            task = self.api.move_device_to_container(
                'test', device, container)['data']['taskIds'][0]
            self.api.cancel_task(task)
            curr_cont = self.api.get_parent_container_for_device(device['key'])
            self.assertEqual(curr_cont['key'], key)
            device = self.api.get_device_by_name(device['fqdn'])
            if 'parentContainerId' in device:
                self.assertEqual(device['parentContainerId'], key)
            task = self.api.move_device_to_container(
                'test', device, orig_cont)['data']['taskIds'][0]
            self.api.cancel_task(task)
            curr_cont = self.api.get_parent_container_for_device(device['key'])
            self.assertEqual(curr_cont['key'], orig_cont['key'])
            device = self.api.get_device_by_name(device['fqdn'])
            if 'parentContainerId' in device:
                self.assertEqual(device['parentContainerId'], orig_cont['key'])

        # Verify delete container
        self.api.delete_container(name, key, parent['name'], parent['key'])
        result = self.api.search_topology(name)
        self.assertEqual(len(result['containerList']), 0)

    def test_api_container_url_encode_name(self):
        ''' Verify special characters can be used in container names
        '''
        new_cont_name = 'Rack2+_DC11'
        parent = self.container
        # Verify create container
        self.api.add_container(new_cont_name, parent['name'], parent['key'])
        # Verify get container for container with special characters in name
        container = self.api.get_container_by_name(new_cont_name)
        self.assertIsNotNone(container)
        self.assertEqual(container['name'], new_cont_name)
        # Verify delete container
        self.api.delete_container(new_cont_name, container['key'],
                                  parent['name'], parent['key'])
        result = self.api.search_topology(new_cont_name)
        self.assertEqual(len(result['containerList']), 0)

    def test_api_configlets_to_device(self):
        ''' Verify apply_configlets_to_device and remove_configlets_from_device
        '''
        # Create a new configlet
        name = 'test_configlet'
        config = 'alias srie show running-config interface ethernet 1'

        # Add the configlet
        key = self._create_configlet(name, config)

        # Get the next task ID
        task_id = self._get_next_task_id()

        # Apply the configlet to the device
        label = 'cvprac test'
        param = {'name': name, 'key': key}
        self.api.apply_configlets_to_device(label, self.device, [param])

        # Validate task was created to apply the configlet to device
        # Wait 30 seconds for task to get created
        cnt = 30
        while cnt > 0:
            time.sleep(1)
            result = self.api.get_task_by_id(task_id)
            if result is not None:
                break
            cnt -= 1
        self.assertIsNotNone(result)
        self.assertEqual(result['workOrderId'], task_id)
        self.assertIn(label, result['description'])

        # Execute Task
        self._execute_task(task_id)

        # Get the next task ID
        task_id = self._get_next_task_id()

        # Remove configlet from device
        self.api.remove_configlets_from_device(label, self.device, [param])

        # Validate task was created to remove the configlet to device
        # Wait 30 seconds for task to get created
        cnt = 30
        while cnt > 0:
            time.sleep(1)
            result = self.api.get_task_by_id(task_id)
            if result is not None:
                break
            cnt -= 1
        self.assertIsNotNone(result)
        self.assertEqual(result['workOrderId'], task_id)
        self.assertIn(label, result['description'])

        # Execute Task
        self._execute_task(task_id)

        # Delete the configlet
        self.api.delete_configlet(name, key)

        # Check compliance
        self.test_api_check_compliance()

    def test_api_request_timeout(self):
        ''' Verify api timeout
        '''
        self.assertEqual(self.api.request_timeout, 30)
        self.api.request_timeout = 0.0001
        with self.assertRaises(Timeout):
            self.api.get_cvp_info()
        self.api.request_timeout = 30.0

    def test_api_get_all_temp_actions(self):
        ''' Verify get_all_temp_actions
        '''
        # pylint: disable=protected-access
        name = 'test_configlet'
        config = 'lldp timer 9'

        # Add a configlet
        key = self._create_configlet(name, config)

        # Apply the configlet to the container
        data = {
            'data': [{
                'info': 'test_api_get_all_temp_actions',
                'infoPreview': 'test_api_get_all_temp_actions',
                'action': 'associate',
                'nodeType': 'configlet',
                'nodeId': '',
                'toId': self.container['key'],
                'fromId': '',
                'nodeName': '',
                'fromName': '',
                'toName': self.container['name'],
                'toIdType': 'container',
                'configletList': [key],
                'configletNamesList': [name],
                'ignoreConfigletList': [],
                'ignoreConfigletNamesList': [],
                'configletBuilderList' : [],
                'configletBuilderNamesList' : [],
                'ignoreConfigletBuilderList' : [],
                'ignoreConfigletBuilderNamesList': [],
            }]
        }
        self.api._add_temp_action(data)

        # Request the list of temp actions
        result = self.api.get_all_temp_actions()

        # Delete the temporary action and the configlet
        self.clnt.post('//provisioning/deleteAllTempAction.do')
        self.api.delete_configlet(name, key)

        # Validate the results
        # There should be 1 temp action
        self.assertEqual(result['total'], 1)
        # The temp action should contain the data from the add action
        for dkey in data['data'][0]:
            self.assertIn(dkey, result['data'][0].keys())
            self.assertEqual(data['data'][0][dkey], result['data'][0][dkey])

    def test_api_get_event_by_id_bad(self):
        ''' Verify get_event_by_id returns an error for a bad ID
        '''
        try:
            # The api request should fail
            result = self.api.get_event_by_id('\n*')
            self.assertIsNone(result)
        except CvpApiError as ebi_err:
            # The error should contain 'Invalid Event Id'
            self.assertIn('Invalid Event Id', str(ebi_err))

    def test_api_get_default_snapshot_template(self):
        ''' Verify get_default_snapshot_template.
        '''
        result = self.api.get_default_snapshot_template()
        if result is not None:
            expected = {
                u'ccTasksTagged': 0,
                u'classId': 63,
                u'commandCount': 1,
                u'createdBy': u'System',
                u'default': True,
                u'factoryId': 1,
                u'id': 63,
                u'isDefault': True,
                u'key': u'Initial_Template',
                u'name': u'Show_Inventory',
                u'note': u'',
            }

            # Remove the snapshotCount, totalSnapshotCount and
            # createdTimestamp, since these can change with usage
            result.pop('snapshotCount', None)
            result.pop('totalSnapshotCount', None)
            result.pop('createdTimestamp', None)
            self.assertDictEqual(result, expected)

    def test_api_capture_container_level_snapshot(self):
        ''' Verify capture_container_level_snapshot
        '''
        # Get the container and snapshot keys
        container_key = self.container['key']
        default_snap = self.api.get_default_snapshot_template()
        if default_snap is not None:
            snapshot_key = default_snap['key']

            # Initialize the snapshot event
            result = self.api.capture_container_level_snapshot(
                snapshot_key, container_key)
            self.assertIn('data', result)
            self.assertIn('eventId', result)
            self.assertEqual('success', result['data'])

    def test_api_add_image_cancel_image(self):
        ''' Verify add_image and cancel_image
        '''
        # Get number of current images
        orig_images = self.api.get_images()

        # Copy the test image file with a timestamp appended
        image_file_name = 'image-file-%s.swix' % time.time()
        image_file = 'test/fixtures/%s' % image_file_name
        shutil.copyfile('test/fixtures/image-file.swix', image_file)

        # Upload the image to the cluster
        add_response = self.api.add_image(image_file)

        # Remove the timestamp copy from the local filesystem
        os.remove(image_file)

        # Check return status good
        self.assertIn('result', add_response)
        self.assertEqual(add_response['result'], 'success')

        # Verify image added
        post_add_images = self.api.get_images()
        self.assertEqual(orig_images['total'] + 1, post_add_images['total'])

        # Cancel/Discard added image
        cancel_resp = self.api.cancel_image(image_file_name)
        self.assertEqual(cancel_resp['data'], 'success')

        # Verify image cancelled/discarded
        post_cancel_images = self.api.get_images()
        self.assertEqual(orig_images['total'], post_cancel_images['total'])

    def test_api_get_images(self):
        ''' Verify get images
        '''
        result = self.api.get_images()
        self.assertIsNotNone(result)
        self.assertEqual(result['total'], len(result['data']))

    def test_api_get_image_bundles(self):
        ''' Verify get image bundles
        '''
        result = self.api.get_image_bundles()
        self.assertIsNotNone(result)
        self.assertEqual(result['total'], len(result['data']))

    def test_api_save_update_delete_image_bundle(self):
        ''' Verify save_image_bundle and update_image_bundle
        '''
        # pylint: disable=too-many-locals
        # Get an existing bundle
        bundles = self.api.get_image_bundles()
        total = bundles['total']
        bundle = self.api.get_image_bundle_by_name(bundles['data'][0]['name'])

        # Get the list of images from the existing bundle
        images = bundle['images']
        # Remove the unused keys from the images
        remove_keys = ['appliedContainersCount', 'appliedDevicesCount',
                       'factoryId', 'id', 'imageFile', 'imageFileName',
                       'isHotFix', 'uploadedDateinLongFormat', 'user']
        for image in images:
            for key in remove_keys:
                image.pop(key, None)

        # Create a new bundle with the same images
        original_name = 'test_image_bundle_%d' % time.time()
        result = self.api.save_image_bundle(original_name, images)
        expected = r'Bundle\s*:\s+%s successfully created' % original_name
        self.assertRegexpMatches(result['data'], expected)

        # Assert bundle added
        new_bundles = self.api.get_image_bundles()
        new_total = new_bundles['total']
        self.assertEqual(total + 1, new_total)

        # Get the bundle ID from the new bundle
        bundle = self.api.get_image_bundle_by_name(original_name)
        bundle_id = bundle['id']

        # Update the name of the bundle and mark it as uncertified
        updated_name = original_name + "_updated"
        result = self.api.update_image_bundle(bundle_id, updated_name, images,
                                              certified=False)
        self.assertRegexpMatches(result['data'],
                                 'Image bundle updated successfully')

        # Verify the updated bundle name has the correct bundle ID
        # and is not a certified image bundle
        bundle = self.api.get_image_bundle_by_name(updated_name)
        bundle_id = bundle['id']
        bundle_name = bundle['name']
        self.assertEqual(bundle['id'], bundle_id)
        self.assertEqual(bundle['isCertifiedImage'], 'false')

        # Verify the original bundle name does not exist
        bundle = self.api.get_image_bundle_by_name(original_name)
        self.assertIsNone(bundle)

        # Remove new bundle
        result = self.api.delete_image_bundle(bundle_id,
                                              bundle_name)
        self.assertEqual(result['data'], 'success')

        # Assert new bundle successfully removed
        new_bundles = self.api.get_image_bundles()
        new_total = new_bundles['total']
        self.assertEqual(total, new_total)

    def test_api_get_image_bundle_by_name(self):
        ''' Verify get image bundle by name
        '''
        bundles = self.api.get_image_bundles()
        if bundles['total'] > 0:
            bundle_name = bundles['data'][0]['name']
            bundle = self.api.get_image_bundle_by_name(bundle_name)
            self.assertEqual(bundle['name'], bundle_name)

    def test_api_get_image_bundle_by_name_doesnt_exist(self):
        ''' Verify get image bundle by name returns none if image bundle doesn't exist
        '''
        result = self.api.get_image_bundle_by_name('nonexistantimagebundle')
        self.assertIsNone(result)

    def test_api_apply_remove_image_device(self):
        ''' Verify task gets created when applying an image bundle to a device.
            This test only runs if at least one image bundle and one device
            exist in the CVP instance being used for testing.
        '''
        bundles = self.api.get_image_bundles()
        devices = self.api.get_inventory()
        # Verify at least one image bundle and device exist
        if bundles['total'] > 0 and len(devices) > 0:
            # Get device and image bundle
            b = self.api.get_image_bundle_by_name(bundles['data'][0]['name'])
            d = self.api.get_device_by_name(devices[0]['fqdn'])
            applied_devices_count = b['appliedDevicesCount']

            # Apply image and verify at least one task id was created
            result = self.api.apply_image_to_device(b, d)
            self.assertIsNotNone(result)
            self.assertEqual(result['data']['status'], 'success')
            taskids = result['data']['taskIds']
            self.assertIsNotNone(taskids)

            # Verify image bundle has been applied to one additional device
            b = self.api.get_image_bundle_by_name(bundles['data'][0]['name'])
            self.assertEqual((applied_devices_count + 1),
                             b['appliedDevicesCount'])

            # Verify task was created and in pending state
            task = self.api.get_task_by_id(taskids[0])
            self.assertIsNotNone(task)
            self.assertEqual(task['workOrderUserDefinedStatus'], 'Pending')

            # Cancel task and verify it is cancelled
            self.api.cancel_task(taskids[0])
            time.sleep(1)
            task = self.api.get_task_by_id(taskids[0])
            self.assertIsNotNone(task)
            self.assertEqual(task['workOrderUserDefinedStatus'], 'Cancelled')

            # Un-apply image bundle from device
            result = self.api.remove_image_from_device(b, d)
            self.assertIsNotNone(result)
            self.assertEqual(result['data']['status'], 'success')
            taskids = result['data']['taskIds']
            self.assertIsNotNone(taskids)

            # Verify image bundle applied to one less device
            b = self.api.get_image_bundle_by_name(bundles['data'][0]['name'])
            self.assertEqual(applied_devices_count, b['appliedDevicesCount'])

    def test_api_apply_remove_image_container(self):
        ''' Verify image bundle is applied to container and removed.
            Test only runs if at least one image bundle exists. Test creates
            a container to apply bundle then removes the container at the end.
        '''
        bundles = self.api.get_image_bundles()
        if bundles['total'] > 0:
            # Create container, get container info, get bundle info
            name = 'imagecontainer'
            parent = self.container
            self.api.add_container(name, parent['name'], parent['key'])
            c = self.api.get_container_by_name(name)
            b = self.api.get_image_bundle_by_name(bundles['data'][0]['name'])
            applied_container_count = b['appliedContainersCount']

            # Apply bundle to new container
            result = self.api.apply_image_to_container(b, c)
            self.assertIsNotNone(result)
            b = self.api.get_image_bundle_by_name(bundles['data'][0]['name'])
            self.assertEqual(b['appliedContainersCount'],
                             (applied_container_count + 1))

            # Remove bundle from container
            result = self.api.remove_image_from_container(b, c)
            self.assertIsNotNone(result)
            b = self.api.get_image_bundle_by_name(bundles['data'][0]['name'])
            self.assertEqual(b['appliedContainersCount'],
                             applied_container_count)

            # Remove container
            self.api.delete_container(name, c['key'], parent['name'],
                                      parent['key'])

    def test_api_inventory(self):
        ''' Verify add_device_to_inventory and delete_device(s)
        '''
        # Get a device
        full_inv = self.api.get_inventory()
        device = full_inv[0]
        # Record number of current/original non connected devices
        orig_non_connect_count = self.api.get_non_connected_device_count()
        # Get devices current container assigned
        orig_cont = self.api.get_parent_container_for_device(device['key'])
        # Get devices current configlets
        orig_configlets = self.api.get_configlets_by_device_id(device['key'])
        # delete from inventory
        self.api.delete_device(device['systemMacAddress'])
        # sleep to allow delete to complete
        time.sleep(1)
        # verify not found in inventory
        res = self.api.get_device_by_name(device['fqdn'])
        self.assertEqual(res, {})
        # add back to inventory
        self.api.add_device_to_inventory(device['ipAddress'],
                                         orig_cont['name'],
                                         orig_cont['key'])
        # get non connected device count until it is back to equal or less
        # than the original non connected device count
        non_connect_count = self.api.get_non_connected_device_count()
        for _ in range(3):
            if non_connect_count <= orig_non_connect_count:
                break
            time.sleep(1)
            non_connect_count = self.api.get_non_connected_device_count()
        results = self.api.save_inventory()
        # Save Inventory is deprecated for 2018.2 and beyond
        if self.clnt.apiversion == 'v1':
            self.assertEqual(results['data'], 1)
        else:
            save_msg = 'Save Inventory not implemented/necessary for' +\
                       ' CVP 2018.2 and beyond'
            self.assertEqual(results['data'], 0)
            self.assertEqual(results['message'], save_msg)
        post_save_inv = self.api.get_inventory()
        self.assertEqual(len(post_save_inv), len(full_inv))
        # verify device is found in inventory again
        re_added_dev = self.api.get_device_by_name(device['fqdn'])
        self.assertEqual(re_added_dev['systemMacAddress'],
                         device['systemMacAddress'])
        # apply original configlets back to device
        results = self.api.apply_configlets_to_device("test_api_inventory",
                                                      device, orig_configlets,
                                                      create_task=True)
        # execute returned task and wait for it to complete
        task_res = self.api.execute_task(results['data']['taskIds'][0])
        self.assertEqual(task_res, None)
        task_status = self.api.get_task_by_id(results['data']['taskIds'][0])
        while task_status['taskStatus'] != 'COMPLETED':
            task_status = self.api.get_task_by_id(
                results['data']['taskIds'][0])
            time.sleep(1)

        # delete from inventory
        # self.api.delete_device(device['systemMacAddress'])
        # verify not found in inventory
        # res = self.api.get_device_by_name(device['fqdn'])
        # self.assertEqual(res, {})
        # dut = self.duts[0]
        # self.api.retry_add_to_inventory(device['ipAddress'],
        #                                device['systemMacAddress'],
        #                                dut['username'], dut['password'])

    def test_api_change_control(self):
        ''' Verify get_change_control_info and execute_change_control.
        '''
        # Set client apiversion if it is not already set
        if self.clnt.apiversion is None:
            self.api.get_cvp_info()
        chg_ctrl_name = 'test_api_%d' % time.time()
        (task_id, _) = self._create_task()
        chg_ctrl_tasks = [{
            'taskId': task_id,
            'taskOrder': 1
        }]
        chg_ctrl = self.api.create_change_control(chg_ctrl_name,
                                                  chg_ctrl_tasks,
                                                  '', '', '')
        cc_id = chg_ctrl['ccId']

        # Verify the pending change control information
        chg_ctrl_pending = self.api.get_change_control_info(cc_id)
        self.assertEqual(chg_ctrl_pending['status'], 'Pending')

        # Execute the change control
        self.api.execute_change_controls([cc_id])

        # Verify the in progress/completed change control information
        chg_ctrl_executed = self.api.get_change_control_info(cc_id)
        self.assertIn(chg_ctrl_executed['status'], ('Inprogress', 'Completed'))
        # Wait until change control is completed before continuing
        # to next test
        for _ in range(3):
            chg_ctrl_executed = self.api.get_change_control_info(cc_id)
            if chg_ctrl_executed['status'] == 'Completed':
                break
            else:
                time.sleep(2)
        # For 2018.2 give a few extra seconds for device status to get
        # back in compliance.
        if self.clnt.apiversion == 'v2':
            time.sleep(5)

    def test_api_filter_topology(self):
        ''' Verify filter_topology.
        '''
        # Set client apiversion if it is not already set
        if self.clnt.apiversion is None:
            self.api.get_cvp_info()
        # Verify the test container topology returns the test device info
        topology = self.api.filter_topology(node_id=self.container['key'])

        # Verify the test device is present in the returned data
        exp_device_key = self.device['key']
        topo_devices = [x['key'] for
                        x in topology['topology']['childNetElementList']]
        self.assertIn(exp_device_key, topo_devices)

        # Verify the test device data is consistent
        topo_dev_data = [x for x in topology['topology']['childNetElementList']
                         if x['key'] == exp_device_key][0]
        # The tempAction field can be either None or [] when empty,
        # so skip this in the comparison.
        topo_dev_data.pop('tempAction', None)
        known_dev_data = dict(self.device)
        known_dev_data.pop('tempAction', None)
        # The device containerName field appears to be null for filter
        # topology calls where the nodeID is a container ID.
        # Skip this in comparison
        topo_dev_data.pop('containerName', None)
        known_dev_data.pop('containerName', None)

        # Test expected parameter keys are in return data.
        # Test values for parameters with consistent return values
        # Ignore comparing values for keys with
        # known different return value formats
        diff_val_form_keys = ['dcaKey', 'modelName', 'isDANZEnabled',
                              'deviceInfo', 'ztpMode', 'isMLAGEnabled']
        for key in topo_dev_data:
            self.assertIn(key, known_dev_data)
            if self.clnt.apiversion == 'v1' or key not in diff_val_form_keys:
                self.assertEqual(topo_dev_data[key], known_dev_data[key])
示例#6
0
class TestCvpClient(DutSystemTest):
    ''' Test cases for the CvpClient class.
    '''
    # pylint: disable=too-many-public-methods
    NEW_PASSWORD = '******'

    def setUp(self):
        ''' Instantiate the CvpClient class.
            Log messages to the /tmp/TestCvpClient.log
        '''
        super(TestCvpClient, self).setUp()
        self.clnt = CvpClient(filename='/tmp/TestCvpClient.log')
        self.assertIsNotNone(self.clnt)

    def tearDown(self):
        ''' Destroy the CvpClient class.
        '''
        super(TestCvpClient, self).tearDown()
        self.clnt = None

    def _change_passwd(self, nodes, username, old_password, new_password):
        ''' Helper method to change the user password on CVP.
        '''
        # Create a new connection to handle the request.
        clnt = CvpClient(filename='/tmp/TestCvpClient.log')
        clnt.connect(nodes, username, old_password)
        data = {
            'user': {
                'userId': username,
                'password': new_password,
                'email': '*****@*****.**',
                'userStatus': 'Enabled',
            },
            'roles': [
                'network-admin'
            ]
        }
        result = clnt.post("/user/updateUser.do?userId=%s" % username, data)
        self.assertEqual('success', result['data'])

    def test_clnt_init(self):
        ''' Verify CvpClient init
        '''
        clnt = CvpClient()
        self.assertIsNotNone(clnt)

    def test_clnt_init_syslog(self):
        ''' Verify CvpClient init with syslog argument
        '''
        clnt = CvpClient(syslog=True)
        self.assertIsNotNone(clnt)

    def test_clnt_init_syslog_filename(self):
        ''' Verify CvpClient init with syslog and filename argument
        '''
        logfile = '/tmp/foo'
        clnt = CvpClient(syslog=True, logger='cvpracTmp', filename=logfile)
        self.assertIsNotNone(clnt)
        os.remove(logfile)

    def test_connect_http_good(self):
        ''' Verify http connection succeeds to a single CVP node
            Uses default protocol and port.
        '''
        dut = self.duts[0]
        self.clnt.connect([dut['node']], dut['username'], dut['password'])

    def test_connect_https_good(self):
        ''' Verify https connection succeeds to a single CVP node
            Uses https protocol and port.
        '''
        dut = self.duts[0]
        self.clnt.connect([dut['node']], dut['username'], dut['password'],
                          protocol='https')

    def test_connect_username_bad(self):
        ''' Verify connect fails with bad username.
        '''
        dut = self.duts[0]
        with self.assertRaises(CvpLoginError):
            self.clnt.connect([dut['node']], 'username', dut['password'])

    def test_connect_password_bad(self):
        ''' Verify connect fails with bad password.
        '''
        dut = self.duts[0]
        with self.assertRaises(CvpLoginError):
            self.clnt.connect([dut['node']], dut['username'], 'password')

    def test_connect_node_bad(self):
        ''' Verify connection fails to a single bogus CVP node
        '''
        with self.assertRaises(CvpLoginError):
            self.clnt.connect(['bogus'], 'username', 'password',
                              connect_timeout=1)

    def test_connect_non_cvp_node(self):
        ''' Verify connection fails to a non-CVP node
        '''
        with self.assertRaises(CvpLoginError):
            self.clnt.connect(['localhost'], 'username', 'password')

    def test_connect_all_nodes_bad(self):
        ''' Verify connection fails to a single bogus CVP node
        '''
        with self.assertRaises(CvpLoginError):
            self.clnt.connect(['bogus1', 'bogus2', 'bogus3'], 'username',
                              'password', connect_timeout=1)

    def test_connect_n1_bad_n2_good(self):
        ''' Verify connect succeeds even if one node is bad
        '''
        dut = self.duts[0]
        self.clnt.connect(['bogus', dut['node']], dut['username'],
                          dut['password'], connect_timeout=2)

    def test_connect_nodes_arg_bad(self):
        ''' Verify non-list nodes argument raises a TypeError
        '''
        with self.assertRaises(TypeError):
            self.clnt.connect('bogus', 'username', 'password')

    def test_connect_port_bad(self):
        ''' Verify non-http protocol with default port raises a TypeError
        '''
        dut = self.duts[0]
        with self.assertRaises(ValueError):
            self.clnt.connect([dut['node']], dut['username'], dut['password'],
                              protocol='bogus')

    def test_get_not_connected(self):
        ''' Verify get with no connection raises a ValueError
        '''
        with self.assertRaises(ValueError):
            self.clnt.get('/bogus')

    def test_get(self):
        ''' Verify get of CVP info
        '''
        dut = self.duts[0]
        self.clnt.connect([dut['node']], dut['username'], dut['password'])
        result = self.clnt.get('/cvpInfo/getCvpInfo.do')
        self.assertIn('version', result)

    def test_get_recover_session(self):
        ''' Verify client(get) recovers session after logout
        '''
        dut = self.duts[0]
        self.clnt.connect([dut['node']], dut['username'], dut['password'])
        result = self.clnt.post('/login/logout.do', None)
        self.assertIn('data', result)
        self.assertEqual('success', result['data'])
        result = self.clnt.get('/cvpInfo/getCvpInfo.do')
        self.assertIn('version', result)

    def test_get_recover_session_bg(self):
        ''' Verify client(get) recovers session after logout for bad/good node
        '''
        dut = self.duts[0]
        self.clnt.connect(['bogus', dut['node']], dut['username'],
                          dut['password'])
        result = self.clnt.post('/login/logout.do', None)
        self.assertIn('data', result)
        self.assertEqual('success', result['data'])
        result = self.clnt.get('/cvpInfo/getCvpInfo.do')
        self.assertIn('version', result)

    def test_get_cvp_error(self):
        ''' Verify get of bad CVP request returns an error
        '''
        dut = self.duts[0]
        self.clnt.connect([dut['node']], dut['username'], dut['password'])
        with self.assertRaises(CvpRequestError):
            self.clnt.get('/aaa/getServerById.do')

    def test_get_cvp_url_bad(self):
        ''' Verify get with bad URL returns an error
        '''
        dut = self.duts[0]
        self.clnt.connect([dut['node']], dut['username'], dut['password'])
        with self.assertRaises(CvpApiError):
            self.clnt.get('/aaa/bogus.do')

    def test_get_handle_timeout(self):
        ''' Verify get with bad URL returns an error
        '''
        dut = self.duts[0]
        self.clnt.connect([dut['node']], dut['username'], dut['password'])
        with self.assertRaises(Timeout):
            self.clnt.get('/tasks/getTasks.do', timeout=0.0001)

    def test_get_except_fail_reconnect(self):
        ''' Verify exception raised if session fails and cannot be
            re-established.
        '''
        dut = self.duts[0]
        nodes = ['bogus', dut['node']]
        self.clnt.connect(nodes, dut['username'], dut['password'])
        # Change the password for the CVP user so that a session reconnect
        # to any node will fail
        self._change_passwd(nodes, dut['username'], dut['password'],
                            self.NEW_PASSWORD)

        try:
            # Logout to end the current session and force a reconnect for the
            # next request.
            result = self.clnt.post('/login/logout.do', None)
            self.assertIn('data', result)
            self.assertEqual('success', result['data'])

        except Exception as error:
            # Should not have had an exception. Restore the CVP password
            # and re-raise the error.
            self._change_passwd(nodes, dut['username'], self.NEW_PASSWORD,
                                dut['password'])
            raise error
        try:
            # Try a get request and expect a CvpSessionLogOutError
            result = self.clnt.get('/cvpInfo/getCvpInfo.do')
        except CvpSessionLogOutError as error:
            pass
        except Exception as error:
            # Unexpected error, restore password and re-raise the error.
            self._change_passwd(nodes, dut['username'], self.NEW_PASSWORD,
                                dut['password'])
            raise error
        # Restore password
        self._change_passwd(nodes, dut['username'], self.NEW_PASSWORD,
                            dut['password'])

    def test_post_not_connected(self):
        ''' Verify post with no connection raises a ValueError
        '''
        with self.assertRaises(ValueError):
            self.clnt.post('/bogus', None)

    def test_post(self):
        ''' Verify post of CVP info
        '''
        dut = self.duts[0]
        self.clnt.connect([dut['node']], dut['username'], dut['password'])
        result = self.clnt.post('/login/logout.do', None)
        self.assertIn('data', result)
        self.assertEqual('success', result['data'])

    def test_post_recover_session(self):
        ''' Verify client(post) recovers session after logout
        '''
        dut = self.duts[0]
        self.clnt.connect([dut['node']], dut['username'], dut['password'])
        result = self.clnt.post('/login/logout.do', None)
        self.assertIn('data', result)
        self.assertEqual('success', result['data'])
        result = self.clnt.post('/login/logout.do', None)
        self.assertEqual('success', result['data'])

    def test_post_recover_session_bg(self):
        ''' Verify client(post) recovers session after logout for bad/good node
        '''
        dut = self.duts[0]
        self.clnt.connect(['bogus', dut['node']], dut['username'],
                          dut['password'])
        result = self.clnt.post('/login/logout.do', None)
        self.assertIn('data', result)
        self.assertEqual('success', result['data'])
        result = self.clnt.post('/login/logout.do', None)
        self.assertEqual('success', result['data'])

    def test_post_cvp_bad_schema(self):
        ''' Verify post with bad schema returns an error
        '''
        dut = self.duts[0]
        self.clnt.connect([dut['node']], dut['username'], dut['password'])
        with self.assertRaises(CvpApiError):
            self.clnt.post('/aaa/saveAAADetails.do', None)

    def test_post_cvp_url_bad(self):
        ''' Verify post with bad URL returns an error
        '''
        dut = self.duts[0]
        self.clnt.connect([dut['node']], dut['username'], dut['password'])
        with self.assertRaises(CvpApiError):
            self.clnt.post('/aaa/bogus.do', None)

    def test_post_except_fail_reconn(self):
        ''' Verify exception raised if session fails and cannot be
            re-established.
        '''
        dut = self.duts[0]
        nodes = ['bogus', dut['node']]
        self.clnt.connect(nodes, dut['username'], dut['password'])
        # Change the password for the CVP user so that a session reconnect
        # to any node will fail
        self._change_passwd(nodes, dut['username'], dut['password'],
                            self.NEW_PASSWORD)

        try:
            # Logout to end the current session and force a reconnect for the
            # next request.
            result = self.clnt.post('/login/logout.do', None)
            self.assertIn('data', result)
            self.assertEqual('success', result['data'])

        except Exception as error:
            # Should not have had an exception. Restore the CVP password
            # and re-raise the error.
            self._change_passwd(nodes, dut['username'], self.NEW_PASSWORD,
                                dut['password'])
            raise error
        try:
            # Try a post request and expect a CvpSessionLogOutError
            result = self.clnt.post('/login/logout.do', None)
        except CvpSessionLogOutError as error:
            pass
        except Exception as error:
            # Unexpected error, restore password and re-raise the error.
            self._change_passwd(nodes, dut['username'], self.NEW_PASSWORD,
                                dut['password'])
            raise error
        # Restore password
        self._change_passwd(nodes, dut['username'], self.NEW_PASSWORD,
                            dut['password'])
示例#7
0
from cvprac.cvp_client import CvpClient
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
import requests.packages.urllib3
requests.packages.urllib3.disable_warnings()

clnt = CvpClient()
clnt.connect(nodes=['cvp1'], username="******",password="******")

ts = "2021-11-19T15:04:05.0Z" # rfc3339 time
uri = "/api/v3/services/compliancecheck.Compliance/GetConfig"

# Fetch the inventory
inventory = clnt.api.get_inventory()

# Iterate through all devices and get the running-config at the specified time for each device
for device in inventory:
    sn = device['serialNumber']
    data = {"request":{
        "device_id": sn,
        "timestamp": ts,
        "type":"RUNNING_CONFIG"
        }
    }
    try:
        resultRunningConfig = clnt.post(uri, data=data)[0]['config']
        with open(device['hostname']+'.cfg','w') as f:
            f.write(resultRunningConfig)
    except Exception as e:
        print("Not able to get configuration for device {} - exception {}".format(device['fqdn'], e))
for snapshot template: SnapshotOfConfig1 and container-ID for container: mlane1,
then POST a request for CVP to capture snapshot configs for every switch in the
cotainer.
'''
from cvprac.cvp_client import CvpClient  # Import CvpClient class module
clnt = CvpClient()  # create clnt object from CvpClient class
clnt.connect(['192.168.1.143'], 'cvpadmin', 'cvpadmin1')  # Connect to CVP

#  Next get Container information for mlane1
result = clnt.get(
    '/inventory/add/searchContainers.do?queryparam=mlane1&startIndex=0&endIndex=0'
)
# And, extract the container key or container-ID.
contnr = result['data'][0]['key']

# Get information for snapshot template: SnapshotOfConfig1
result2 = clnt.get(
    '/snapshot/getSnapshotTemplates.do?startIndex=0&endIndex=0&queryparam=SnapshotOfConfig1'
)
# And, extract it's key or template-ID
snapTemplt = result2['data'][0]['key']

# Build a dictionary which includes: "templateid" and "containerid"
parms2 = {"templateId": snapTemplt, "containerId": contnr}

# Execute a POST to the Container Snapshot module and pass the parameter dictionary: parms2
snapResult = clnt.post('/snapshot/captureContainerLevelSnapshot.do', parms2)

# Print out the returned result which shoud be "success"
print snapResult