Skip to content

Commit 052b85e

Browse files
authored
Adds APIs for BIG-IQ regkey and utility licensing (#1328)
Issues: Fixes #1327 Problem: These APIs were missing or no longer valid due to changes in BIG-IQ. Analysis: This patch changes the minimum supported version to 5.3.0 and ensures these APIs are correct. Tests: * functional * unit
1 parent 2e7f769 commit 052b85e

9 files changed

Lines changed: 614 additions & 209 deletions

File tree

f5/bigiq/cm/device/licensing/pool/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,17 @@
2424
N/A -- HTTP GET returns an error
2525
"""
2626

27+
from f5.bigiq.cm.device.licensing.pool.initial_activation import Initial_Activations
2728
from f5.bigiq.cm.device.licensing.pool.regkey import Regkey
29+
from f5.bigiq.cm.device.licensing.pool.utility import Utility
2830
from f5.bigiq.resource import OrganizingCollection
2931

3032

3133
class Pool(OrganizingCollection):
3234
def __init__(self, licensing):
3335
super(Pool, self).__init__(licensing)
3436
self._meta_data['allowed_lazy_attributes'] = [
35-
Regkey
37+
Initial_Activations,
38+
Regkey,
39+
Utility
3640
]
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# coding=utf-8
2+
#
3+
# Copyright 2017 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-IQ® license pool regkeys.
19+
20+
REST URI
21+
``http://localhost/mgmt/cm/device/licensing/pool/initial-activation``
22+
23+
REST Kind
24+
``cm:device:licensing:pool:initial-activation:*``
25+
"""
26+
27+
from f5.bigiq.resource import Collection
28+
from f5.bigiq.resource import Resource
29+
30+
31+
class Initial_Activations(Collection):
32+
def __init__(self, pool):
33+
super(Initial_Activations, self).__init__(pool)
34+
self._meta_data['required_json_kind'] = \
35+
'cm:device:licensing:pool:initial-activation:initialactivationworkercollectionstate' # NOQA
36+
self._meta_data['allowed_lazy_attributes'] = [Initial_Activation]
37+
self._meta_data['attribute_registry'] = {
38+
'cm:device:licensing:pool:initial-activation:initialactivationworkeritemstate': Initial_Activation # NOQA
39+
}
40+
41+
42+
class Initial_Activation(Resource):
43+
def __init__(self, initial_activations):
44+
super(Initial_Activation, self).__init__(initial_activations)
45+
self._meta_data['required_creation_parameters'] = {'name', 'regKey'}
46+
self._meta_data['required_json_kind'] = \
47+
'cm:device:licensing:pool:initial-activation:initialactivationworkeritemstate'

f5/bigiq/cm/device/licensing/pool/regkey/licenses.py renamed to f5/bigiq/cm/device/licensing/pool/regkey.py

Lines changed: 51 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# coding=utf-8
22
#
3-
# Copyright 2014-2016 F5 Networks Inc.
3+
# Copyright 2017 F5 Networks Inc.
44
#
55
# Licensed under the Apache License, Version 2.0 (the "License");
66
# you may not use this file except in compliance with the License.
@@ -24,26 +24,40 @@
2424
``cm:device:licensing:pool:regkey:licenses:*``
2525
"""
2626

27+
import os
28+
import time
29+
import uuid
30+
2731
from f5.bigiq.resource import Collection
32+
from f5.bigiq.resource import OrganizingCollection
2833
from f5.bigiq.resource import Resource
34+
from f5.sdk_exception import F5SDKError
2935
from f5.sdk_exception import RequiredOneOf
3036
from six import iterkeys
3137

3238

39+
class Regkey(OrganizingCollection):
40+
def __init__(self, pool):
41+
super(Regkey, self).__init__(pool)
42+
self._meta_data['allowed_lazy_attributes'] = [
43+
Licenses_s
44+
]
45+
46+
3347
class Licenses_s(Collection):
3448
def __init__(self, regkey):
3549
super(Licenses_s, self).__init__(regkey)
3650
self._meta_data['required_json_kind'] = \
3751
'cm:device:licensing:pool:regkey:licenses:regkeypoollicensecollectionstate' # NOQA
38-
self._meta_data['allowed_lazy_attributes'] = [License]
52+
self._meta_data['allowed_lazy_attributes'] = [Licenses]
3953
self._meta_data['attribute_registry'] = {
40-
'cm:device:licensing:pool:regkey:licenses:regkeypoollicensestate': License # NOQA
54+
'cm:device:licensing:pool:regkey:licenses:regkeypoollicensestate': Licenses # NOQA
4155
}
4256

4357

44-
class License(Resource):
58+
class Licenses(Resource):
4559
def __init__(self, licenses_s):
46-
super(License, self).__init__(licenses_s)
60+
super(Licenses, self).__init__(licenses_s)
4761
self._meta_data['required_creation_parameters'] = {'name', }
4862
self._meta_data['required_json_kind'] = \
4963
'cm:device:licensing:pool:regkey:licenses:regkeypoollicensestate'
@@ -60,20 +74,21 @@ def __init__(self, license):
6074
super(Offerings_s, self).__init__(license)
6175
self._meta_data['required_json_kind'] = \
6276
'cm:device:licensing:pool:regkey:licenses:item:offerings:regkeypoollicenseofferingcollectionstate' # NOQA
63-
self._meta_data['allowed_lazy_attributes'] = [Offering]
77+
self._meta_data['allowed_lazy_attributes'] = [Offerings]
6478
self._meta_data['attribute_registry'] = {
65-
'cm:device:licensing:pool:regkey:licenses:item:offerings:regkeypoollicenseofferingstate': Offering # NOQA
79+
'cm:device:licensing:pool:regkey:licenses:item:offerings:regkeypoollicenseofferingstate': Offerings # NOQA
6680
}
6781

6882

69-
class Offering(Resource):
83+
class Offerings(Resource):
7084
def __init__(self, offerings_s):
71-
super(Offering, self).__init__(offerings_s)
85+
super(Offerings, self).__init__(offerings_s)
7286
self._meta_data['required_creation_parameters'] = {'regKey', }
7387
self._meta_data['required_json_kind'] = \
7488
'cm:device:licensing:pool:regkey:licenses:item:offerings:regkeypoollicenseofferingstate' # NOQA
89+
self._meta_data['allowed_lazy_attributes'] = [Members_s]
7590
self._meta_data['attribute_registry'] = {
76-
'cm:device:licensing:pool:regkey:licenses:item:offerings:regkeypoollicenseofferingstate': Offering # NOQA
91+
'cm:device:licensing:pool:regkey:licenses:item:offerings:regkey:members:regkeypoollicensemembercollectionstate': Members_s
7792
}
7893

7994

@@ -82,17 +97,16 @@ def __init__(self, pool):
8297
super(Members_s, self).__init__(pool)
8398
self._meta_data['required_json_kind'] = \
8499
'cm:device:licensing:pool:regkey:licenses:item:offerings:regkey:members:regkeypoollicensemembercollectionstate' # NOQA
85-
self._meta_data['allowed_lazy_attributes'] = [Member]
100+
self._meta_data['allowed_lazy_attributes'] = [Members]
86101
self._meta_data['attribute_registry'] = {
87-
'cm:shared:licensing:pools:licensepoolmemberstate': Member
102+
'cm:device:licensing:pool:regkey:licenses:item:offerings:regkey:members:regkeypoollicensememberstate': Members
88103
}
89104

90105

91-
class Member(Resource):
106+
class Members(Resource):
92107
def __init__(self, members_s):
93-
super(Member, self).__init__(members_s)
94-
self._meta_data['required_json_kind'] = \
95-
'cm:shared:licensing:pools:licensepoolmemberstate'
108+
super(Members, self).__init__(members_s)
109+
self._meta_data['required_json_kind'] = 'cm:device:licensing:pool:regkey:licenses:item:offerings:regkey:members:regkeypoollicensememberstate'
96110

97111
# This set is empty because the creation checking is done as
98112
# a required_one_of in the create() method
@@ -118,7 +132,7 @@ def create(self, **kwargs):
118132
raise RequiredOneOf(required_one_of)
119133

120134
def delete(self, **kwargs):
121-
"""Deletes a member from an unmanaged license pool
135+
"""Deletes a member from a license pool
122136
123137
You need to be careful with this method. When you use it, and it
124138
succeeds on the remote BIG-IP, the configuration of the BIG-IP
@@ -132,17 +146,19 @@ def delete(self, **kwargs):
132146
:param kwargs:
133147
:return:
134148
"""
135-
if 'deviceAddress' in kwargs:
136-
self._delete_unmanaged_device(**kwargs)
137-
else:
138-
self._delete_managed_device(**kwargs)
139-
140-
def _delete_managed_device(self, **kwargs):
141-
self._delete(**kwargs)
142-
143-
def _delete_unmanaged_device(self, **kwargs):
144-
if 'uuid' not in kwargs:
145-
kwargs['uuid'] = str(self.uuid)
149+
if 'id' not in kwargs:
150+
# BIG-IQ requires that you provide the ID of the members to revoke
151+
# a license from. This ID is already part of the deletion URL though.
152+
# Therefore, if you do not provide it, we enumerate it for you.
153+
delete_uri = self._meta_data['uri']
154+
if delete_uri.endswith('/'):
155+
delete_uri = delete_uri[0:-1]
156+
kwargs['id'] = os.path.basename(delete_uri)
157+
uid = uuid.UUID(kwargs['id'], version=4)
158+
if uid.hex != kwargs['id'].replace('-', ''):
159+
raise F5SDKError(
160+
"The specified ID is invalid"
161+
)
146162

147163
requests_params = self._handle_requests_params(kwargs)
148164
kwargs = self._check_for_python_keywords(kwargs)
@@ -159,3 +175,10 @@ def _delete_unmanaged_device(self, **kwargs):
159175
response = session.delete(delete_uri, json=kwargs, **requests_params)
160176
if response.status_code == 200:
161177
self.__dict__ = {'deleted': True}
178+
179+
# This sleep is necessary to prevent BIG-IQ from being able to remove
180+
# a license. It happens in certain cases that assignments can be revoked
181+
# (and license deletion started) too quickly. Therefore, we must introduce
182+
# an artificial delay here to prevent revoking from returning before
183+
# BIG-IQ would be ready to remove the license.
184+
time.sleep(1)

f5/bigiq/cm/device/licensing/pool/regkey/__init__.py

Lines changed: 0 additions & 36 deletions
This file was deleted.

0 commit comments

Comments
 (0)