Skip to content

Commit f1b09be

Browse files
author
Paul Breaux
committed
VCMP endpoints need to be added
Issues: Fixes #666 Problem: The VCMP endpoints should be added in the tm organizing collection. A VCMP has a collection of guests. We should be able to retrieve the vlan and management address for a particular guest as well. Analysis: Added the VCMP OrganizingCollection, the Guests Collection, and the Guest Resource. Added a unit test and some functional tests. The functional tests can only be run against a vcmp host. So I added a pytest config option of --vcmp-host, which allows a user to provide an IP address for a vcmp configured host. Only then will the functional tests be run. Tests: All tests pass against the vcmp host in the Boulder lab
1 parent 0d94796 commit f1b09be

8 files changed

Lines changed: 244 additions & 2 deletions

File tree

conftest.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ def pytest_addoption(parser):
4646
parser.addoption("--release", action="store",
4747
help="TMOS version, in dotted format, eg. 12.0.0",
4848
default='11.6.0')
49+
parser.addoption("--vcmp-host", action="store",
50+
help="IP address of VCMP enabled host.")
4951

5052

5153
@pytest.fixture
@@ -122,6 +124,11 @@ def opt_port(request):
122124
return request.config.getoption("--port")
123125

124126

127+
@pytest.fixture(scope='session')
128+
def opt_vcmp_host(request):
129+
return request.config.getoption("--vcmp-host")
130+
131+
125132
@pytest.fixture(scope='session')
126133
def bigip(opt_bigip, opt_username, opt_password, opt_port, scope="module"):
127134
'''bigip fixture'''
@@ -136,6 +143,14 @@ def mgmt_root(opt_bigip, opt_username, opt_password, opt_port, scope="module"):
136143
return m
137144

138145

146+
@pytest.fixture(scope='module')
147+
def vcmp_host(opt_vcmp_host, opt_username, opt_password, opt_port):
148+
'''vcmp fixture'''
149+
m = ManagementRoot(
150+
opt_vcmp_host, opt_username, opt_password, port=opt_port)
151+
return m
152+
153+
139154
@pytest.fixture(scope='session')
140155
def opt_release(request):
141156
return request.config.getoption("--release")

f5/bigip/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
from f5.bigip.tm.sys import Sys
3636
from f5.bigip.tm import Tm
3737
from f5.bigip.tm.transaction import Transactions
38+
from f5.bigip.tm.vcmp import Vcmp
3839

3940

4041
class ManagementRoot(PathElement):
@@ -95,4 +96,4 @@ def __init__(self, hostname, username, password, **kwargs):
9596
super(BigIP, self).__init__(hostname, username, password, **kwargs)
9697
self._meta_data['uri'] = self._meta_data['uri'] + 'tm/'
9798
self._meta_data['allowed_lazy_attributes'] =\
98-
[TmAuth, TmCm, Ltm, Gtm, Net, TmShared, Sys, Transactions]
99+
[TmAuth, TmCm, Ltm, Gtm, Net, TmShared, Sys, Transactions, Vcmp]

f5/bigip/tm/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from f5.bigip.tm.shared import Shared
2727
from f5.bigip.tm.sys import Sys
2828
from f5.bigip.tm.transaction import Transactions
29+
from f5.bigip.tm.vcmp import Vcmp
2930

3031

3132
class Tm(OrganizingCollection):
@@ -40,5 +41,6 @@ def __init__(self, bigip):
4041
Net,
4142
Shared,
4243
Sys,
43-
Transactions
44+
Transactions,
45+
Vcmp
4446
]

f5/bigip/tm/vcmp/__init__.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# coding=utf-8
2+
#
3+
# Copyright 2016 F5 Networks Inc.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
#
17+
18+
"""BIG-IP® VCMP (vcmp) module
19+
20+
REST URI
21+
``http://localhost/mgmt/tm/vcmp/``
22+
23+
GUI Path
24+
``vCMP``
25+
26+
REST Kind
27+
``tm:vcmp:*``
28+
"""
29+
30+
from f5.bigip.resource import OrganizingCollection
31+
from f5.bigip.tm.vcmp.guest import Guests
32+
33+
34+
class Vcmp(OrganizingCollection):
35+
def __init__(self, tm):
36+
super(Vcmp, self).__init__(tm)
37+
self._meta_data['allowed_lazy_attributes'] = [Guests]

f5/bigip/tm/vcmp/guest.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# coding=utf-8
2+
#
3+
# Copyright 2016 F5 Networks Inc.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
#
17+
18+
"""BIG-IP® Guest (vcmp) module
19+
20+
REST URI
21+
``http://localhost/mgmt/tm/vcmp/guest/``
22+
23+
GUI Path
24+
``Guest List``
25+
26+
REST Kind
27+
``tm:vcmp:guest:*``
28+
"""
29+
30+
from f5.bigip.resource import Collection
31+
from f5.bigip.resource import Resource
32+
33+
34+
class Guests(Collection):
35+
"""BIG-IP® Guests collection."""
36+
def __init__(self, vcmp):
37+
super(Guests, self).__init__(vcmp)
38+
self._meta_data['allowed_lazy_attributes'] = [Guest]
39+
self._meta_data['required_json_kind'] =\
40+
'tm:vcmp:guest:guestcollectionstate'
41+
self._meta_data['attribute_registry'] =\
42+
{'tm:vcmp:guest:gueststate': Guest}
43+
44+
45+
class Guest(Resource):
46+
"""BIG-IP® Guest resource."""
47+
def __init__(self, guests):
48+
super(Guest, self).__init__(guests)
49+
self._meta_data['required_json_kind'] =\
50+
'tm:vcmp:guest:gueststate'
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Copyright 2016 F5 Networks Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
#
15+
16+
import mock
17+
import pytest
18+
19+
from f5.bigip.resource import MissingRequiredCreationParameter
20+
from f5.bigip.tm.vcmp.guest import Guest
21+
22+
23+
@pytest.fixture
24+
def FakeGuest():
25+
fake_guests = mock.MagicMock()
26+
return Guest(fake_guests)
27+
28+
29+
def test_create_no_args(FakeGuest):
30+
with pytest.raises(MissingRequiredCreationParameter) as ex:
31+
FakeGuest.create()
32+
assert "Missing required params: ['name']" in ex.value.message

test/functional/tm/vcmp/__init__.py

Whitespace-only changes.
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# Copyright 2016 F5 Networks Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
#
15+
16+
from f5.bigip.resource import MissingRequiredCreationParameter
17+
from f5.bigip.resource import MissingRequiredReadParameter
18+
from icontrol.session import iControlUnexpectedHTTPError
19+
20+
import copy
21+
import pytest
22+
23+
24+
try:
25+
vcmp_host = pytest.config.getoption('--vcmp-host')
26+
except Exception as ex:
27+
vcmp_host = None
28+
29+
30+
@pytest.fixture
31+
def setup_guest_test(request, vcmp_host):
32+
def teardown():
33+
guest.delete()
34+
request.addfinalizer(teardown)
35+
guests = vcmp_host.tm.vcmp.guests
36+
guest = guests.guest.create(name='test')
37+
return guests, guest
38+
39+
40+
@pytest.mark.skipif(vcmp_host is None,
41+
reason='Provide --vcmp-host to run vcmp tests.')
42+
class TestGuest(object):
43+
def test_guests_get_collection(self, setup_guest_test):
44+
guests, guest1 = setup_guest_test
45+
gc = list(guests.get_collection())
46+
assert len(gc) > 1
47+
48+
def test_guest_create_refresh_update_delete_load_modify(
49+
self, setup_guest_test
50+
):
51+
guests, guest1 = setup_guest_test
52+
assert guest1.name == 'test'
53+
assert guest1.managementNetwork == 'bridged'
54+
guest1.managementGw = '10.190.0.1'
55+
guest1.update()
56+
assert guest1.managementGw == '10.190.0.1'
57+
old_sslmode = guest1.sslMode
58+
guest1.sslMode = 'dedicated'
59+
guest1.refresh()
60+
assert guest1.sslMode == old_sslmode
61+
guest2 = guests.guest.load(name='test')
62+
assert guest1.selfLink == guest2.selfLink
63+
guest2.modify(sslMode='dedicated')
64+
guest1.refresh()
65+
assert guest2.sslMode == guest1.sslMode
66+
67+
def test_guest_modify(self, setup_guest_test):
68+
guests, guest1 = setup_guest_test
69+
original_dict = copy.copy(guest1.__dict__)
70+
gw = 'managementGw'
71+
guest1.modify(managementGw='10.190.0.1')
72+
for k, v in original_dict.items():
73+
if k != gw:
74+
original_dict[k] = guest1.__dict__[k]
75+
elif k == gw:
76+
guest1.__dict__[k] == '10.190.0.1'
77+
78+
def test_guest_no_creation_args(self, vcmp_host):
79+
with pytest.raises(MissingRequiredCreationParameter) as ex:
80+
vcmp_host.tm.vcmp.guests.guest.create()
81+
assert 'name' in ex.value.message
82+
83+
def test_guest_bad_creation_args(self, vcmp_host):
84+
with pytest.raises(iControlUnexpectedHTTPError) as ex:
85+
vcmp_host.tm.vcmp.guests.guest.create(
86+
name='test', partition='Common')
87+
assert '(/Common/test) is invalid' in ex.value.message
88+
89+
def test_guest_no_load_args(self, vcmp_host):
90+
with pytest.raises(MissingRequiredReadParameter) as ex:
91+
vcmp_host.tm.vcmp.guests.guest.load()
92+
assert 'name' in ex.value.message
93+
94+
def test_guest_bad_load_args(self, vcmp_host):
95+
with pytest.raises(iControlUnexpectedHTTPError) as ex:
96+
vcmp_host.tm.vcmp.guests.guest.load(
97+
name='test', partition='test-bad-arg')
98+
assert 'The requested VCMP (/test-bad-arg/test) was not found' in \
99+
ex.value.message
100+
101+
def test_guest_bad_modify(self, setup_guest_test):
102+
guests, guest1 = setup_guest_test
103+
with pytest.raises(iControlUnexpectedHTTPError) as ex:
104+
guest1.modify(generation=34)
105+
assert 'one or more properties must be specified' in ex.value.message

0 commit comments

Comments
 (0)