diff --git a/conftest.py b/conftest.py index 9cce6b952..808522f5e 100644 --- a/conftest.py +++ b/conftest.py @@ -14,8 +14,11 @@ # from f5.bigip import BigIP +import mock import pytest +from icontrol.session import iControlRESTSession + def pytest_addoption(parser): parser.addoption("--bigip", action="store", @@ -33,6 +36,19 @@ def pytest_addoption(parser): default='11.6.0') +@pytest.fixture +def fakeicontrolsession(monkeypatch): + class Response(object): + def json(self): + return {'selfLink': 'https://localhost/mgmt/tm/sys?ver=11.6.0'} + fakesessionclass = mock.create_autospec(iControlRESTSession, spec_set=True) + fakesessioninstance =\ + mock.create_autospec(iControlRESTSession('A', 'B'), spec_set=True) + fakesessioninstance.get = mock.MagicMock(return_value=Response()) + fakesessionclass.return_value = fakesessioninstance + monkeypatch.setattr('f5.bigip.iControlRESTSession', fakesessionclass) + + @pytest.fixture def opt_bigip(request): return request.config.getoption("--bigip") diff --git a/f5/bigip/__init__.py b/f5/bigip/__init__.py index 49dd1386d..98a45c198 100644 --- a/f5/bigip/__init__.py +++ b/f5/bigip/__init__.py @@ -18,6 +18,9 @@ from icontrol.session import iControlRESTSession +from urlparse import parse_qs +from urlparse import urlparse + from f5.bigip.cm import Cm from f5.bigip.resource import PathElement @@ -54,8 +57,10 @@ def __init__(self, hostname, username, password, **kwargs): 'bigip': self, 'icontrol_version': icontrol_version, 'username': username, - 'password': password + 'password': password, + 'tmos_version': None, } + self._get_tmos_version() @property def hostname(self): @@ -65,6 +70,18 @@ def hostname(self): def icontrol_version(self): return self._meta_data['icontrol_version'] + @property + def tmos_version(self): + return self._meta_data['tmos_version'] + + def _get_tmos_version(self): + connect = self._meta_data['bigip']._meta_data['icr_session'] + base_uri = self._meta_data['uri'] + 'tm/sys/' + response = connect.get(base_uri) + ver = response.json() + version = parse_qs(urlparse(ver['selfLink']).query)['ver'][0] + self._meta_data['tmos_version'] = version + class BigIP(ManagementRoot): """A shim class used to access the default config resources in 'mgmt/tm.' diff --git a/f5/bigip/cm/autodeploy/test/test_software_image_uploads.py b/f5/bigip/cm/autodeploy/test/test_software_image_uploads.py index f9a6865f9..3becf5221 100644 --- a/f5/bigip/cm/autodeploy/test/test_software_image_uploads.py +++ b/f5/bigip/cm/autodeploy/test/test_software_image_uploads.py @@ -24,7 +24,7 @@ CHUNKSIZE = 20 -def test_software_image_uploads_80a(tmpdir): +def test_software_image_uploads_80a(tmpdir, fakeicontrolsession): filepath = tmpdir.mkdir('testdir').join('eightya.iso') filepath.write(80*'a') mr = ManagementRoot('FAKENETLOC', 'FAKENAME', 'FAKEPASSWORD') @@ -37,7 +37,7 @@ def test_software_image_uploads_80a(tmpdir): assert d == 'a'*CHUNKSIZE -def test_software_image_uploads_70a(tmpdir): +def test_software_image_uploads_70a(tmpdir, fakeicontrolsession): filepath = tmpdir.mkdir('testdir').join('seventya.iso') filepath.write(70*'a') mr = ManagementRoot('FAKENETLOC', 'FAKENAME', 'FAKEPASSWORD') @@ -53,7 +53,7 @@ def test_software_image_uploads_70a(tmpdir): assert 10*'a' == lchunk -def test_non_ISO_extension(tmpdir): +def test_non_ISO_extension(tmpdir, fakeicontrolsession): filepath = tmpdir.mkdir('testdir').join('wrong.name') mr = ManagementRoot('FAKENETLOC', 'FAKENAME', 'FAKEPASSWORD') sius = mr.cm.autodeploy.software_image_uploads diff --git a/f5/bigip/mixins.py b/f5/bigip/mixins.py index 596dde495..635eb56f2 100644 --- a/f5/bigip/mixins.py +++ b/f5/bigip/mixins.py @@ -31,6 +31,14 @@ class UnsupportedMethod(F5SDKError): pass +class UnsupportedTmosVersion(F5SDKError): + """Raise the error if a class of an API is instantiated, + + on a TMOS version where API was not yet implemented/supported. + """ + pass + + class LazyAttributesRequired(F5SDKError): """Raised when a object accesses a lazy attribute that is not listed""" pass @@ -101,10 +109,20 @@ def __getattr__(container, name): if name == lazy_attribute.__name__.lower(): attribute = lazy_attribute(container) bases = [base.__name__ for base in lazy_attribute.__bases__] + # Doing version check per each resource + container._check_supported_versions(attribute) if 'Resource' not in bases: setattr(container, name, attribute) return attribute + def _check_supported_versions(container, attribute): + tmos_v = container._meta_data['bigip'].tmos_version + if tmos_v not in attribute._meta_data['supported_versions']: + error = "There was an attempt to access API which " \ + "has not been implemented or supported " \ + "in the device's TMOS version: {}".format(tmos_v) + raise UnsupportedTmosVersion(error) + class ExclusiveAttributesMixin(object): """Overrides ``__setattr__`` to remove exclusive attrs from the object.""" diff --git a/f5/bigip/resource.py b/f5/bigip/resource.py index d81b554fa..f97f2fab0 100644 --- a/f5/bigip/resource.py +++ b/f5/bigip/resource.py @@ -162,6 +162,7 @@ class PathElement(LazyAttributeMixin): those elements and does not support any of the CURDLE methods that the other objects do. """ + def __init__(self, container): self._meta_data = { 'container': container, @@ -170,6 +171,9 @@ def __init__(self, container): 'icontrol_version': container._meta_data['icontrol_version'] } self._set_meta_data_uri() + # Supported versions for each class will be defined here. + # List can be modified downstream in each sub-class + self._meta_data['supported_versions'] = set(['11.6.0', '12.0.0']) def _set_meta_data_uri(self): base_uri = self.__class__.__name__.lower() diff --git a/f5/bigip/shared/test/test_file_uploads.py b/f5/bigip/shared/test/test_file_uploads.py index ef336f6e3..7cf82a48b 100644 --- a/f5/bigip/shared/test/test_file_uploads.py +++ b/f5/bigip/shared/test/test_file_uploads.py @@ -22,7 +22,7 @@ FileMustNotHaveDotISOExtension -def test_file_upload_80a(tmpdir): +def test_file_upload_80a(tmpdir, fakeicontrolsession): filepath = tmpdir.mkdir('testdir').join('eightya.txt') filepath.write(80*'a') mr = ManagementRoot('FAKENETLOC', 'FAKENAME', 'FAKEPASSWORD') @@ -35,7 +35,7 @@ def test_file_upload_80a(tmpdir): assert d == 'aaaaaaaaaaaaaaaaaaaa' -def test_file_upload_70a(tmpdir): +def test_file_upload_70a(tmpdir, fakeicontrolsession): filepath = tmpdir.mkdir('testdir').join('seventya.txt') filepath.write(70*'a') mr = ManagementRoot('FAKENETLOC', 'FAKENAME', 'FAKEPASSWORD') @@ -51,7 +51,7 @@ def test_file_upload_70a(tmpdir): assert 10*'a' == lchunk -def test_ISO_extension(tmpdir): +def test_ISO_extension(tmpdir, fakeicontrolsession): filepath = tmpdir.mkdir('testdir').join('wrongname.iso') filepath.write('fake') mr = ManagementRoot('FAKENETLOC', 'FAKENAME', 'FAKEPASSWORD') @@ -61,7 +61,7 @@ def test_ISO_extension(tmpdir): assert EIO.value.message == 'wrongname.iso' -def test_stringio_upload_80a(tmpdir): +def test_stringio_upload_80a(tmpdir, fakeicontrolsession): sio = StringIO(80*'a') mr = ManagementRoot('FAKENETLOC', 'FAKENAME', 'FAKEPASSWORD') mr._meta_data['icr_session'] = mock.MagicMock() @@ -73,7 +73,7 @@ def test_stringio_upload_80a(tmpdir): assert d == 'aaaaaaaaaaaaaaaaaaaa' -def test_stringio_upload_70a(tmpdir): +def test_stringio_upload_70a(tmpdir, fakeicontrolsession): sio = StringIO(70*'a') mr = ManagementRoot('FAKENETLOC', 'FAKENAME', 'FAKEPASSWORD') mr._meta_data['icr_session'] = mock.MagicMock() @@ -87,7 +87,7 @@ def test_stringio_upload_70a(tmpdir): assert 10*'a' == lchunk -def test_bytes_upload_80a(tmpdir): +def test_bytes_upload_80a(tmpdir, fakeicontrolsession): bytestring80a = 80*'a' mr = ManagementRoot('FAKENETLOC', 'FAKENAME', 'FAKEPASSWORD') mr._meta_data['icr_session'] = mock.MagicMock() @@ -99,7 +99,7 @@ def test_bytes_upload_80a(tmpdir): assert d == 'aaaaaaaaaaaaaaaaaaaa' -def test_bytes_upload_70a(tmpdir): +def test_bytes_upload_70a(tmpdir, fakeicontrolsession): bytestring70a = 70*'a' mr = ManagementRoot('FAKENETLOC', 'FAKENAME', 'FAKEPASSWORD') mr._meta_data['icr_session'] = mock.MagicMock() diff --git a/f5/bigip/test/test___init__.py b/f5/bigip/test/test___init__.py index 790f17b26..ff3812f29 100644 --- a/f5/bigip/test/test___init__.py +++ b/f5/bigip/test/test___init__.py @@ -26,14 +26,14 @@ @pytest.fixture -def FakeBigIP(): +def FakeBigIP(fakeicontrolsession): FBIP = BigIP('FakeHostName', 'admin', 'admin') FBIP.icontrol = mock.MagicMock() return FBIP @pytest.fixture -def FakeBigIPWithPort(): +def FakeBigIPWithPort(fakeicontrolsession): FBIP = BigIP('FakeHostName', 'admin', 'admin', port='10443') FBIP.icontrol = mock.MagicMock() return FBIP diff --git a/f5/bigip/tm/auth/test/test_user.py b/f5/bigip/tm/auth/test/test_user.py index 186502e7f..2208bbb75 100644 --- a/f5/bigip/tm/auth/test/test_user.py +++ b/f5/bigip/tm/auth/test/test_user.py @@ -29,7 +29,7 @@ def FakeUser(): class TestCreate(object): - def test_create_two(self): + def test_create_two(self, fakeicontrolsession): b = BigIP('localhost', 'admin', 'admin') n1 = b.auth.users.user n2 = b.auth.users.user diff --git a/f5/bigip/tm/cm/test/test___init__.py b/f5/bigip/tm/cm/test/test___init__.py index 6a69fc3e9..ee901c432 100644 --- a/f5/bigip/tm/cm/test/test___init__.py +++ b/f5/bigip/tm/cm/test/test___init__.py @@ -20,7 +20,7 @@ @pytest.fixture -def FakeiControl(): +def FakeiControl(fakeicontrolsession): bigip = BigIP('host', 'fake_admin', 'fake_admin') mock_session = mock.MagicMock() mock_session.post.return_value.json.return_value = {} diff --git a/f5/bigip/tm/cm/trust.py b/f5/bigip/tm/cm/trust.py index b3c7a387f..45690b498 100644 --- a/f5/bigip/tm/cm/trust.py +++ b/f5/bigip/tm/cm/trust.py @@ -38,6 +38,7 @@ def __init__(self, cm): self._meta_data['required_json_kind'] = \ 'tm:cm:add-to-trust:runstate' self._meta_data['allowed_commands'].append('run') + self._meta_data['supported_versions'].discard('11.6.0') class Remove_From_Trust(UnnamedResourceMixin, CommandExecutionMixin, Resource): @@ -59,3 +60,4 @@ def __init__(self, cm): self._meta_data['required_json_kind'] = \ 'tm:cm:remove-from-trust:runstate' self._meta_data['allowed_commands'].append('run') + self._meta_data['supported_versions'].discard('11.6.0') diff --git a/f5/bigip/tm/ltm/profile.py b/f5/bigip/tm/ltm/profile.py index c5e2d1684..49c644890 100644 --- a/f5/bigip/tm/ltm/profile.py +++ b/f5/bigip/tm/ltm/profile.py @@ -511,6 +511,7 @@ def __init__(self, profile): self._meta_data['allowed_lazy_attributes'] = [Iiop] self._meta_data['attribute_registry'] = \ {'tm:ltm:profile:iiop:iiopstate': Iiop} + self._meta_data['supported_versions'].discard('11.6.0') class Iiop(Resource): @@ -1034,6 +1035,7 @@ def __init__(self, profile): self._meta_data['allowed_lazy_attributes'] = [Tftp] self._meta_data['attribute_registry'] = \ {'tm:ltm:profile:tftp:tftpstate': Tftp} + self._meta_data['supported_versions'].discard('11.6.0') class Tftp(Resource): diff --git a/f5/bigip/tm/ltm/test/test_nat.py b/f5/bigip/tm/ltm/test/test_nat.py index d33344798..f0e498350 100644 --- a/f5/bigip/tm/ltm/test/test_nat.py +++ b/f5/bigip/tm/ltm/test/test_nat.py @@ -29,7 +29,7 @@ def FakeNat(): class TestCreate(object): - def test_create_two(self): + def test_create_two(self, fakeicontrolsession): b = BigIP('192.168.1.1', 'admin', 'admin') n1 = b.ltm.nats.nat n2 = b.ltm.nats.nat diff --git a/f5/bigip/tm/ltm/test/test_rule.py b/f5/bigip/tm/ltm/test/test_rule.py index b597cb196..21b685ff4 100644 --- a/f5/bigip/tm/ltm/test/test_rule.py +++ b/f5/bigip/tm/ltm/test/test_rule.py @@ -29,7 +29,7 @@ def FakeRule(): class TestCreate(object): - def test_create_two(self): + def test_create_two(self, fakeicontrolsession): b = BigIP('192.168.1.1', 'admin', 'admin') r1 = b.ltm.rules.rule r2 = b.ltm.rules.rule diff --git a/f5/bigip/tm/sys/test/test_db.py b/f5/bigip/tm/sys/test/test_db.py index 2058e81ff..a1c173855 100644 --- a/f5/bigip/tm/sys/test/test_db.py +++ b/f5/bigip/tm/sys/test/test_db.py @@ -22,12 +22,15 @@ @pytest.fixture def fake_dbs(): fake_sys = mock.MagicMock() - return Dbs(fake_sys) + dbs = Dbs(fake_sys) + dbs._meta_data['bigip'].tmos_version = '11.6.0' + return dbs class TestDb(object): def test_create_raises(self): dbs = fake_dbs() + print(dbs.raw) db = dbs.db with pytest.raises(UnsupportedOperation): db.create() diff --git a/f5/bigip/tm/sys/test/test_folder.py b/f5/bigip/tm/sys/test/test_folder.py index 248b5d16a..7c60775fd 100644 --- a/f5/bigip/tm/sys/test/test_folder.py +++ b/f5/bigip/tm/sys/test/test_folder.py @@ -23,7 +23,9 @@ @pytest.fixture def FakeFolders(): fake_sys = mock.MagicMock() - return Folders(fake_sys) + folders = Folders(fake_sys) + folders._meta_data['bigip'].tmos_version = '11.6.0' + return folders class TestFolder(object): diff --git a/f5/bigip/tm/sys/test/test_performance.py b/f5/bigip/tm/sys/test/test_performance.py index b89db31ce..ab820a335 100644 --- a/f5/bigip/tm/sys/test/test_performance.py +++ b/f5/bigip/tm/sys/test/test_performance.py @@ -24,7 +24,9 @@ @pytest.fixture def FakePerformance(): fake_sys = mock.MagicMock() - return Performances(fake_sys) + performances = Performances(fake_sys) + performances._meta_data['bigip'].tmos_version = '11.6.0' + return performances class TestPerformance(object): diff --git a/f5/bigip/tm/sys/test/test_sys_application.py b/f5/bigip/tm/sys/test/test_sys_application.py index a60ce4fb3..84f33a431 100644 --- a/f5/bigip/tm/sys/test/test_sys_application.py +++ b/f5/bigip/tm/sys/test/test_sys_application.py @@ -171,7 +171,7 @@ def __init__(self): class TestServiceCreate(object): - def test_create_two(self): + def test_create_two(self, fakeicontrolsession): b = BigIP('192.168.1.1', 'admin', 'admin') serv1 = b.sys.applications.services.service serv2 = b.sys.applications.services.service @@ -323,7 +323,7 @@ def test_update_inherit_tg_false(self, FakeService): class TestTemplateCreate(object): - def test_create_two(self): + def test_create_two(self, fakeicontrolsession): b = BigIP('192.168.1.1', 'admin', 'admin') templ1 = b.sys.applications.templates.template templ2 = b.sys.applications.templates.template @@ -337,7 +337,7 @@ def test_create_no_args(self, FakeTemplate): class TestAplscript(object): - def test_create_two(self): + def test_create_two(self, fakeicontrolsession): b = BigIP('192.168.1.1', 'admin', 'admin') templ1 = b.sys.applications.aplscripts.aplscript templ2 = b.sys.applications.aplscripts.aplscript @@ -350,7 +350,7 @@ def test_create_no_args(self, FakeAplscript): class TestCustomstat(object): - def test_create_two(self): + def test_create_two(self, fakeicontrolsession): b = BigIP('192.168.1.1', 'admin', 'admin') templ1 = b.sys.applications.customstats.customstat templ2 = b.sys.applications.customstats.customstat diff --git a/f5/multi_device/cluster/test/test_cluster_manager.py b/f5/multi_device/cluster/test/test_cluster_manager.py index bbc1b1759..dbb966003 100644 --- a/f5/multi_device/cluster/test/test_cluster_manager.py +++ b/f5/multi_device/cluster/test/test_cluster_manager.py @@ -30,7 +30,7 @@ def __init__(self, name): @pytest.fixture -def BigIPs(): +def BigIPs(fakeicontrolsession): mock_bigips = [] for bigip in range(4): mock_bigips.append(ManagementRoot('test', 'un', 'pw'))