Skip to content

Commit 5d23aaf

Browse files
committed
Implement client-ssl profile creation
Issues: Fixes #49 Problem: The SDK didn't support ssl-client profile creation Analysis: Add necessary code to support it. Tests: * f5/bigip/cm/autodeploy/test/test_software_image_uploads.py * f5/bigip/shared/test/test_file_uploads.py * test/functional/ssl_profile_creation/
1 parent 5d3cea0 commit 5d23aaf

9 files changed

Lines changed: 290 additions & 35 deletions

File tree

f5/bigip/cm/autodeploy/test/test_software_image_uploads.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def test_software_image_uploads_80a(tmpdir):
3333
sius.upload_image(str(filepath), chunk_size=CHUNKSIZE)
3434
session_mock = mr._meta_data['icr_session']
3535
for i in range(4):
36-
d = session_mock.post.call_args_list[i][1]['requests_params']['data']
36+
d = session_mock.post.call_args_list[i][1]['data']
3737
assert d == 'a'*CHUNKSIZE
3838

3939

@@ -47,9 +47,9 @@ def test_software_image_uploads_70a(tmpdir):
4747
sius.upload_image(str(filepath), chunk_size=CHUNKSIZE)
4848
for i in range(3):
4949
print(i)
50-
d = session_mock.post.call_args_list[i][1]['requests_params']['data']
50+
d = session_mock.post.call_args_list[i][1]['data']
5151
assert d == 'a'*CHUNKSIZE
52-
lchunk = session_mock.post.call_args_list[3][1]['requests_params']['data']
52+
lchunk = session_mock.post.call_args_list[3][1]['data']
5353
assert 10*'a' == lchunk
5454

5555

f5/bigip/mixins.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#
1717
# NOTE: Code taken from Effective Python Item 26
1818

19+
import logging
1920
import os
2021

2122
from f5.sdk_exception import F5SDKError
@@ -245,6 +246,7 @@ def _exec_cmd(self, command, **kwargs):
245246

246247
class FileUploadMixin(object):
247248
def _upload(self, filepathname, **kwargs):
249+
requests_params = self._handle_requests_params(kwargs)
248250
session = self._meta_data['icr_session']
249251
chunk_size = kwargs.pop('chunk_size', 512 * 1024)
250252
size = os.path.getsize(filepathname)
@@ -263,8 +265,11 @@ def _upload(self, filepathname, **kwargs):
263265
headers = {
264266
'Content-Range': '%s-%s/%s' % (start, end - 1, size),
265267
'Content-Type': 'application/octet-stream'}
266-
req_params = {'data': file_slice,
267-
'headers': headers,
268-
'verify': False}
269-
session.post(self.file_bound_uri, requests_params=req_params)
268+
data = {'data': file_slice,
269+
'headers': headers,
270+
'verify': False}
271+
logging.debug(data)
272+
requests_params.update(data)
273+
session.post(self.file_bound_uri,
274+
**requests_params)
270275
start += current_bytes

f5/bigip/resource.py

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,30 @@ def _check_generation(self):
258258
"(" + str(self.generation) + ")")
259259
raise GenerationMismatch(error_message)
260260

261+
def _handle_requests_params(self, kwargs):
262+
"""Validate parameters that will be passed to the requests verbs.
263+
264+
This method validates that there is no conflict in the names of the
265+
requests_params passed to the function an the other kwargs. It also
266+
ensures that the required request parameters for the object are
267+
added to the request params that are passed into the verbs. An
268+
example of the latter is ensuring that a certain version of the API
269+
is always called to add 'ver=11.6.0' to the url.
270+
"""
271+
requests_params = kwargs.pop('requests_params', {})
272+
for param in requests_params:
273+
if param in kwargs:
274+
error_message = 'Requests Parameter %r collides with a load'\
275+
' parameter of the same name.' % param
276+
raise RequestParamKwargCollision(error_message)
277+
278+
# If we have an icontrol version we need to add 'ver' to params
279+
if self._meta_data['icontrol_version']:
280+
params = requests_params.pop('params', {})
281+
params.update({'ver': self._meta_data['icontrol_version']})
282+
requests_params.update({'params': params})
283+
return requests_params
284+
261285
@property
262286
def raw(self):
263287
"""Display the attributes that the current object has and their values.
@@ -385,30 +409,6 @@ def update(self, **kwargs):
385409
# Need to implement checking for valid params here.
386410
self._update(**kwargs)
387411

388-
def _handle_requests_params(self, kwargs):
389-
"""Validate parameters that will be passed to the requests verbs.
390-
391-
This method validates that there is no conflict in the names of the
392-
requests_params passed to the function an the other kwargs. It also
393-
ensures that the required request parameters for the object are
394-
added to the request params that are passed into the verbs. An
395-
example of the latter is ensuring that a certain version of the API
396-
is always called to add 'ver=11.6.0' to the url.
397-
"""
398-
requests_params = kwargs.pop('requests_params', {})
399-
for param in requests_params:
400-
if param in kwargs:
401-
error_message = 'Requests Parameter %r collides with a load'\
402-
' parameter of the same name.' % param
403-
raise RequestParamKwargCollision(error_message)
404-
405-
# If we have an icontrol version we need to add 'ver' to params
406-
if self._meta_data['icontrol_version']:
407-
params = requests_params.pop('params', {})
408-
params.update({'ver': self._meta_data['icontrol_version']})
409-
requests_params.update({'params': params})
410-
return requests_params
411-
412412
def _refresh(self, **kwargs):
413413
"""wrapped by `refresh` override that in a subclass to customize"""
414414
requests_params = self._handle_requests_params(kwargs)

f5/bigip/shared/test/test_file_uploads.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
import pytest
1919

2020
from f5.bigip import ManagementRoot
21-
from f5.bigip.shared.file_transfer import FileMustNotHaveDotISOExtension
21+
from f5.bigip.shared.file_transfer import\
22+
FileMustNotHaveDotISOExtension
2223

2324

2425
def test_file_upload_80a(tmpdir):
@@ -30,7 +31,7 @@ def test_file_upload_80a(tmpdir):
3031
ftu.upload_file(filepath.__str__(), chunk_size=20)
3132
session_mock = mr._meta_data['icr_session']
3233
for i in range(4):
33-
d = session_mock.post.call_args_list[i][1]['requests_params']['data']
34+
d = session_mock.post.call_args_list[i][1]['data']
3435
assert d == 'aaaaaaaaaaaaaaaaaaaa'
3536

3637

@@ -44,9 +45,9 @@ def test_file_upload_70a(tmpdir):
4445
session_mock = mr._meta_data['icr_session']
4546
for i in range(3):
4647
print(i)
47-
d = session_mock.post.call_args_list[i][1]['requests_params']['data']
48+
d = session_mock.post.call_args_list[i][1]['data']
4849
assert d == 'aaaaaaaaaaaaaaaaaaaa'
49-
lchunk = session_mock.post.call_args_list[3][1]['requests_params']['data']
50+
lchunk = session_mock.post.call_args_list[3][1]['data']
5051
assert 10*'a' == lchunk
5152

5253

f5/bigip/tm/ltm/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
from f5.bigip.tm.ltm.persistence import Persistences
3636
from f5.bigip.tm.ltm.policy import Policys
3737
from f5.bigip.tm.ltm.pool import Pools
38+
from f5.bigip.tm.ltm.profile import Profile
3839
from f5.bigip.tm.ltm.rule import Rules
3940
from f5.bigip.tm.ltm.snat import Snats
4041
from f5.bigip.tm.ltm.snat_translation import Snat_Translations
@@ -54,6 +55,7 @@ def __init__(self, tm):
5455
Persistences,
5556
Policys,
5657
Pools,
58+
Profile,
5759
Rules,
5860
Snats,
5961
Snatpools,

f5/bigip/tm/ltm/profile.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
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+
"""BIG-IP® system config module
18+
19+
REST URI
20+
``http://localhost/mgmt/tm/ltm/profile``
21+
22+
GUI Path
23+
``System``
24+
25+
REST Kind
26+
``tm:sys:*``
27+
"""
28+
import logging
29+
30+
from f5.bigip.resource import Collection
31+
from f5.bigip.resource import OrganizingCollection
32+
from f5.bigip.resource import Resource
33+
34+
35+
class Profile(OrganizingCollection):
36+
def __init__(self, ltm):
37+
super(Profile, self).__init__(ltm)
38+
self._meta_data['allowed_lazy_attributes'] = [
39+
Client_Ssls
40+
]
41+
42+
43+
class Client_Ssls(Collection):
44+
def __init__(self, profile):
45+
super(Client_Ssls, self).__init__(profile)
46+
self._meta_data['allowed_lazy_attributes'] = [
47+
Client_Ssl
48+
]
49+
self._meta_data['attribute_registry'] =\
50+
{'tm:ltm:profile:client-ssl:client-sslstate': Client_Ssl}
51+
52+
53+
class Client_Ssl(Resource):
54+
def __init__(self, client_ssls):
55+
super(Client_Ssl, self).__init__(client_ssls)
56+
self._meta_data['required_json_kind'] =\
57+
'tm:ltm:profile:client-ssl:client-sslstate'
58+
59+
def create(self, certname, keyname):
60+
payload = {"name": certname[:-4],
61+
"cert": certname,
62+
"key": keyname}
63+
logging.debug(payload)
64+
return self._create(**payload)

f5/bigip/tm/sys/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
from f5.bigip.resource import OrganizingCollection
3131
from f5.bigip.tm.sys.application import Applications
3232
from f5.bigip.tm.sys.config import Config
33+
from f5.bigip.tm.sys.crypto import Crypto
3334
from f5.bigip.tm.sys.db import Dbs
3435
from f5.bigip.tm.sys.dns import Dns
3536
from f5.bigip.tm.sys.failover import Failover
@@ -46,6 +47,7 @@ def __init__(self, tm):
4647
super(Sys, self).__init__(tm)
4748
self._meta_data['allowed_lazy_attributes'] = [
4849
Config,
50+
Crypto,
4951
Folders,
5052
Applications,
5153
Performances,

f5/bigip/tm/sys/crypto.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
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+
"""BIG-IP® system config module
18+
19+
REST URI
20+
``http://localhost/mgmt/tm/sys/config``
21+
22+
GUI Path
23+
N/A
24+
25+
REST Kind
26+
``tm:sys:config:*``
27+
"""
28+
29+
from f5.bigip.resource import Collection
30+
from f5.bigip.resource import OrganizingCollection
31+
from f5.bigip.resource import Resource
32+
33+
34+
class Crypto(OrganizingCollection):
35+
def __init__(self, sys):
36+
super(Crypto, self).__init__(sys)
37+
self._meta_data['allowed_lazy_attributes'] = [
38+
Certs,
39+
Keys
40+
]
41+
42+
43+
class Keys(Collection):
44+
def __init__(self, crypto):
45+
super(Keys, self).__init__(crypto)
46+
self._meta_data['allowed_lazy_attributes'] = [
47+
Key
48+
]
49+
self._meta_data['attribute_registry'] =\
50+
{'tm:sys:crypto:key:keystate': Key}
51+
52+
def install_key(self, certfilename, keyfilename):
53+
payload =\
54+
{"from-local-file": "/var/config/rest/downloads/%s" % keyfilename,
55+
"command": "install",
56+
"name": certfilename[:-4]}
57+
self._meta_data['icr_session'].post(self._meta_data['uri'],
58+
json=payload)
59+
60+
61+
class Key(Resource):
62+
def __init__(self, keys):
63+
super(Key, self).__init__(keys)
64+
self._meta_data['required_json_kind'] = 'tm:sys:crypto:key:keystate'
65+
66+
67+
class Certs(Collection):
68+
def __init__(self, crypto):
69+
super(Certs, self).__init__(crypto)
70+
self._meta_data['allowed_lazy_attributes'] = [
71+
Cert
72+
]
73+
self._meta_data['attribute_registry'] =\
74+
{'tm:sys:crypto:cert:certstate': Cert}
75+
76+
def install_cert(self, certfilename):
77+
payload =\
78+
{"from-local-file": "/var/config/rest/downloads/%s" % certfilename,
79+
"command": "install",
80+
"name": certfilename[:-4]}
81+
self._meta_data['icr_session'].post(self._meta_data['uri'],
82+
json=payload)
83+
84+
85+
class Cert(Resource):
86+
def __init__(self, certs):
87+
super(Cert, self).__init__(certs)
88+
self._meta_data['required_json_kind'] = 'tm:sys:crypto:cert:certstate'

0 commit comments

Comments
 (0)