Skip to content

Commit b180c4d

Browse files
authored
Merge pull request #558 from pjbreaux/bugfix.endisable
Fix nat test for test_create_disabled_false
2 parents 6fd6b17 + 3bdb3fc commit b180c4d

4 files changed

Lines changed: 150 additions & 34 deletions

File tree

f5/bigip/resource.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,11 @@ class UnsupportedOperation(F5SDKError):
161161
pass
162162

163163

164+
class BooleansToReduceHaveSameValue(F5SDKError):
165+
'''Dict contains two keys with same boolean value.'''
166+
pass
167+
168+
164169
class PathElement(LazyAttributeMixin):
165170
"""Base class to represent a URI path element that does not contain data.
166171
@@ -442,6 +447,11 @@ def _update(self, **kwargs):
442447
# generation has a known server-side error
443448
self._check_generation()
444449

450+
# Reduce any boolean pairs as specified by the meta_data entry below
451+
if 'reduction_forcing_pairs' in self._meta_data:
452+
for key1, key2 in self._meta_data['reduction_forcing_pairs']:
453+
kwargs = self._reduce_boolean_pair(kwargs, key1, key2)
454+
445455
# Save the meta data so we can add it back into self after we
446456
# load the new object.
447457
temp_meta = self.__dict__.pop('_meta_data')
@@ -463,6 +473,7 @@ def _update(self, **kwargs):
463473
data_dict.pop(attr, '')
464474

465475
data_dict.update(kwargs)
476+
466477
response = session.put(update_uri, json=data_dict, **requests_params)
467478
self._meta_data = temp_meta
468479
self._local_update(response.json())
@@ -694,6 +705,12 @@ def __init__(self, container):
694705
self._meta_data['required_load_parameters'] = set(('name',))
695706
# You can't set these attributes, only 'read' them.
696707
self._meta_data['read_only_attributes'] = []
708+
self._meta_data['reduction_forcing_pairs'] = \
709+
[
710+
('enabled', 'disabled'),
711+
('online', 'offline'),
712+
('vlansEnabled', 'vlansDisabled')
713+
]
697714

698715
def _activate_URI(self, selfLinkuri):
699716
"""Call this with a selfLink, after it's returned in _create or _load.
@@ -745,6 +762,30 @@ def _activate_URI(self, selfLinkuri):
745762
'creation_uri_frag': frag,
746763
'allowed_lazy_attributes': attrs})
747764

765+
def _reduce_boolean_pair(self, config_dict, key1, key2):
766+
'''Ensure only one key with a boolean value is present in dict.
767+
768+
:param config_dict: dict -- dictionary of config or kwargs
769+
:param key1: string -- first key name
770+
:param key2: string -- second key name
771+
:raises: BooleansToReduceHaveSameValue
772+
'''
773+
774+
if key1 in config_dict and key2 in config_dict \
775+
and config_dict[key1] == config_dict[key2]:
776+
msg = 'Boolean pair, %s and %s, have same value: %s. If both ' \
777+
'are given to this method, they cannot be the same, as this ' \
778+
'method cannot decide which one should be True.' \
779+
% (key1, key2, config_dict[key1])
780+
raise BooleansToReduceHaveSameValue(msg)
781+
elif key1 in config_dict and not config_dict[key1]:
782+
config_dict[key2] = True
783+
config_dict.pop(key1)
784+
elif key2 in config_dict and not config_dict[key2]:
785+
config_dict[key1] = True
786+
config_dict.pop(key2)
787+
return config_dict
788+
748789
def _create(self, **kwargs):
749790
"""wrapped by `create` override that in subclasses to customize"""
750791
if 'uri' in self._meta_data:
@@ -756,6 +797,10 @@ def _create(self, **kwargs):
756797
requests_params = self._handle_requests_params(kwargs)
757798
self._check_create_parameters(**kwargs)
758799

800+
# Reduce boolean pairs as specified by the meta_data entry below
801+
for key1, key2 in self._meta_data['reduction_forcing_pairs']:
802+
kwargs = self._reduce_boolean_pair(kwargs, key1, key2)
803+
759804
# Make convenience variable with short names for this method.
760805
_create_uri = self._meta_data['container']._meta_data['uri']
761806
session = self._meta_data['bigip']._meta_data['icr_session']
@@ -809,6 +854,8 @@ def _load(self, **kwargs):
809854
refresh_session = self._meta_data['bigip']._meta_data['icr_session']
810855
base_uri = self._meta_data['container']._meta_data['uri']
811856
kwargs.update(requests_params)
857+
for key1, key2 in self._meta_data['reduction_forcing_pairs']:
858+
kwargs = self._reduce_boolean_pair(kwargs, key1, key2)
812859
response = refresh_session.get(base_uri, **kwargs)
813860
# Make new instance of self
814861
return self._produce_instance(response)

f5/bigip/test/test_resource.py

Lines changed: 101 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import pytest
1717
import requests
1818

19+
from f5.bigip.resource import BooleansToReduceHaveSameValue
1920
from f5.bigip.resource import Collection
2021
from f5.bigip.resource import DeviceProvidesIncompatibleKey
2122
from f5.bigip.resource import ExclusiveAttributesPresent
@@ -38,6 +39,30 @@
3839
from f5.sdk_exception import UnsupportedMethod
3940

4041

42+
@pytest.fixture
43+
def fake_vs():
44+
r = Virtual(mock.MagicMock())
45+
MRO = MockResponse({u"kind": u"tm:ltm:virtual:virtualstate",
46+
u"selfLink": u".../~Common~test_create"})
47+
r._meta_data['bigip']._meta_data['icr_session'].post.return_value = MRO
48+
r._meta_data['required_json_kind'] = u"tm:ltm:virtual:virtualstate"
49+
r._meta_data['allowed_lazy_attributes'] = []
50+
return r
51+
52+
53+
@pytest.fixture
54+
def fake_rsrc():
55+
r = Resource(mock.MagicMock())
56+
r._meta_data['allowed_lazy_attributes'] = []
57+
r._meta_data['uri'] = 'URI'
58+
r._meta_data['read_only_attributes'] = [u"READONLY"]
59+
attrs = {'put.return_value': MockResponse({u"generation": 0}),
60+
'get.return_value': MockResponse({u"generation": 0})}
61+
mock_session = mock.MagicMock(**attrs)
62+
r._meta_data['bigip']._meta_data = {'icr_session': mock_session}
63+
return r
64+
65+
4166
class MockResponse(object):
4267
def __init__(self, attr_dict):
4368
self.__dict__ = attr_dict
@@ -115,17 +140,51 @@ def test_KindTypeMismatch(self):
115140
"be ''tm:ltm:virtual:virtualstate'' but creation returned "\
116141
"JSON with kind: u'tm:'"
117142

118-
def test_success(self):
119-
r = Virtual(mock.MagicMock())
120-
MRO = MockResponse({u"kind": u"tm:ltm:virtual:virtualstate",
121-
u"selfLink": u".../~Common~test_create"})
122-
r._meta_data['bigip']._meta_data['icr_session'].post.return_value = MRO
123-
r._meta_data['required_json_kind'] = u"tm:ltm:virtual:virtualstate"
124-
r._meta_data['allowed_lazy_attributes'] = []
125-
x = r.create(partition="Common", name="test_create")
143+
def test_success(self, fake_vs):
144+
x = fake_vs.create(partition="Common", name="test_create")
126145
assert x.kind == u"tm:ltm:virtual:virtualstate"
127146
assert x.selfLink == u".../~Common~test_create"
128147

148+
def test_reduce_boolean_removes_enabled(self, fake_vs):
149+
assert fake_vs._meta_data['reduction_forcing_pairs'] == \
150+
[
151+
('enabled', 'disabled'),
152+
('online', 'offline'),
153+
('vlansEnabled', 'vlansDisabled')
154+
]
155+
fake_vs.create(partition="Common", name="test_create", enabled=False)
156+
pos, kwargs = fake_vs._meta_data['bigip']._meta_data['icr_session'].post.\
157+
call_args
158+
assert kwargs['json']['disabled'] is True
159+
assert 'enabled' not in kwargs['json']
160+
161+
def test_reduce_boolean_removes_disabled(self, fake_vs):
162+
fake_vs.create(partition='Common', name='test_create', disabled=False)
163+
pos, kwargs = fake_vs._meta_data['bigip']._meta_data['icr_session'].post.\
164+
call_args
165+
assert kwargs['json']['enabled'] is True
166+
assert 'disabled' not in kwargs['json']
167+
168+
def test_reduce_boolean_removes_nothing(self, fake_vs):
169+
fake_vs.create(partition='Common', name='test_create', enabled=True)
170+
pos, kwargs = fake_vs._meta_data['bigip']._meta_data['icr_session'].post.\
171+
call_args
172+
assert kwargs['json']['enabled'] is True
173+
assert 'disabled' not in kwargs['json']
174+
175+
def test_reduce_boolean_same_value(self, fake_vs):
176+
with pytest.raises(BooleansToReduceHaveSameValue) as ex:
177+
fake_vs.create(
178+
partition='Common',
179+
name='test_create',
180+
enabled=True,
181+
disabled=True
182+
)
183+
msg = 'Boolean pair, enabled and disabled, have same value: True. ' \
184+
'If both are given to this method, they cannot be the same, as ' \
185+
'this method cannot decide which one should be True.'
186+
assert msg == ex.value.message
187+
129188

130189
def test__activate_URI():
131190
r = Resource(mock.MagicMock())
@@ -219,6 +278,40 @@ def test_read_only_removal(self):
219278
_meta_data['icr_session'].put.call_args[1]['json']
220279
assert 'READONLY' not in submitted
221280

281+
def test_reduce_boolean_removes_enabled(self, fake_rsrc):
282+
fake_rsrc.update(enabled=False)
283+
pos, kwargs = fake_rsrc._meta_data['bigip']._meta_data['icr_session'].put.\
284+
call_args
285+
assert kwargs['json']['disabled'] is True
286+
assert 'enabled' not in kwargs['json']
287+
288+
def test_reduce_boolean_removes_disabled(self, fake_rsrc):
289+
fake_rsrc.update(disabled=False)
290+
pos, kwargs = fake_rsrc._meta_data['bigip']._meta_data['icr_session'].put.\
291+
call_args
292+
assert kwargs['json']['enabled'] is True
293+
assert 'disabled' not in kwargs['json']
294+
295+
def test_reduce_boolean_removes_nothing(self, fake_rsrc):
296+
fake_rsrc.update(partition='Common', name='test_create', enabled=True)
297+
pos, kwargs = fake_rsrc._meta_data['bigip']._meta_data['icr_session'].put.\
298+
call_args
299+
assert kwargs['json']['enabled'] is True
300+
assert 'disabled' not in kwargs['json']
301+
302+
def test_reduce_boolean_same_value(self, fake_rsrc):
303+
with pytest.raises(BooleansToReduceHaveSameValue) as ex:
304+
fake_rsrc.update(
305+
partition='Common',
306+
name='test_create',
307+
enabled=True,
308+
disabled=True
309+
)
310+
msg = 'Boolean pair, enabled and disabled, have same value: True. ' \
311+
'If both are given to this method, they cannot be the same, as ' \
312+
'this method cannot decide which one should be True.'
313+
assert msg == ex.value.message
314+
222315

223316
class TestResource_delete(object):
224317
def test_success(self):

f5/bigip/tm/gtm/datacenter.py

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,6 @@ def __init__(self, dc_s):
4949
'tm:gtm:datacenter:datacenterstate'
5050
self._meta_data['exclusive_attributes'].append(('enabled', 'disabled'))
5151

52-
def _endis_able(self, config_dict):
53-
if 'enabled' in config_dict and not config_dict['enabled']:
54-
config_dict['disabled'] = True
55-
elif 'disabled' in config_dict and not config_dict['disabled']:
56-
config_dict['enabled'] = True
57-
return config_dict
58-
5952
def _endis_attrs(self):
6053
"""Manipulate return value to equal negation of set value
6154
@@ -121,25 +114,18 @@ def _endis_attrs(self):
121114
return None
122115

123116
def create(self, **kwargs):
124-
kwargs = self._endis_able(kwargs)
125-
126-
if 'enabled' in kwargs and kwargs['enabled']:
127-
del kwargs['disabled']
128-
if 'disabled' in kwargs and kwargs['disabled']:
129-
del kwargs['enabled']
130-
131117
inst = self._create(**kwargs)
132118
inst._endis_attrs()
133119
return inst
134120

135121
def load(self, **kwargs):
136-
kwargs = self._endis_able(kwargs)
122+
kwargs = self._reduce_boolean_pair(kwargs, 'enabled', 'disabled')
137123
inst = self._load(**kwargs)
138124
inst._endis_attrs()
139125
return inst
140126

141127
def refresh(self, **kwargs):
142-
kwargs = self._endis_able(kwargs)
128+
kwargs = self._reduce_boolean_pair(kwargs, 'enabled', 'disabled')
143129
self._refresh(**kwargs)
144130
self._endis_attrs()
145131
return self
@@ -149,6 +135,5 @@ def update(self, **kwargs):
149135
kwargs['enabled'] = self.__dict__.pop('enabled')
150136
elif 'disabled' in self.__dict__ and 'disabled' not in kwargs:
151137
kwargs['disabled'] = self.__dict__.pop('disabled')
152-
kwargs = self._endis_able(kwargs)
153138
self._update(**kwargs)
154139
self._endis_attrs()

f5/bigip/tm/ltm/nat.py

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -89,24 +89,15 @@ def create(self, **kwargs):
8989
% kwargs['trafficGroup'])
9090
except KeyError:
9191
pass
92-
kwargs = self._endis_able(kwargs)
9392
new_instance = self._create(**kwargs)
9493
return new_instance
9594

96-
def _endis_able(self, config_dict):
97-
if 'enabled' in config_dict and not config_dict['enabled']:
98-
config_dict['disabled'] = True
99-
elif 'disabled' in config_dict and not config_dict['disabled']:
100-
config_dict['enabled'] = True
101-
return config_dict
102-
10395
def update(self, **kwargs):
10496
# This is an example implementation of read-only params
10597
stash_translation_address = self.__dict__.pop('translationAddress')
10698
if 'enabled' in self.__dict__ and 'enabled' not in kwargs:
10799
kwargs['enabled'] = self.__dict__.pop('enabled')
108100
elif 'disabled' in self.__dict__ and 'disabled' not in kwargs:
109101
kwargs['disabled'] = self.__dict__.pop('disabled')
110-
kwargs = self._endis_able(kwargs)
111102
self._update(**kwargs)
112103
self.__dict__['translationAddress'] = stash_translation_address

0 commit comments

Comments
 (0)