diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 000000000..8531b0313 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,4 @@ +[run] +omit = + lib/jnpr/junos/cfg/phyport/* + lib/jnpr/junos/cfg/srx/* diff --git a/development.txt b/development.txt index c9377f21e..5b8904b21 100644 --- a/development.txt +++ b/development.txt @@ -4,3 +4,4 @@ coverage # http://nedbatchelder.com/code/coverage/ mock # http://www.voidspace.org.uk/python/mock/ nose # http://nose.readthedocs.org/en/latest/ pep8 # https://github.com/jcrocholl/pep8 +pyflakes # https://launchpad.net/pyflakes \ No newline at end of file diff --git a/lib/jnpr/junos/device.py b/lib/jnpr/junos/device.py index 4105e4d84..1fadf7fe3 100644 --- a/lib/jnpr/junos/device.py +++ b/lib/jnpr/junos/device.py @@ -294,7 +294,7 @@ def open(self, *vargs, **kvargs): except socket.gaierror: # invalid DNS name, so unreachable - raise EzErrors.ConnectUnknownError(self) + raise EzErrors.ConnectUnknownHostError(self) except Exception as err: # anything else, we will re-raise as a diff --git a/lib/jnpr/junos/utils/sw.py b/lib/jnpr/junos/utils/sw.py index a41568c6b..e720f2350 100644 --- a/lib/jnpr/junos/utils/sw.py +++ b/lib/jnpr/junos/utils/sw.py @@ -132,7 +132,7 @@ def _scp_progress(_path, _total, _xfrd): # execute the secure-copy with the Python SCP module - with SCP(self._dev, progress=_scp_progress) as scp: + with SCP(self._dev, progress=_scp_progress) as scp: scp.put(package, remote_path) # ------------------------------------------------------------------------- diff --git a/tests/unit/facts/test_chassis.py b/tests/unit/facts/test_chassis.py index 6fc7b3bb7..f2ce8e685 100644 --- a/tests/unit/facts/test_chassis.py +++ b/tests/unit/facts/test_chassis.py @@ -3,11 +3,13 @@ import unittest from nose.plugins.attrib import attr -from mock import patch +from mock import patch, MagicMock +from lxml import etree import os from jnpr.junos import Device from jnpr.junos.facts.chassis import facts_chassis as chassis +from jnpr.junos.exception import ConnectNotMasterError from ncclient.manager import Manager, make_device_handler from ncclient.transport import SSHSession @@ -30,6 +32,12 @@ def test_2RE_true(self, mock_execute): chassis(self.dev, self.facts) self.assertTrue(self.facts['2RE']) + def test_chassis_exception_ConnectNotMasterError(self): + xmldata = etree.XML('test') + self.dev.rpc.get_chassis_inventory = MagicMock(side_effect=xmldata) + with self.assertRaises(ConnectNotMasterError): + chassis(self.dev, self.facts) + def _read_file(self, fname): from ncclient.xml_ import NCElement diff --git a/tests/unit/facts/test_domain.py b/tests/unit/facts/test_domain.py new file mode 100644 index 000000000..fcdd77639 --- /dev/null +++ b/tests/unit/facts/test_domain.py @@ -0,0 +1,42 @@ +__author__ = "Nitin Kumar, Rick Sherman" +__credits__ = "Jeremy Schulman" + +import unittest +from nose.plugins.attrib import attr +from mock import patch + +from jnpr.junos.facts.domain import facts_domain +from jnpr.junos import Device + + +@attr('unit') +class TestDomain(unittest.TestCase): + + def setUp(self): + self.dev = Device(host='1.1.1.1', user='rick', password='password123', + gather_facts=False) + self.facts = {} + + @patch('jnpr.junos.facts.domain.FS.cat') + def test_resolv_conf(self, mock_fs_cat): + mock_fs_cat.return_value =\ + """# domain juniper.net + search englab.juniper.net spglab.juniper.net juniper.net jnpr.net + nameserver 10.11.12.13 + """ + self.facts['hostname'] = 'test' + facts_domain(self.dev, self.facts) + self.assertEqual(self.facts['domain'], 'juniper.net') + self.assertEqual(self.facts['fqdn'], 'test.juniper.net') + + @patch('jnpr.junos.facts.domain.FS.cat') + def test_resolv_conf_no_domain(self, mock_fs_cat): + mock_fs_cat.return_value =\ + """ + search englab.juniper.net spglab.juniper.net juniper.net jnpr.net + nameserver 10.11.12.13 + """ + self.facts['hostname'] = 'test' + facts_domain(self.dev, self.facts) + self.assertIsNone(self.facts['domain']) + self.assertEqual(self.facts['fqdn'], 'test') diff --git a/tests/unit/facts/test_swver.py b/tests/unit/facts/test_swver.py index 34ab087c1..4c0a906b2 100644 --- a/tests/unit/facts/test_swver.py +++ b/tests/unit/facts/test_swver.py @@ -27,22 +27,22 @@ def test_version_info_repr(self): 'type=R, minor=7, build=5)') def test_version_info_lt(self): - self.assertTrue(version_info('13.3-20131120') < (14, 1)) + self.assertLess(version_info('13.3-20131120'), (14, 1)) def test_version_info_lt_eq(self): - self.assertTrue(version_info('13.3-20131120') <= (14, 1)) + self.assertLessEqual(version_info('13.3-20131120'), (14, 1)) def test_version_info_gt(self): - self.assertTrue(version_info('13.3-20131120') > (12, 1)) + self.assertGreater(version_info('13.3-20131120'), (12, 1)) def test_version_info_gt_eq(self): - self.assertTrue(version_info('13.3-20131120') >= (12, 1)) + self.assertGreaterEqual(version_info('13.3-20131120'), (12, 1)) def test_version_info_eq(self): - self.assertTrue(version_info('13.3-20131120') == (13, 3)) + self.assertEqual(version_info('13.3-20131120'), (13, 3)) def test_version_info_not_eq(self): - self.assertTrue(version_info('13.3-20131120') != (15, 3)) + self.assertNotEqual(version_info('13.3-20131120'), (15, 3)) @attr('unit') @@ -63,6 +63,14 @@ def test_swver(self, mock_execute): software_version(self.dev, self.facts) self.assertEqual(self.facts['version'], '12.3R6.6') + @patch('jnpr.junos.Device.execute') + def test_swver_hostname_none(self, mock_execute): + mock_execute.side_effect = self._mock_manager + self.facts['master'] = 'RE5' + self.facts['version_RE5'] = '15.3R6.6' + software_version(self.dev, self.facts) + self.assertEqual(self.facts['version'], '15.3R6.6') + # --> JLS, there should always be a facts['master'] assigned. # @patch('jnpr.junos.Device.execute') # def test_swver_master_none(self, mock_execute): @@ -73,7 +81,7 @@ def test_swver(self, mock_execute): @patch('jnpr.junos.Device.execute') @patch('jnpr.junos.facts.swver.re.findall') - def test_swver_exception_handling(self, mock_re_findall, mock_execute): + def test_swver_exception_handling(self, mock_re_findall, mock_execute): mock_execute.side_effect = self._mock_manager mock_re_findall.side_effect = IndexError self.facts['master'] = 'RE0' diff --git a/tests/unit/rpc-reply/show-configuration.xml b/tests/unit/rpc-reply/show-configuration.xml new file mode 100644 index 000000000..ae8852d8d --- /dev/null +++ b/tests/unit/rpc-reply/show-configuration.xml @@ -0,0 +1,154 @@ + + +## Last commit: 2014-03-24 16:34:32 UTC by rick +version 12.1X46-D15.3; +system { + host-name firefly; + root-authentication { + encrypted-password "$1$kmSqRIU6$9EogG7ow0DWiww9mev8.b."; ## SECRET-DATA + } + login { + user rick { + uid 2000; + class super-user; + authentication { + encrypted-password "$1$wDDri7eJ$2Ot4pfE29PgVbeutIveov1"; ## SECRET-DATA + } + } + } + services { + ssh; + netconf { + ssh; + } + web-management { + http { + interface ge-0/0/0.0; + } + } + } + syslog { + user * { + any emergency; + } + file messages { + any any; + authorization info; + } + file interactive-commands { + interactive-commands any; + } + } + license { + autoupdate { + url https://ae1.juniper.net/junos/key_retrieval; + } + } +} +interfaces { + ge-0/0/0 { + unit 0 { + family inet { + address 10.0.0.31/24; + } + } + } + ge-0/0/1 { + unit 0 { + family inet { + address 192.168.2.1/24; + } + } + } +} +routing-options { + static { + route 0.0.0.0/0 next-hop 10.0.0.1; + } +} +security { + screen { + ids-option untrust-screen { + icmp { + ping-death; + } + ip { + source-route-option; + tear-drop; + } + tcp { + syn-flood { + alarm-threshold 1024; + attack-threshold 200; + source-threshold 1024; + destination-threshold 2048; + queue-size 2000; ## Warning: 'queue-size' is deprecated + timeout 20; + } + land; + } + } + } + policies { + from-zone trust to-zone trust { + policy default-permit { + match { + source-address any; + destination-address any; + application any; + } + then { + permit; + } + } + } + from-zone trust to-zone untrust { + policy default-permit { + match { + source-address any; + destination-address any; + application any; + } + then { + permit; + } + } + } + from-zone untrust to-zone trust { + policy default-deny { + match { + source-address any; + destination-address any; + application any; + } + then { + deny; + } + } + } + } + zones { + security-zone trust { + tcp-rst; + } + security-zone untrust { + screen untrust-screen; + interfaces { + ge-0/0/0.0 { + host-inbound-traffic { + system-services { + http; + https; + ssh; + telnet; + dhcp; + netconf; + } + } + } + } + } + } +} + + diff --git a/tests/unit/rpc-reply/show-system-alarms.xml b/tests/unit/rpc-reply/show-system-alarms.xml new file mode 100644 index 000000000..44c9567a7 --- /dev/null +++ b/tests/unit/rpc-reply/show-system-alarms.xml @@ -0,0 +1,5 @@ + +1 alarms currently active +Alarm time Class Description +2014-04-17 09:09:21 UTC Minor Rescue configuration is not set + \ No newline at end of file diff --git a/tests/unit/rpc-reply/show-system-uptime-rpc.xml b/tests/unit/rpc-reply/show-system-uptime-rpc.xml new file mode 100644 index 000000000..415366b6c --- /dev/null +++ b/tests/unit/rpc-reply/show-system-uptime-rpc.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/tests/unit/templates/config-example.xml b/tests/unit/templates/config-example.xml new file mode 100644 index 000000000..49be2f79b --- /dev/null +++ b/tests/unit/templates/config-example.xml @@ -0,0 +1,4 @@ +system { + host-name {{ host_name }}; + domain-name {{ domain_name }}; +} diff --git a/tests/unit/test_device.py b/tests/unit/test_device.py index 061d19766..7837474ba 100644 --- a/tests/unit/test_device.py +++ b/tests/unit/test_device.py @@ -1,7 +1,6 @@ -''' +__author__ = "Rick Sherman, Nitin Kumar" +__credits__ = "Jeremy Schulman" -@author: rsherman -''' import unittest from nose.plugins.attrib import attr from mock import MagicMock, patch, mock_open @@ -10,10 +9,13 @@ from ncclient.manager import Manager, make_device_handler from ncclient.transport import SSHSession +import ncclient.transport.errors as NcErrors from jnpr.junos.facts.swver import version_info from jnpr.junos import Device from jnpr.junos.exception import RpcError +from jnpr.junos import exception as EzErrors + facts = {'domain': None, 'hostname': 'firefly', 'ifd_style': 'CLASSIC', 'version_info': version_info('12.1X46-D15.3'), @@ -66,6 +68,55 @@ def setUp(self, mock_connect): def tearDown(self, mock_session): self.dev.close() + @patch('jnpr.junos.device.netconf_ssh') + def test_device_ConnectAuthError(self, mock_manager): + mock_manager.connect.side_effect = NcErrors.AuthenticationError + with self.assertRaises(EzErrors.ConnectAuthError): + self.dev.open() + + @patch('jnpr.junos.device.netconf_ssh') + def test_device_ConnectRefusedError(self, mock_manager): + mock_manager.connect.side_effect = NcErrors.SSHError + with self.assertRaises(EzErrors.ConnectRefusedError): + self.dev.open() + + @patch('jnpr.junos.device.netconf_ssh') + @patch('jnpr.junos.device.datetime') + def test_device_ConnectTimeoutError(self, mock_datetime, mock_manager): + NcErrors.SSHError.message = 'cannot open' + mock_manager.connect.side_effect = NcErrors.SSHError + from datetime import timedelta, datetime + currenttime = datetime.now() + mock_datetime.datetime.now.side_effect = [currenttime, + currenttime + timedelta(minutes=4)] + with self.assertRaises(EzErrors.ConnectTimeoutError): + self.dev.open() + + @patch('jnpr.junos.device.netconf_ssh') + @patch('jnpr.junos.device.datetime') + def test_device_diff_err_message(self, mock_datetime, mock_manager): + NcErrors.SSHError.message = 'why are you trying :)' + mock_manager.connect.side_effect = NcErrors.SSHError + from datetime import timedelta, datetime + currenttime = datetime.now() + mock_datetime.datetime.now.side_effect = [currenttime, + currenttime + timedelta(minutes=4)] + with self.assertRaises(EzErrors.ConnectError): + self.dev.open() + + @patch('jnpr.junos.device.netconf_ssh') + def test_device_ConnectUnknownHostError(self, mock_manager): + import socket + mock_manager.connect.side_effect = socket.gaierror + with self.assertRaises(EzErrors.ConnectUnknownHostError): + self.dev.open() + + @patch('jnpr.junos.device.netconf_ssh') + def test_device_other_error(self, mock_manager): + mock_manager.connect.side_effect = TypeError + with self.assertRaises(EzErrors.ConnectError): + self.dev.open() + def test_device_property_logfile_isinstance(self): mock = MagicMock() with patch('__builtin__.open', mock): @@ -98,9 +149,22 @@ def test_device_repr(self): @patch('jnpr.junos.device.os') @patch('__builtin__.open') - def test_device__sshconf_lkup(self, os_mock, open_mock): + @patch('paramiko.config.SSHConfig.lookup') + def test_device__sshconf_lkup(self, os_mock, open_mock, mock_paramiko): os_mock.path.exists.return_value = True self.dev._sshconf_lkup() + mock_paramiko.assert_called_any() + + @patch('os.getenv') + def test_device__sshconf_lkup_path_not_exists(self, mock_env): + mock_env.return_value = '/home/test' + self.assertIsNone(self.dev._sshconf_lkup()) + + @patch('os.getenv') + def test_device__sshconf_lkup_home_not_defined(self, mock_env): + mock_env.return_value = None + self.assertIsNone(self.dev._sshconf_lkup()) + mock_env.assert_called_with('HOME') @patch('ncclient.manager.connect') @patch('jnpr.junos.Device.execute') @@ -130,24 +194,24 @@ def test_device_facts(self, mock_execute): assert self.dev.facts['version'] == facts['version'] def test_device_hostname(self): - assert self.dev.hostname == '1.1.1.1' + self.assertEqual(self.dev.hostname, '1.1.1.1') def test_device_user(self): - assert self.dev.user == 'rick' + self.assertEqual(self.dev.user, 'rick') def test_device_get_password(self): - assert self.dev.password is None + self.assertIsNone(self.dev.password) def test_device_set_password(self): self.dev.password = 'secret' - assert self.dev._password == 'secret' + self.assertEqual(self.dev._password, 'secret') def test_device_get_timeout(self): - assert self.dev.timeout == 30 + self.assertEqual(self.dev.timeout, 30) def test_device_set_timeout(self): self.dev.timeout = 10 - assert self.dev.timeout == 10 + self.assertEqual(self.dev.timeout, 10) def test_device_manages(self): self.assertEqual(self.dev.manages, [], @@ -159,10 +223,26 @@ def test_device_set_facts_exception(self): except RuntimeError as ex: self.assertEqual(RuntimeError, type(ex)) - def test_device_cli(self): - self.dev.execute = MagicMock(name='execute') - self.dev.cli('show version') - assert self.dev.execute.call_args[0][0].text == 'show version' + @patch('jnpr.junos.Device.execute') + def test_device_cli(self, mock_execute): + mock_execute.side_effect = self._mock_manager + self.assertEqual(self.dev.cli('show cli directory').tag, 'cli') + + @patch('jnpr.junos.Device.execute') + def test_device_cli_conf_info(self, mock_execute): + mock_execute.side_effect = self._mock_manager + self.assertIn('ge-0/0/0', self.dev.cli('show configuration')) + + @patch('jnpr.junos.Device.execute') + def test_device_cli_output(self, mock_execute): + mock_execute.side_effect = self._mock_manager + self.assertIn('Alarm', self.dev.cli('show system alarms')) + + @patch('jnpr.junos.Device.execute') + def test_device_cli_rpc(self, mock_execute): + mock_execute.side_effect = self._mock_manager + self.assertEqual(self.dev.cli('show system uptime | display xml rpc')\ + .tag, 'get-system-uptime-information') def test_device_cli_exception(self): self.dev.rpc.cli = MagicMock(side_effect=AttributeError) @@ -171,11 +251,13 @@ def test_device_cli_exception(self): def test_device_execute(self): self.dev._conn.rpc = MagicMock(side_effect=self._mock_manager) - self.dev.execute('') + self.assertEqual(self.dev.execute('').tag, + 'directory-list') def test_device_execute_topy(self): self.dev._conn.rpc = MagicMock(side_effect=self._mock_manager) - self.dev.execute('', to_py=self._do_nothing) + self.assertEqual(self.dev.execute('', + to_py=self._do_nothing), 'Nothing') def test_device_execute_exception(self): class MyException(Exception): @@ -206,8 +288,8 @@ def test_device_execute_ValueError(self): self.assertRaises(ValueError, self.dev.execute, None) def test_device_rpcmeta(self): - assert self.dev.rpc.get_software_information.func_doc ==\ - 'get-software-information' + self.assertEqual(self.dev.rpc.get_software_information.func_doc, + 'get-software-information') def test_device_probe_timeout_zero(self): with patch('jnpr.junos.device.socket'): @@ -227,14 +309,19 @@ def test_device_probe_timeout_exception(self): mock_time.return_value = None self.assertFalse(self.dev.probe(.01)) - def test_device_bind(self): + def test_device_bind_varg(self): self.dev.bind() mock = MagicMock() - mock.__name__ = 'magic mock' - #for *args + mock.__name__ = 'magic_mock' self.dev.bind(mock) - #for **kwargs + self.assertEqual(self.dev.magic_mock.__name__, 'magic_mock') + + def test_device_bind_kvarg(self): + self.dev.bind() + mock = MagicMock() + mock.return_value = 'Test' self.dev.bind(kw=mock) + self.assertEqual(self.dev.kw, 'Test') def test_device_bind_varg_exception(self): with self.assertRaises(ValueError): @@ -255,8 +342,18 @@ def test_device_bind_kvarg_exception(self): self.dev.bind(kw=mock) def test_device_template(self): - self.dev._j2ldr = MagicMock() - self.dev.Template('test') + # Try to load the template relative to module base + try: + template = self.dev.Template('tests/unit/templates/config-example') + except: + # Try to load the template relative to test base + try: + template = self.dev.Template('templates/config-example') + except: + raise + self.assertEqual(template.render({'host_name': '1', + 'domain_name': '2'}), + 'system {\n host-name 1;\n domain-name 2;\n}') def test_device_close(self): def close_conn(): @@ -278,6 +375,10 @@ def _read_file(self, fname): fname == 'get-system-core-dumps.xml'): rpc_reply = NCElement(foo, self.dev._conn._device_handler .transform_reply()) + elif (fname == 'show-configuration.xml' or + fname == 'show-system-alarms.xml'): + rpc_reply = NCElement(foo, self.dev._conn._device_handler + .transform_reply())._NCElement__doc else: rpc_reply = NCElement(foo, self.dev._conn._device_handler .transform_reply())._NCElement__doc[0] @@ -294,6 +395,12 @@ def _mock_manager(self, *args, **kwargs): if args[0].tag == 'command': if args[0].text == 'show cli directory': return self._read_file('show-cli-directory.xml') + elif args[0].text == 'show configuration': + return self._read_file('show-configuration.xml') + elif args[0].text == 'show system alarms': + return self._read_file('show-system-alarms.xml') + elif args[0].text == 'show system uptime | display xml rpc': + return self._read_file('show-system-uptime-rpc.xml') else: raise RpcError @@ -301,8 +408,5 @@ def _mock_manager(self, *args, **kwargs): return self._read_file(args[0].tag + '.xml') def _do_nothing(self, *args, **kwargs): - return - -if __name__ == "__main__": - #import sys;sys.argv = ['', 'TestDevice.testName'] - unittest.main() + return 'Nothing' + diff --git a/tests/unit/test_jxml.py b/tests/unit/test_jxml.py index 1cae06177..47d496e30 100644 --- a/tests/unit/test_jxml.py +++ b/tests/unit/test_jxml.py @@ -27,4 +27,9 @@ def test_remove_namespaces(self): """ import xml.etree.ElementTree as ET root = ET.fromstring(xmldata) - remove_namespaces(root) + test = remove_namespaces(root) + for elem in test.getiterator(): + i = elem.tag.find('}') + if i > 0: + i = i + 1 + self.assertLessEqual(i, 0) diff --git a/tests/unit/test_rpcmeta.py b/tests/unit/test_rpcmeta.py index 7a9096f51..952e4bff7 100644 --- a/tests/unit/test_rpcmeta.py +++ b/tests/unit/test_rpcmeta.py @@ -22,9 +22,11 @@ def test_rpcmeta_constructor(self): self.assertIsInstance(self.rpc._junos, Device) @patch('jnpr.junos.device.Device.execute') - def test_rpcmeta_load_config_option(self, mock_execute_fn): + def test_rpcmeta_load_config(self, mock_execute_fn): root = etree.XML('test') self.rpc.load_config(root) + self.assertEqual(mock_execute_fn.call_args[0][0].tag, + 'load-configuration') @patch('jnpr.junos.device.Device.execute') def test_rpcmeta_load_config_option_action(self, mock_execute_fn): @@ -33,6 +35,8 @@ def test_rpcmeta_load_config_option_action(self, mock_execute_fn): set system domain-name test.juniper.net """ self.rpc.load_config(set_commands, action='set') + self.assertEqual(mock_execute_fn.call_args[0][0].get('action'), + 'set') @patch('jnpr.junos.device.Device.execute') def test_rpcmeta_option_format(self, mock_execute_fn): @@ -41,11 +45,17 @@ def test_rpcmeta_option_format(self, mock_execute_fn): set system domain-name test.juniper.net """ self.rpc.load_config(set_commands, format='text') + self.assertEqual(mock_execute_fn.call_args[0][0].get('format'), + 'text') @patch('jnpr.junos.device.Device.execute') def test_rpcmeta_exec_rpc_vargs(self, mock_execute_fn): self.rpc.system_users_information(dict(format='text')) + self.assertEqual(mock_execute_fn.call_args[0][0].get('format'), + 'text') @patch('jnpr.junos.device.Device.execute') def test_rpcmeta_exec_rpc_kvargs(self, mock_execute_fn): self.rpc.system_users_information(set_data=('test',)) + self.assertEqual(mock_execute_fn.call_args[0][0][0].text, + 'test') diff --git a/tests/unit/utils/rpc-reply/checksum.xml b/tests/unit/utils/rpc-reply/checksum.xml new file mode 100644 index 000000000..07e8ee89d --- /dev/null +++ b/tests/unit/utils/rpc-reply/checksum.xml @@ -0,0 +1,8 @@ + + + xxxx + + + + + diff --git a/tests/unit/utils/rpc-reply/file-archive.xml b/tests/unit/utils/rpc-reply/file-archive.xml new file mode 100644 index 000000000..2b2f10cd5 --- /dev/null +++ b/tests/unit/utils/rpc-reply/file-archive.xml @@ -0,0 +1,23 @@ + + + testing tgz + + + + + /var/tmp//abckLjq + + + + + + + /var/tmp//abckLjq + abc.tar + + + + + + + \ No newline at end of file diff --git a/tests/unit/utils/rpc-reply/file-list_dir.xml b/tests/unit/utils/rpc-reply/file-list_dir.xml new file mode 100644 index 000000000..1bf53dc26 --- /dev/null +++ b/tests/unit/utils/rpc-reply/file-list_dir.xml @@ -0,0 +1,21 @@ + + + + /var + 123 + + abc + + 555 + root + xyz + 1 + 2 + 1392651039 + + + + + + + diff --git a/tests/unit/utils/rpc-reply/file-list_file.xml b/tests/unit/utils/rpc-reply/file-list_file.xml new file mode 100644 index 000000000..32d25303f --- /dev/null +++ b/tests/unit/utils/rpc-reply/file-list_file.xml @@ -0,0 +1,20 @@ + + + + + /var/abc.sh + + 755 + pqr + abc + 1 + 2 + 1394693680 + + 1 + + + + + + \ No newline at end of file diff --git a/tests/unit/utils/rpc-reply/file-list_symlink.xml b/tests/unit/utils/rpc-reply/file-list_symlink.xml new file mode 100644 index 000000000..849100721 --- /dev/null +++ b/tests/unit/utils/rpc-reply/file-list_symlink.xml @@ -0,0 +1,21 @@ + + + + /var + 123 + + abc + symlink test + 555 + root + xyz + 1 + 2 + 1392651039 + + + + + + + diff --git a/tests/unit/utils/rpc-reply/file-show.xml b/tests/unit/utils/rpc-reply/file-show.xml new file mode 100644 index 000000000..10c82188e --- /dev/null +++ b/tests/unit/utils/rpc-reply/file-show.xml @@ -0,0 +1,8 @@ + + + testing cat functionality + + + + + \ No newline at end of file diff --git a/tests/unit/utils/rpc-reply/get-chassis-inventory.xml b/tests/unit/utils/rpc-reply/get-chassis-inventory.xml new file mode 100644 index 000000000..d15410a03 --- /dev/null +++ b/tests/unit/utils/rpc-reply/get-chassis-inventory.xml @@ -0,0 +1,33 @@ + + + + Chassis + aaf5fe5f9b88 + FIREFLY-PERIMETER + + Midplane + + + System IO + + + Routing Engine + FIREFLY-PERIMETER RE + + + FPC 0 + Virtual FPC + + PIC 0 + Virtual GE + + + + Power Supply 0 + + + + + + + diff --git a/tests/unit/utils/rpc-reply/get-checksum-information.xml b/tests/unit/utils/rpc-reply/get-checksum-information.xml new file mode 100644 index 000000000..3669fab43 --- /dev/null +++ b/tests/unit/utils/rpc-reply/get-checksum-information.xml @@ -0,0 +1,12 @@ + + + + MD5 + safecopy.tgz + 96a35ab371e1ca10408c3caecdbd8a67 + + + + + + \ No newline at end of file diff --git a/tests/unit/utils/rpc-reply/get-configuration.xml b/tests/unit/utils/rpc-reply/get-configuration.xml new file mode 100644 index 000000000..d4b0d42b7 --- /dev/null +++ b/tests/unit/utils/rpc-reply/get-configuration.xml @@ -0,0 +1,214 @@ + + + 12.1X46-D15.3 + + firefly + + $1$kmSqRIU6$9EogG7ow0DWiww9mev8.b. + + + + rick + 2000 + super-user + + $1$wDDri7eJ$2Ot4pfE29PgVbeutIveov1 + + + + + + + + + + + + + ge-0/0/0.0 + + + + + + * + + any + + + + + messages + + any + + + + authorization + + + + + interactive-commands + + interactive-commands + + + + + + + + https://ae1.juniper.net/junos/key_retrieval + + + + + + + ge-0/0/0 + + 0 + + +
+ 10.0.0.31/24 +
+
+
+
+
+ + ge-0/0/1 + + 0 + + +
+ 192.168.2.1/24 +
+
+
+
+
+
+ + + + 0.0.0.0/0 + 10.0.0.1 + + + + + + + untrust-screen + + + + + + + + + + 1024 + 200 + 1024 + 2048 + 2000 + 20 + + + + + + + + trust + trust + + default-permit + + any + any + any + + + + + + + + + trust + untrust + + default-permit + + any + any + any + + + + + + + + + untrust + trust + + default-deny + + any + any + any + + + + + + + + + + trust + + + + untrust + untrust-screen + + ge-0/0/0.0 + + + http + + + https + + + ssh + + + telnet + + + dhcp + + + netconf + + + + + + +
+ + + +
diff --git a/tests/unit/utils/rpc-reply/get-route-engine-information.xml b/tests/unit/utils/rpc-reply/get-route-engine-information.xml new file mode 100644 index 000000000..6dc37692c --- /dev/null +++ b/tests/unit/utils/rpc-reply/get-route-engine-information.xml @@ -0,0 +1,31 @@ + + + + Testing + 2048 + 819 + 40 + 1150 + 460 + 40 + 898 + 350 + 39 + 0 + 0 + 0 + 0 + 100 + FIREFLY-PERIMETER RE + 2014-03-26 13:15:20 UTC + 6 hours, 29 minutes, 30 seconds + Router rebooted after a normal shutdown. + 0.00 + 0.00 + 0.00 + + + + + + diff --git a/tests/unit/utils/rpc-reply/get-software-information.xml b/tests/unit/utils/rpc-reply/get-software-information.xml new file mode 100644 index 000000000..6905d08fb --- /dev/null +++ b/tests/unit/utils/rpc-reply/get-software-information.xml @@ -0,0 +1,14 @@ + + + firefly + firefly-perimeter + firefly-perimeter + + junos + JUNOS Software Release [12.1X46-D15.3] + + + + + + diff --git a/tests/unit/utils/rpc-reply/get-system-storage.xml b/tests/unit/utils/rpc-reply/get-system-storage.xml new file mode 100644 index 000000000..0f406885d --- /dev/null +++ b/tests/unit/utils/rpc-reply/get-system-storage.xml @@ -0,0 +1,15 @@ + + + + /dev/abc + 567431 + 2346455 + 234234 + 1 + / + + + + + + diff --git a/tests/unit/utils/rpc-reply/request-package-add.xml b/tests/unit/utils/rpc-reply/request-package-add.xml new file mode 100644 index 000000000..c4cd1f9bc --- /dev/null +++ b/tests/unit/utils/rpc-reply/request-package-add.xml @@ -0,0 +1,32 @@ + + + + + + + hup + + + + /var/tmp/incoming-package.31020 1742 kB 1742 kBps + Package contains junos-11.1R3.5.tgz ; renaming ... + Formatting alternate root (/dev/ad0s1a)... + /dev/ad0s1a: 299.7MB (613792 sectors) block size 16384, fragment size 2048 + using 4 cylinder groups of 74.94MB, 4796 blks, 9600 inodes. + super-block backups (for fsck -b #) at: + 32, 153504, 306976, 460448 + Removing /var/tmp/junos-11.1R3.5.tgz + Installing package '/altroot/cf/packages/install-tmp/junos-11.1R3.5-domestic' ... + Verified junos-boot-srxsme-11.1R3.5.tgz signed by PackageProduction_11_1_0 + Verified junos-srxsme-11.1R3.5-domestic signed by PackageProduction_11_1_0 + Saving boot file package in /var/sw/pkg/junos-boot-srxsme-11.1R3.5.tgz + JUNOS 11.1R3.5 will become active at next reboot + WARNING: A reboot is required to load this software correctly + WARNING: Use the 'request system reboot' command + WARNING: when software installation is complete + Saving state for rollback ... + + + 0 + + \ No newline at end of file diff --git a/tests/unit/utils/rpc-reply/request-package-rollback.xml b/tests/unit/utils/rpc-reply/request-package-rollback.xml new file mode 100644 index 000000000..5c8a05b11 --- /dev/null +++ b/tests/unit/utils/rpc-reply/request-package-rollback.xml @@ -0,0 +1,16 @@ + + + + + + member1 + + ERROR: cannot locate jroute-14.2I20140424_0657_ramas: rollback aborted + 1 + + + + + {master:member1-re0} + + diff --git a/tests/unit/utils/rpc-reply/request-package-validate.xml b/tests/unit/utils/rpc-reply/request-package-validate.xml new file mode 100644 index 000000000..bee2495ef --- /dev/null +++ b/tests/unit/utils/rpc-reply/request-package-validate.xml @@ -0,0 +1,40 @@ + +    +        +            hup +        +    +    +        Checking compatibility with configuration +        Initializing... +        rm: /var/validate/chroot/var/etc/pam.conf: Operation not permitted +        rm: /var/validate/chroot/var/etc: Directory not empty +        rm: /var/validate/chroot/var: Directory not empty +        rm: /var/validate/chroot: Directory not empty +        Using jbase-13.2I20140401_1054_katharh +        Using /var/tmp/py-extensions-i386-13.2I20140430_1237_katharh.tgz +        Checking py-extensions-i386 requirements on / +        Verified py-extensions-i386-13.2I20140430_1237_katharh signed by PackageDevelopment_13_2_0 +        Registering py-extensions-i386 as unsupported +        Using jruntime-13.2I20140401_1054_katharh +        Using jkernel-13.2I20140401_1054_katharh +        Using jroute-13.2I20140401_1054_katharh +        Using jcrypto-13.2I20140401_1054_katharh +        cp: /var/validate/chroot/var/etc/pam.conf: Operation not permitted +        Hardware Database regeneration succeeded +        Validating against /config/juniper.conf.gz +        mgd: error: schema: dbs_remap_daemon_index: could not find daemon name 'analyticsd' +        mgd: warning: schema: init: 'logical-systems-vlans' contains-node 'juniper-config vlans': not found +        mgd: error: rename failed for /var/etc/pam.conf +        mgd: commit complete +        Validation succeeded +        rm: /var/validate/chroot/var/etc/pam.conf: Operation not permitted +        rm: /var/validate/chroot/var/etc: Directory not empty +        rm: /var/validate/chroot/var: Directory not empty +        rm: /var/validate/chroot: Directory not empty +    +    0 +    +        +    + diff --git a/tests/unit/utils/rpc-reply/request-power-off.xml b/tests/unit/utils/rpc-reply/request-power-off.xml new file mode 100644 index 000000000..408e9e129 --- /dev/null +++ b/tests/unit/utils/rpc-reply/request-power-off.xml @@ -0,0 +1,33 @@ + + + + This command will not halt the other routing-engine. + If planning to switch off power, use the both-routing-engines option. + + + + + + yes + Perform the operation + + + + no + Don't perform the operation + + + no + Power Off the system ? [yes,no] (no) + + + + + Shutdown NOW! + [pid 17221] + + + + + + \ No newline at end of file diff --git a/tests/unit/utils/rpc-reply/request-reboot.xml b/tests/unit/utils/rpc-reply/request-reboot.xml new file mode 100644 index 000000000..a6d1c983e --- /dev/null +++ b/tests/unit/utils/rpc-reply/request-reboot.xml @@ -0,0 +1,27 @@ + + + + + yes + Perform the operation + + + + no + Don't perform the operation + + + no + Reboot the system ? [yes,no] (no) + + + + + Shutdown NOW! + [pid 17221] + + + + + + diff --git a/tests/unit/utils/rpc-reply/request-system-storage-cleanup.xml b/tests/unit/utils/rpc-reply/request-system-storage-cleanup.xml new file mode 100644 index 000000000..73e0ae04d --- /dev/null +++ b/tests/unit/utils/rpc-reply/request-system-storage-cleanup.xml @@ -0,0 +1,14 @@ + + + + + /var/abc.txt + 11 + Apr 25 10:38 + + + + + + + diff --git a/tests/unit/utils/rpc-reply/show-cli-directory.xml b/tests/unit/utils/rpc-reply/show-cli-directory.xml new file mode 100644 index 000000000..2eee530a4 --- /dev/null +++ b/tests/unit/utils/rpc-reply/show-cli-directory.xml @@ -0,0 +1,8 @@ + + + /cf/var/home/rick + + + + + diff --git a/tests/unit/utils/test_config.py b/tests/unit/utils/test_config.py index c2fac194c..f222cbc9b 100644 --- a/tests/unit/utils/test_config.py +++ b/tests/unit/utils/test_config.py @@ -25,6 +25,17 @@ def test_config_confirm(self): self.conf.rpc.commit_configuration = MagicMock() self.assertTrue(self.conf.commit(confirm=True)) + def test_config_commit_confirm_timeout(self): + self.conf.rpc.commit_configuration = MagicMock() + self.conf.commit(confirm=10) + self.conf.rpc.commit_configuration\ + .assert_called_with(**{'confirm-timeout': '10', 'confirmed': True}) + + def test_config_commit_comment(self): + self.conf.rpc.commit_configuration = MagicMock() + self.conf.commit(comment='Test') + self.conf.rpc.commit_configuration.assert_called_with(log='Test') + @patch('jnpr.junos.utils.config.JXML.remove_namespaces') def test_config_commit_exception(self, mock_jxml): with self.assertRaises(AttributeError): @@ -81,10 +92,13 @@ def test_config_commit_check_exception_RpcError(self): def test_config_diff(self): self.conf.rpc.get_configuration = MagicMock() self.conf.diff() + self.conf.rpc.get_configuration.\ + assert_called_with({'compare': 'rollback', 'rollback': '0'}) def test_config_pdiff(self): self.conf.diff = MagicMock(return_value='') self.conf.pdiff() + self.conf.diff.assert_any_call() def test_config_load(self): self.assertRaises(RuntimeError, self.conf.load) @@ -113,25 +127,41 @@ def test_config_load_lset_format_ValueError(self): @patch('__builtin__.open') @patch('jnpr.junos.utils.config.etree.XML') - def test_config_load_path(self, mock_etree, mock_open): + def test_config_load_path_xml(self, mock_etree, mock_open): self.conf.dev.Template = MagicMock() mock_etree.return_value = 'rpc_contents' self.conf.rpc.load_config = \ MagicMock(return_value=mock_etree.return_value) self.assertEqual(self.conf.load(path='test.xml'), 'rpc_contents') + @patch('__builtin__.open') + def test_config_load_path_text(self, mock_open): + self.conf.rpc.load_config = MagicMock() + self.conf.load(path='test.conf') + self.assertEqual(self.conf.rpc.load_config.call_args[1]['format'], + 'text') + + @patch('__builtin__.open') + def test_config_load_path_set(self, mock_open): + self.conf.rpc.load_config = MagicMock() + self.conf.load(path='test.set') + self.assertEqual(self.conf.rpc.load_config.call_args[1]['action'], + 'set') + def test_config_load_template_path(self): self.conf.rpc.load_config = MagicMock() self.conf.dev.Template = MagicMock() self.conf.load(template_path='test.xml') + self.conf.dev.Template.assert_called_with('test.xml') def test_config_load_template(self): class Temp: filename = 'abc.xml' render = MagicMock() self.conf.rpc.load_config = MagicMock() - self.conf.load(template=Temp) + self.assertEqual(self.conf.rpc.load_config.call_args[1]['format'], + 'xml') def test_config_diff_exception(self): self.conf.rpc.get_configuration = MagicMock() diff --git a/tests/unit/utils/test_fs.py b/tests/unit/utils/test_fs.py new file mode 100644 index 000000000..520a5d1f5 --- /dev/null +++ b/tests/unit/utils/test_fs.py @@ -0,0 +1,333 @@ +__author__ = "Nitin Kumar, Rick Sherman" +__credits__ = "Jeremy Schulman" + +import unittest +from nose.plugins.attrib import attr +import os + +from ncclient.manager import Manager, make_device_handler +from ncclient.transport import SSHSession + +from jnpr.junos import Device +from jnpr.junos.utils.fs import FS + +from mock import patch, MagicMock, call + + +@attr('unit') +class TestFS(unittest.TestCase): + + @patch('ncclient.manager.connect') + def setUp(self, mock_connect): + mock_connect.side_effect = self._mock_manager + self.dev = Device(host='1.1.1.1', user='rick', password='password123', + gather_facts=False) + self.dev.open() + self.fs = FS(self.dev) + + def test_cat_wrong_path_return_none(self): + path = 'test/report' + self.assertIsNone(self.fs.cat(path)) + + def test_cat(self): + self.fs._dev.rpc.file_show = MagicMock(side_effect=self._mock_manager) + path = 'test/cat.txt' + self.assertIn('testing cat functionality', self.fs.cat(path)) + self.fs._dev.rpc.file_show.assert_called_with(filename='test/cat.txt') + + def test_cwd(self): + self.fs._dev.rpc.set_cli_working_directory = MagicMock() + folder = 'test/report' + self.fs.cwd(folder) + self.fs._dev.rpc.set_cli_working_directory.\ + assert_called_with(directory='test/report') + + @patch('jnpr.junos.Device.execute') + def test_pwd(self, mock_execute): + mock_execute.side_effect = MagicMock(side_effect=self._mock_manager) + self.fs.pwd() + self.assertEqual(self.fs.pwd(), '/cf/var/home/rick') + + def test_checksum_return_none(self): + path = 'test/report' + self.assertIsNone(self.fs.checksum(path)) + + def test_checksum_unknown_calc(self): + path = 'test/report' + self.assertRaises(ValueError, self.fs.checksum, path=path, calc='abc') + + def test_checksum_return_rsp(self): + self.fs.dev.rpc.get_sha256_checksum_information = \ + MagicMock(side_effect=self._mock_manager) + path = 'test/checksum' + self.assertEqual(self.fs.checksum(path, 'sha256'), 'xxxx') + self.fs.dev.rpc.get_sha256_checksum_information.\ + assert_called_with(path='test/checksum') + + def test_stat_calling___decode_file(self): + path = 'test/stat/decode_file' + self.fs.dev.rpc.file_list = \ + MagicMock(side_effect=self._mock_manager) + self.assertDictEqual(self.fs.stat(path), + {'owner': 'pqr', 'path': '/var/abc.sh', + 'permissions': 755, + 'permissions_text': '-rwxr-xr-x', 'size': 2, + 'ts_date': 'Mar 13 06:54', + 'ts_epoc': '1394693680', + 'type': 'file'}) + + def test_stat_calling___decode_dir(self): + path = 'test/stat/decode_dir' + self.fs.dev.rpc.file_list = \ + MagicMock(side_effect=self._mock_manager) + self.assertDictEqual(self.fs.stat(path), + {'path': '/var', 'type': 'dir', 'file_count': 1, + 'size': 2}) + + def test_stat_return_none(self): + path = 'test/abc' + self.fs.dev.rpc.file_list = MagicMock() + self.fs.dev.rpc.file_list.find.return_value = 'output' + self.assertIsNone(self.fs.stat(path)) + + def test_ls_calling___decode_file(self): + path = 'test/stat/decode_file' + self.fs.dev.rpc.file_list = \ + MagicMock(side_effect=self._mock_manager) + self.assertDictEqual(self.fs.ls(path), + {'owner': 'pqr', 'path': '/var/abc.sh', + 'permissions': 755, + 'permissions_text': '-rwxr-xr-x', 'size': 2, + 'ts_date': 'Mar 13 06:54', + 'ts_epoc': '1394693680', + 'type': 'file'}) + + def test_ls_calling___decode_dir(self): + path = 'test/stat/decode_dir' + self.fs.dev.rpc.file_list = \ + MagicMock(side_effect=self._mock_manager) + self.assertDictEqual(self.fs.ls(path), + {'files': + {'abc': {'permissions_text': 'drwxr-xr-x', + 'ts_date': 'Feb 17 15:30', + 'ts_epoc': '1392651039', + 'owner': 'root', 'path': 'abc', + 'size': 2, 'type': 'dir', + 'permissions': 555}}, + 'path': '/var', 'type': 'dir', + 'file_count': 1, + 'size': 2}) + + def test_ls_return_none(self): + path = 'test/abc' + self.fs.dev.rpc.file_list = MagicMock() + self.fs.dev.rpc.file_list.find.return_value = 'output' + self.assertIsNone(self.fs.ls(path)) + + @patch('jnpr.junos.utils.fs.FS._decode_file') + def test_ls_link_path_false(self, mock_decode_file): + mock_decode_file.get.return_value = False + path = 'test/stat/decode_file' + self.fs.dev.rpc.file_list = \ + MagicMock(side_effect=self._mock_manager) + op = self.fs.ls(path, followlink=False) + mock_decode_file.assert_has_calls(call().get('link')) + + def test_ls_brief_true(self): + path = 'test/stat/decode_dir' + self.fs.dev.rpc.file_list = \ + MagicMock(side_effect=self._mock_manager) + self.assertDictEqual(self.fs.ls(path, brief=True), + {'files': ['abc'], 'path': '/var', + 'type': 'dir', 'file_count': 1, 'size': 2}) + + def test_ls_calling___decode_dir_type_symbolic_link(self): + path = 'test/stat/decode_symbolic_link' + self.fs.dev.rpc.file_list = \ + MagicMock(side_effect=self._mock_manager) + self.assertDictEqual(self.fs.ls(path), + {'files': + {'abc': {'permissions_text': 'drwxr-xr-x', + 'ts_date': 'Feb 17 15:30', + 'link': 'symlink test', + 'ts_epoc': '1392651039', + 'owner': 'root', 'path': 'abc', + 'size': 2, 'type': 'link', + 'permissions': 555}}, + 'path': '/var', 'type': 'dir', 'file_count': 1, + 'size': 2}) + + def test_rm_return_true(self): + self.fs.dev.rpc.file_delete = MagicMock(return_value=True) + path = 'test/abc' + self.assertTrue(self.fs.rm(path)) + self.fs.dev.rpc.file_delete.assert_called_once_with( + path='test/abc') + + def test_rm_return_false(self): + path = 'test/abc' + self.fs.dev.rpc.file_delete = MagicMock(return_value=False) + self.assertFalse(self.fs.rm(path)) + self.fs.dev.rpc.file_delete.assert_called_once_with( + path='test/abc') + + def test_copy_return_true(self): + self.fs.dev.rpc.file_copy = MagicMock() + initial = 'test/abc' + final = 'test/xyz' + self.assertTrue(self.fs.cp(initial, final)) + self.fs.dev.rpc.file_copy.assert_called_once_with( + source='test/abc', + destination='test/xyz') + + def test_copy_return_false(self): + initial = 'test/abc' + final = 'test/xyz' + self.fs.dev.rpc.file_copy = MagicMock(side_effect=Exception) + self.assertFalse(self.fs.cp(initial, final)) + self.fs.dev.rpc.file_copy.assert_called_once_with( + source='test/abc', + destination='test/xyz') + + def test_move_return_true(self): + self.fs.dev.rpc.file_rename = MagicMock(return_value=True) + initial = 'test/abc' + final = 'test/xyz' + self.assertTrue(self.fs.mv(initial, final)) + self.fs.dev.rpc.file_rename.assert_called_once_with( + source='test/abc', + destination='test/xyz') + + def test_move_return_false(self): + initial = 'test/abc' + final = 'test/xyz' + self.fs.dev.rpc.file_rename = MagicMock(return_value=False) + self.assertFalse(self.fs.mv(initial, final)) + self.fs.dev.rpc.file_rename.assert_called_once_with( + source='test/abc', + destination='test/xyz') + + def test_tgz_return_true(self): + src = 'test/tgz.txt' + dst = 'test/xyz' + self.fs.dev.rpc.file_archive = MagicMock(return_value=True) + self.assertTrue(self.fs.tgz(src, dst)) + self.fs.dev.rpc.file_archive.assert_called_once_with( + source='test/tgz.txt', + destination='test/xyz', compress=True) + + @patch('jnpr.junos.Device.execute') + def test_tgz_return_error(self, mock_execute): + mock_execute.side_effect = self._mock_manager + src = 'test/tgz.txt' + dst = 'test/xyz' + self.assertIn('testing tgz', self.fs.tgz(src, dst)) + + @patch('jnpr.junos.utils.fs.StartShell') + def test_rmdir(self, mock_StartShell): + path = 'test/rmdir' + print self.fs.rmdir(path) + calls = [ + call().__enter__(), + call().__enter__().run('rmdir test/rmdir'), + call().__exit__(None, None, None)] + mock_StartShell.assert_has_calls(calls) + + @patch('jnpr.junos.utils.fs.StartShell') + def test_mkdir(self, mock_StartShell): + path = 'test/mkdir' + print self.fs.mkdir(path) + calls = [ + call().__enter__(), + call().__enter__().run('mkdir -p test/mkdir'), + call().__exit__(None, None, None)] + mock_StartShell.assert_has_calls(calls) + + @patch('jnpr.junos.utils.fs.StartShell') + def test_symlink(self, mock_StartShell): + src = 'test/tgz.txt' + dst = 'test/xyz' + print self.fs.symlink(src, dst) + calls = [ + call().__enter__(), + call().__enter__().run('ln -sf test/tgz.txt test/xyz'), + call().__exit__(None, None, None)] + mock_StartShell.assert_has_calls(calls) + + @patch('jnpr.junos.Device.execute') + def test_storage_usage(self, mock_execute): + mock_execute.side_effect = self._mock_manager + self.assertDictEqual(self.fs.storage_usage(), + {'/dev/abc': + {'avail_block': 234234, + 'used_blocks': 2346455, 'used_pct': '1', + 'mount': '/', 'total_blocks': 567431, + 'avail': '2F', 'used': '481M', + 'total': '4F'}}) + + @patch('jnpr.junos.Device.execute') + def test_storage_cleanup(self, mock_execute): + mock_execute.side_effect = self._mock_manager + self.assertDictEqual(self.fs.storage_cleanup(), + {'/var/abc.txt': + {'ts_date': 'Apr 25 10:38', 'size': 11}}) + + @patch('jnpr.junos.Device.execute') + def test_storage_cleanup_check(self, mock_execute): + mock_execute.side_effect = self._mock_manager + self.assertDictEqual(self.fs.storage_cleanup_check(), + {'/var/abc.txt': + {'ts_date': 'Apr 25 10:38', 'size': 11}}) + + def _read_file(self, fname): + from ncclient.xml_ import NCElement + fpath = os.path.join(os.path.dirname(__file__), + 'rpc-reply', fname) + foo = open(fpath).read() + + if (fname == 'get-rpc-error.xml' or + fname == 'get-index-error.xml' or + fname == 'get-system-core-dumps.xml'): + rpc_reply = NCElement(foo, self.dev._conn._device_handler + .transform_reply()) + elif (fname == 'show-configuration.xml' or + fname == 'show-system-alarms.xml'): + rpc_reply = NCElement(foo, self.dev._conn._device_handler + .transform_reply())._NCElement__doc + else: + rpc_reply = NCElement(foo, self.dev._conn._device_handler + .transform_reply())._NCElement__doc[0] + return rpc_reply + + def _mock_manager(self, *args, **kwargs): + if kwargs: + # if 'path' in kwargs and 'detail' in kwargs: + # return self._read_file('dir_list_detail.xml') + + if 'path' in kwargs: + if kwargs['path'] == 'test/stat/decode_dir': + return self._read_file('file-list_dir.xml') + elif kwargs['path'] == 'test/stat/decode_file': + return self._read_file('file-list_file.xml') + elif kwargs['path'] == 'test/checksum': + return self._read_file('checksum.xml') + elif kwargs['path'] == 'test/stat/decode_symbolic_link': + return self._read_file('file-list_symlink.xml') + if 'filename' in kwargs: + if kwargs['filename'] == 'test/cat.txt': + return self._read_file('file-show.xml') + device_params = kwargs['device_params'] + device_handler = make_device_handler(device_params) + session = SSHSession(device_handler) + return Manager(session, device_handler) + + elif args: + if args[0].tag == 'command': + if args[0].text == 'show cli directory': + return self._read_file('show-cli-directory.xml') + elif args[0].tag == 'get-system-storage': + return self._read_file('get-system-storage.xml') + elif args[0].tag == 'request-system-storage-cleanup': + return self._read_file('request-system-storage-cleanup.xml') + elif args[0].tag == 'file-archive': + return self._read_file('file-archive.xml') diff --git a/tests/unit/utils/test_scp.py b/tests/unit/utils/test_scp.py new file mode 100644 index 000000000..b1eac98c9 --- /dev/null +++ b/tests/unit/utils/test_scp.py @@ -0,0 +1,33 @@ +__author__ = "Rick Sherman" +__credits__ = "Jeremy Schulman, Nitin Kumar" + +import unittest +from nose.plugins.attrib import attr + +from jnpr.junos import Device +from jnpr.junos.utils.scp import SCP + +from mock import patch + + +@attr('unit') +class TestScp(unittest.TestCase): + def setUp(self): + self.dev = Device(host='1.1.1.1') + + @patch('paramiko.SSHClient') + def test_scp_open(self, mock_connect): + from scp import SCPClient + self.dev.bind(scp=SCP) + assert isinstance(self.dev.scp.open(), SCPClient) + + @patch('paramiko.SSHClient') + def test_scp_close(self, mock_connect): + self.dev.bind(scp=SCP) + self.dev.scp.open() + self.assertIsNone(self.dev.scp.close()) + + @patch('paramiko.SSHClient') + def test_scp_context(self, mock_connect): + with SCP(self.dev) as scp: + scp.get('addrbook.conf') diff --git a/tests/unit/utils/test_start_shell.py b/tests/unit/utils/test_start_shell.py new file mode 100644 index 000000000..fb1087da2 --- /dev/null +++ b/tests/unit/utils/test_start_shell.py @@ -0,0 +1,52 @@ +__author__ = "Rick Sherman" +__credits__ = "Jeremy Schulman, Nitin Kumar" + +import unittest +from nose.plugins.attrib import attr + +from jnpr.junos import Device +from jnpr.junos.utils.start_shell import StartShell + +from mock import patch, MagicMock, call + + +@attr('unit') +class TestStartShell(unittest.TestCase): + @patch('paramiko.SSHClient') + def setUp(self, mock_connect): + self.dev = Device(host='1.1.1.1') + self.shell = StartShell(self.dev) + + @patch('paramiko.SSHClient') + @patch('jnpr.junos.utils.start_shell.StartShell.wait_for') + def test_startshell_open(self, mock_connect, mock_wait): + self.shell.open() + mock_connect.assert_called_with('% ') + + @patch('paramiko.SSHClient') + def test_startshell_close(self, mock_connect): + self.shell._chan = MagicMock() + self.shell._client = MagicMock() + self.shell.close() + self.shell._client.close.assert_called_once() + + @patch('jnpr.junos.utils.start_shell.StartShell.wait_for') + def test_startshell_run(self, mock_wait): + self.shell._chan = MagicMock() + self.shell.run('ls') + self.assertIn(call.send('echo $?'), self.shell._chan.mock_calls) + + @patch('jnpr.junos.utils.start_shell.select') + def test_startshell_wait_for(self, mock_select): + mock_select.return_value = ['> ', 2, 3] + self.shell._chan = MagicMock() + self.assertIn(call.endswith('> '), + self.shell.wait_for('> ')[0].mock_calls) + + @patch('jnpr.junos.utils.start_shell.StartShell.open') + @patch('jnpr.junos.utils.start_shell.StartShell.close') + def test_startshell_context(self, mock_open, mock_close): + with StartShell(self.dev) as shell: + shell._chan = MagicMock() + shell.send('test') + mock_close.assert_called_once(call()) diff --git a/tests/unit/utils/test_sw.py b/tests/unit/utils/test_sw.py new file mode 100644 index 000000000..4525d4506 --- /dev/null +++ b/tests/unit/utils/test_sw.py @@ -0,0 +1,277 @@ +__author__ = "Nitin Kumar, Rick Sherman" +__credits__ = "Jeremy Schulman" + +import unittest +from nose.plugins.attrib import attr + +import os +import sys +from cStringIO import StringIO +from contextlib import contextmanager + +from jnpr.junos import Device +from jnpr.junos.exception import RpcError +from jnpr.junos.utils.sw import SW +from jnpr.junos.facts.swver import version_info +from ncclient.manager import Manager, make_device_handler +from ncclient.transport import SSHSession + +from jnpr.junos.exception import RpcError +from lxml import etree + +from mock import patch, MagicMock, call, mock_open + + +facts = {'domain': None, 'hostname': 'firefly', 'ifd_style': 'CLASSIC', + 'version_info': version_info('12.1X46-D15.3'), + '2RE': False, 'serialnumber': 'aaf5fe5f9b88', 'fqdn': 'firefly', + 'virtual': True, 'switch_style': 'NONE', 'version': '12.1X46-D15.3', + 'HOME': '/cf/var/home/rick', 'srx_cluster': False, + 'model': 'FIREFLY-PERIMETER', + 'RE0': {'status': 'Testing', + 'last_reboot_reason': 'Router rebooted after a ' + 'normal shutdown.', + 'model': 'FIREFLY-PERIMETER RE', + 'up_time': '6 hours, 29 minutes, 30 seconds'}, + 'vc_capable': False, 'personality': 'SRX_BRANCH'} + + +@attr('unit') +class TestSW(unittest.TestCase): + + @patch('ncclient.manager.connect') + def setUp(self, mock_connect): + mock_connect.side_effect = self._mock_manager + self.dev = Device(host='1.1.1.1', user='rick', password='password123', + gather_facts=False) + self.dev.open() + self.dev._facts = facts + self.sw = self.get_sw() + + @patch('jnpr.junos.Device.execute') + def get_sw(self, mock_execute): + mock_execute.side_effect = self._mock_manager + return SW(self.dev) + + @patch('ncclient.operations.session.CloseSession.request') + def tearDown(self, mock_session): + self.dev.close() + + def test_sw_hashfile(self): + with patch('__builtin__.open', mock_open(), create=True) as m: + import jnpr.junos.utils.sw + with open('foo') as h: + h.read.side_effect = ('abc', 'a', '') + jnpr.junos.utils.sw._hashfile(h, MagicMock()) + self.assertEqual(h.read.call_count, 3) + + @patch('jnpr.junos.Device.execute') + def test_sw_constructor_multi_re(self, mock_execute): + mock_execute.side_effect = self._mock_manager + self.sw = SW(self.dev) + self.assertFalse(self.sw._multi_RE) + + @patch('jnpr.junos.Device.execute') + def test_sw_constructor_multi_mx(self, mock_execute): + mock_execute.side_effect = self._mock_manager + self.sw = SW(self.dev) + self.assertFalse(self.sw._multi_MX) + + @patch('jnpr.junos.Device.execute') + def test_sw_constructor_multi_vc(self, mock_execute): + mock_execute.side_effect = self._mock_manager + self.sw = SW(self.dev) + self.assertFalse(self.sw._multi_VC) + + @patch('__builtin__.open') + def test_sw_local_sha256(self, mock_built_open): + package = 'test.tgz' + self.assertEqual(SW.local_sha256(package), + 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934' + 'ca495991b7852b855') + + @patch('__builtin__.open') + def test_sw_local_md5(self, mock_built_open): + package = 'test.tgz' + self.assertEqual(SW.local_md5(package), + 'd41d8cd98f00b204e9800998ecf8427e') + + @patch('__builtin__.open') + def test_sw_local_sha1(self, mock_built_open): + package = 'test.tgz' + self.assertEqual(SW.local_sha1(package), + 'da39a3ee5e6b4b0d3255bfef95601890afd80709') + + def test_sw_progress(self): + with self.capture(SW.progress, self.dev, 'running') as output: + self.assertEqual('1.1.1.1: running\n', output) + + @patch('paramiko.SSHClient') + @patch('scp.SCPClient.put') + def test_sw_put(self, mock_scp_put, mock_scp): + #mock_scp_put.side_effect = self.mock_put + package = 'test.tgz' + self.sw.put(package) + self.assertIn(call('test.tgz', '/var/tmp'), + mock_scp_put.mock_calls) + + @patch('jnpr.junos.utils.scp.SCP.__exit__') + @patch('jnpr.junos.utils.scp.SCP.__init__') + @patch('jnpr.junos.utils.scp.SCP.__enter__') + def test_sw_put_progress(self, mock_enter, mock_scp, mock_exit): + package = 'test.tgz' + mock_scp.side_effect = self._fake_scp + self.sw.put(package, progress=self._myprogress) + self.assertEqual(mock_scp.call_args_list[0][1]['progress'].by10pct, 50) + + def _fake_scp(self, *args, **kwargs): + progress = kwargs['progress'] + progress('test.tgz', 100, 50) + + @patch('jnpr.junos.Device.execute') + def test_sw_pkgadd(self, mock_execute): + mock_execute.side_effect = self._mock_manager + package = 'test.tgz' + self.assertTrue(self.sw.pkgadd(package)) + + @patch('jnpr.junos.Device.execute') + def test_sw_validate(self, mock_execute): + mock_execute.side_effect = self._mock_manager + package = 'package.tgz' + self.assertTrue(self.sw.validate(package)) + + @patch('jnpr.junos.Device.execute') + def test_sw_safe_copy(self, mock_execute): + mock_execute.side_effect = self._mock_manager + package = 'safecopy.tgz' + self.sw.put = MagicMock() + SW.local_md5 = MagicMock() + + self.assertTrue(self.sw.safe_copy(package, progress=self._myprogress, cleanfs=True, + checksum='96a35ab371e1ca10408c3caecdbd8a67')) + + @patch('jnpr.junos.Device.execute') + def test_sw_safe_copy_return_false(self, mock_execute): + # not passing checksum value, will get random from magicmock + mock_execute.side_effect = self._mock_manager + package = 'safecopy.tgz' + self.sw.put = MagicMock() + SW.local_md5 = MagicMock() + + self.assertFalse(self.sw.safe_copy(package, progress=self._myprogress, + cleanfs=True)) + SW.local_md5.assert_called_with(package) + + @patch('jnpr.junos.Device.execute') + def test_sw_safe_copy_checksum_none(self, mock_execute): + mock_execute.side_effect = self._mock_manager + package = 'safecopy.tgz' + self.sw.put = MagicMock() + SW.local_md5 = MagicMock(return_value='96a35ab371e1ca10408c3caecdbd8a67') + + self.assertTrue(self.sw.safe_copy(package, progress=self._myprogress, + cleanfs=True)) + + @patch('jnpr.junos.Device.execute') + def test_sw_safe_install(self, mock_execute): + mock_execute.side_effect = self._mock_manager + package = 'install.tgz' + self.sw.put = MagicMock() + SW.local_md5 = MagicMock(return_value='96a35ab371e1ca10408c3caecdbd8a67') + self.assertTrue(self.sw.install(package, progress=self._myprogress, + cleanfs=True)) + + @patch('jnpr.junos.utils.sw.SW.safe_copy') + def test_sw_safe_install_copy_fail(self, mock_copy): + mock_copy.return_value = False + self.assertFalse(self.sw.install('file')) + + @patch('jnpr.junos.utils.sw.SW.validate') + def test_sw_install_validate(self, mock_validate): + mock_validate.return_value = False + self.assertFalse(self.sw.install('file', validate=True, no_copy=True)) + + @patch('jnpr.junos.utils.sw.SW.pkgadd') + def test_sw_install_multi_mx(self, mock_pkgadd): + mock_pkgadd.return_value = True + self.sw._multi_RE = True + self.sw._multi_MX = True + self.assertTrue(self.sw.install('file', no_copy=True)) + + @patch('jnpr.junos.utils.sw.SW.pkgadd') + def test_sw_install_multi_vc(self, mock_pkgadd): + mock_pkgadd.return_value = True + self.sw._multi_RE = True + self.sw._multi_VC = True + self.sw._RE_list = ('version_RE0', 'version_RE1') + self.assertTrue(self.sw.install('file', no_copy=True)) + + @patch('jnpr.junos.Device.execute') + def test_sw_rollback(self, mock_execute): + # we need proper xml for this test case, update request-package-roll + # back.xml + mock_execute.side_effect = self._mock_manager + self.assertEqual(self.sw.rollback(), '') + + def test_sw_inventory(self): + self.sw.dev.rpc.file_list = \ + MagicMock(side_effect=self._mock_manager) + self.assertDictEqual(self.sw.inventory, + {'current': None, 'rollback': None}) + + @patch('jnpr.junos.Device.execute') + def test_sw_reboot(self, mock_execute): + mock_execute.side_effect = self._mock_manager + self.sw._multi_MX = True + self.assertIn('Shutdown NOW', self.sw.reboot()) + + @patch('jnpr.junos.Device.execute') + def test_sw_reboot_exception(self, mock_execute): + rsp = etree.XML('test') + mock_execute.side_effect = RpcError(rsp=rsp) + self.assertRaises(Exception, self.sw.reboot) + + @patch('jnpr.junos.Device.execute') + def test_sw_poweroff(self, mock_execute): + mock_execute.side_effect = self._mock_manager + self.sw._multi_MX = True + self.assertIn('Shutdown NOW', self.sw.poweroff()) + + @patch('jnpr.junos.Device.execute') + def test_sw_poweroff_exception(self, mock_execute): + rsp = etree.XML('test') + mock_execute.side_effect = RpcError(rsp=rsp) + self.assertRaises(Exception, self.sw.poweroff) + + def _myprogress(self, dev, report): + pass + + @contextmanager + def capture(self, command, *args, **kwargs): + out, sys.stdout = sys.stdout, StringIO() + command(*args, **kwargs) + sys.stdout.seek(0) + yield sys.stdout.read() + sys.stdout = out + + def _read_file(self, fname): + from ncclient.xml_ import NCElement + + fpath = os.path.join(os.path.dirname(__file__), + 'rpc-reply', fname) + foo = open(fpath).read() + rpc_reply = NCElement(foo, self.dev._conn._device_handler.transform_reply())._NCElement__doc[0] + return rpc_reply + + def _mock_manager(self, *args, **kwargs): + if kwargs: + if 'path' in kwargs: + if kwargs['path'] == '/packages': + return self._read_file('file-list_dir.xml') + device_params = kwargs['device_params'] + device_handler = make_device_handler(device_params) + session = SSHSession(device_handler) + return Manager(session, device_handler) + + elif args: + return self._read_file(args[0].tag + '.xml') diff --git a/tests/unit/utils/test_util.py b/tests/unit/utils/test_util.py new file mode 100644 index 000000000..5c6413833 --- /dev/null +++ b/tests/unit/utils/test_util.py @@ -0,0 +1,32 @@ +__author__ = "Nitin Kumar, Rick Sherman" +__credits__ = "Jeremy Schulman" + +import unittest +from nose.plugins.attrib import attr + +from jnpr.junos import Device +from jnpr.junos.utils.util import Util + +from mock import patch + + +@attr('unit') +class TestUtil(unittest.TestCase): + + @patch('ncclient.manager.connect') + def setUp(self, mock_connect): + self.dev = Device(host='1.1.1.1', user='nitin', password='password123', + gather_facts=False) + self.dev.open() + self.util = Util(self.dev) + + def test_repr(self): + self.assertEqual(repr(self.util), 'jnpr.junos.utils.Util(1.1.1.1)') + + def test_dev_setter_exception(self): + with self.assertRaises(RuntimeError): + self.util.dev = 'abc' + + def test_rpc_setter_exception(self): + with self.assertRaises(RuntimeError): + self.util.rpc = 'abc'