Skip to content

Commit 555fea5

Browse files
author
Paul Breaux
committed
Adding device group manager
Issues: Fixes #400 Problem: For clustering, we need to break apart the large cluster_manager module into something less Blob(ish) Analysis: Broke apart similar functionality for checking/managing the device group into a new module, the device_group_manager Tests: All manual tests still
1 parent f15b3fb commit 555fea5

3 files changed

Lines changed: 276 additions & 79 deletions

File tree

f5/bigip/mixins.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,3 +271,19 @@ def _upload(self, filepathname, **kwargs):
271271
session.post(self.file_bound_uri,
272272
**requests_params)
273273
start += current_bytes
274+
275+
276+
class DeviceMixin(object):
277+
'''Class to manage BigIP device cluster in a general way.'''
278+
279+
def _get_device_info(self, bigip):
280+
'''Get device information about a specific BigIP device.
281+
282+
:param bigip: bigip object --- device to inspect
283+
:returns: bigip object
284+
'''
285+
286+
coll = bigip.cm.devices.get_collection()
287+
device = [device for device in coll if device.selfDevice == 'true']
288+
assert len(device) == 1
289+
return device[0]

f5/managers/cluster_manager.py

Lines changed: 125 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
#
1515
#
1616

17+
from f5.bigip.mixins import DeviceMixin
1718
from f5.common import pollster
19+
from f5.managers.device_group_manager import DeviceGroupManager
1820

1921

2022
class BigIPDeviceNotInExpectedState(Exception):
@@ -29,124 +31,169 @@ class UnexpectedBigIPState(Exception):
2931
pass
3032

3133

32-
class ClusterManager(object):
34+
class ClusterManager(DeviceMixin):
3335
'''Manage a cluster of BigIPs.
3436
3537
This is accomplished with REST URI calls only, but some operations are
36-
only permitted through the CLI (such as adding peers via cm/trust-domain).
38+
only permitted via tmsh commands (such as adding cm/trust-domain peers).
3739
We get around this issue by deploying iApps (sys/application).
3840
'''
3941

4042
iapp_actions = {'definition': {'implementation': None, 'presentation': ''}}
4143
sync_status_entry = 'https://localhost/mgmt/tm/cm/sync-status/0'
4244

4345
def __init__(self, bigips, cluster_name, partition, cluster_type):
44-
if len(bigips) > 4:
46+
if len(bigips) > 8:
4547
raise ClusterNotSupported(
4648
'The number of devices to cluster is not supported.'
4749
)
4850
self.bigips = bigips
49-
self.bigip_trust_root = self.bigips[0]
51+
self.root_bigip = self.bigips[0]
5052
self.peers = self.bigips[1:]
5153
self.cluster_name = cluster_name
52-
self.device_iapp_name = 'device_iapp'
53-
self.cluster_iapp_name = 'cluster_iapp'
54+
self.peer_iapp_prefix = 'cluster_iapp'
5455
self.partition = partition
5556
self.cluster_type = cluster_type
57+
self.device_group = DeviceGroupManager(
58+
cluster_name, self.root_bigip, partition
59+
)
5660

57-
def cluster_bigips(self):
58-
if len(self._get_bigips_by_activation_state('active')) != \
59-
len(self.bigips):
60-
raise BigIPDeviceNotInExpectedState(
61-
'One or more BigIP devices was not in a active/licensed state.'
62-
)
63-
print('Adding trusted peers...')
64-
self._add_peers()
61+
def create_bigip_cluster(self):
62+
'''Cluster the BigIP devices given.'''
63+
self._ensure_bigips_active_licensed()
6564
print('Creating cluster group...')
66-
self._create_cluster_group()
65+
self._create_device_cluster()
6766
if self.cluster_type == 'sync-failover':
6867
print('Ensure cluster has settled into active/standby...')
69-
self._ensure_active_standby()
68+
self._devices_in_standby()
7069
elif self.cluster_type == 'sync-only':
7170
print('Ensure devices are all in sync and active...')
72-
self.bigip_trust_root.cm.sync(self.cluster_name)
71+
self.root_bigip.cm.sync(self.cluster_name)
7372
self._all_devices_in_sync()
7473

75-
def _add_peers(self):
76-
peer_cmds = []
74+
def teardown_bigip_cluster(self):
75+
'''Teardown cluster of BigIP devices.'''
76+
77+
if self.cluster_type == 'sync-failover':
78+
self._devices_in_standby()
79+
self.root_bigip.cm.sync(self.cluster_name)
80+
self.device_group.teardown_device_group(self.bigips)
81+
# remove other devices from each device
7782
for peer in self.peers:
78-
peer_cmds.append(self._get_add_peer_command(peer))
79-
iapp_actions = self.iapp_actions.copy()
80-
iapp_actions['definition']['implementation'] = '\n'.join(peer_cmds)
81-
self._deploy_iapp(self.cluster_iapp_name, iapp_actions)
82-
83-
def _create_cluster_group(self):
84-
self.bigip_trust_root.cm.device_groups.device_group.create(
85-
name=self.cluster_name,
86-
partition=self.partition,
87-
type=self.cluster_type
83+
self._modify_trusted_peer(
84+
peer, self._get_delete_peer_command, peer
85+
)
86+
87+
def _ensure_bigips_active_licensed(self):
88+
'''All devices should be in an active/licensed state.'''
89+
if len(self._get_bigips_by_activation_state('active')) != \
90+
len(self.bigips):
91+
raise BigIPDeviceNotInExpectedState(
92+
'One or more BigIP devices was not in a active/licensed state.'
93+
)
94+
95+
def scale_cluster_up(self, bigip):
96+
'''Scale cluster up by one device.
97+
98+
:param bigip: bigip object -- bigip to add
99+
'''
100+
101+
if len(self.bigips) == 8:
102+
raise ClusterNotSupported(
103+
'The number of devices to cluster is not supported.'
104+
)
105+
self._modify_trusted_peer(
106+
bigip, self._get_add_peer_command, self.root_bigip
88107
)
89-
for bigip in self.bigips:
90-
self._add_device_to_device_group(bigip)
108+
self.device_group.scale_up_device_group(bigip)
109+
self.bigips.append(bigip)
110+
111+
def scale_cluster_down(self, bigip):
112+
'''Scale cluster down by one device.
113+
114+
:param bigip: bigip object -- bigip to delete
115+
'''
91116

92-
def _ensure_active_standby(self):
93-
self._devices_in_standby()
117+
self.device_group.scale_down_device_group(bigip)
118+
self._modify_trusted_peer(
119+
bigip, self._get_delete_peer_command, self.root_bigip
120+
)
121+
self.bigips.remove(bigip)
122+
123+
def _create_device_cluster(self):
124+
'''Deploy an iapp to add trusted peers, then create device group.'''
125+
126+
for peer in self.peers:
127+
self._modify_trusted_peer(
128+
peer, self._get_add_peer_command, self.root_bigip
129+
)
130+
self._all_devices_in_sync()
131+
self.device_group.create_device_group(self.bigips, self.cluster_type)
132+
133+
def _modify_trusted_peer(
134+
self, peer, mod_peer_func, deploy_bigip
135+
):
136+
'''Modify a trusted peer device.
137+
138+
:param peer: bigip object -- peer to modify
139+
:param mod_peer_func: function -- function to call to modify peer
140+
'''
141+
142+
peer_info = self._get_device_info(peer)
143+
iapp_name = '%s_%s' % (self.peer_iapp_prefix, peer_info.name)
144+
mod_peer_cmd = mod_peer_func(peer)
145+
iapp_actions = self.iapp_actions.copy()
146+
iapp_actions['definition']['implementation'] = mod_peer_cmd
147+
self._deploy_iapp(iapp_name, iapp_actions, deploy_bigip)
148+
# Once the command has been run via the iapp, delete the iapp
149+
self._delete_iapp(iapp_name, deploy_bigip)
94150

95151
@pollster.poll_by_method
96152
def _all_devices_in_sync(self):
153+
'''Wait until all devices have failover status of 'In Sync'.'''
97154
assert len(self._get_bigips_by_failover_status('In Sync')) == \
98155
len(self.bigips)
99156

100157
@pollster.poll_by_method
101158
def _devices_in_standby(self):
159+
'''Wait until n-1 devices in 'standby' activation state.'''
102160
standby_bigips = \
103161
self._get_bigips_by_activation_state('standby')
104162
assert len(standby_bigips) == (len(self.bigips)-1)
105163
return standby_bigips
106164

107165
def _get_bigips_by_failover_status(self, status):
166+
'''Get a list of bigips by failover status.'''
108167
bigips = []
109168
for bigip in self.bigips:
110169
sync_status = bigip.cm.sync_status
111170
sync_status.refresh()
112171
current_status = (sync_status.entries[self.sync_status_entry]
113172
['nestedStats']['entries']['status']
114173
['description'])
115-
print(current_status)
116174
if status == current_status:
117175
bigips.append(bigip)
118176
return bigips
119177

120178
def _get_bigips_by_activation_state(self, state):
179+
'''Get a list of bigips by activation statue.'''
121180
bigips = []
122181
for bigip in self.bigips:
123-
reg = bigip.shared.licensing.registration.load()
124182
act = bigip.cm.devices.device.load(
125183
name=self._get_device_info(bigip).name,
126184
partition=self.partition
127185
)
128-
if reg.licensedVersion != '' and act.failoverState == state:
186+
if act.failoverState == state:
129187
bigips.append(bigip)
130188
return bigips
131189

132-
def teardown_cluster(self):
133-
self._remove_iapp(self.cluster_iapp_name)
134-
if self.cluster_type == 'sync-failover':
135-
self._devices_in_standby()
136-
self.bigip_trust_root.cm.sync(self.cluster_name)
137-
dg = self.bigip_trust_root.cm.device_groups.device_group.load(
138-
name=self.cluster_name, partition=self.partition
139-
)
140-
for bigip in self.bigips:
141-
bigip_info = self._get_device_info(bigip)
142-
dgd = dg.devices_s.devices.load(
143-
name=bigip_info.name, partition=self.partition
144-
)
145-
dgd.delete()
146-
dg.delete()
190+
def _delete_iapp(self, iapp_name, deploy_bigip):
191+
'''Delete an iapp service and template on the root device.
192+
193+
:param iapp_name: str -- name of iapp
194+
'''
147195

148-
def _remove_iapp(self, iapp_name):
149-
iapp = self.bigip_trust_root.sys.applications
196+
iapp = deploy_bigip.sys.applications
150197
iapp_service = iapp.services.service.load(
151198
name=iapp_name, partition=self.partition
152199
)
@@ -156,43 +203,42 @@ def _remove_iapp(self, iapp_name):
156203
)
157204
iapp_template.delete()
158205

159-
def _get_device_info(self, bigip):
160-
coll = bigip.cm.devices.get_collection()
161-
device = [device for device in coll if device.selfDevice == 'true']
162-
assert len(device) == 1
163-
return device[0]
206+
def _deploy_iapp(self, iapp_name, actions, deploy_bigip):
207+
'''Deploy iapp on the root device.
164208
165-
def _add_device_to_device_group(self, bigip):
166-
bigip_info = self._get_device_info(bigip)
167-
poll_for_dg = pollster.poll_by_method(
168-
bigip.cm.device_groups.device_group.load
169-
)
170-
dg = poll_for_dg(
171-
name=self.cluster_name, partition=self.partition
172-
)
173-
dg.devices_s.devices.create(
174-
name=bigip_info.name, partition=self.partition
175-
)
176-
root_dg = self.bigip_trust_root.cm.device_groups.device_group.load(
177-
name=self.cluster_name, partition=self.partition
178-
)
179-
trust_root_poll = pollster.poll_by_method(
180-
root_dg.devices_s.devices.load
181-
)
182-
trust_root_poll(name=bigip_info.name, partition=self.partition)
209+
:param iapp_name: str -- name of iapp
210+
:param actions: dict -- actions definition of iapp sections
211+
'''
183212

184-
def _deploy_iapp(self, iapp_name, actions):
185-
tmpl = self.bigip_trust_root.sys.applications.templates.template
186-
serv = self.bigip_trust_root.sys.applications.services.service
213+
tmpl = deploy_bigip.sys.applications.templates.template
214+
serv = deploy_bigip.sys.applications.services.service
187215
tmpl.create(name=iapp_name, partition=self.partition, actions=actions)
188216
serv.create(
189217
name=iapp_name, partition=self.partition, template=iapp_name
190218
)
191219

192220
def _get_add_peer_command(self, peer):
221+
'''Get tmsh command to add a trusted peer.
222+
223+
:param peer: bigip object -- peer device
224+
:returns: str -- tmsh command to add trusted peer
225+
'''
226+
193227
peer_device = self._get_device_info(peer)
194228
add_peer_cmd = 'tmsh::modify cm trust-domain Root ca-devices add ' \
195229
'\\{{ {0} \\}} name {1} username admin password admin'.format(
196230
peer_device.managementIp, peer_device.name
197231
)
198232
return add_peer_cmd
233+
234+
def _get_delete_peer_command(self, peer):
235+
'''Get tmsh command to delete a trusted peer.
236+
237+
:param peer: bigip object -- peer device
238+
:returns: str -- tmsh command to delete trusted peer
239+
'''
240+
241+
peer_device = self._get_device_info(peer)
242+
del_peer_cmd = 'tmsh::modify cm trust-domain Root ca-devices delete ' \
243+
'\\{ %s \\}' % peer_device.name
244+
return del_peer_cmd

0 commit comments

Comments
 (0)