Support get & update for rate limit

Rate limit is returned as a part of node/services/http response
In order to update one should:
1. GET the current configuration
2. PUT the updated configuration (the http response will be 202)
3. POST a restart action request (the http response will be 202)

Change-Id: I35a05f9810832e9a22ec9db43f167f13df0744a4
This commit is contained in:
Adit Sarfaty 2018-01-10 15:27:04 +02:00
parent 9bf329a0e3
commit faeb5b8605
5 changed files with 142 additions and 14 deletions

View File

@ -1650,6 +1650,54 @@ class LogicalDhcpServerTestCase(BaseTestResource):
headers=self.default_headers())
class NodeHttpServicePropertiesTestCase(BaseTestResource):
def setUp(self):
super(NodeHttpServicePropertiesTestCase, self).setUp(
resources.NodeHttpServiceProperties)
def test_get_resource(self):
self.skipTest("The action is not supported by this resource")
def test_list_all(self):
self.skipTest("The action is not supported by this resource")
def test_delete_resource(self):
self.skipTest("The action is not supported by this resource")
def test_get_rate_limit(self):
mocked_resource = self.get_mocked_resource()
rate_limit = 40
body = {'service_properties': {'api_rate_limit': rate_limit}}
with mock.patch("vmware_nsxlib.v3.NsxLib.get_version",
return_value='2.2.0'),\
mock.patch.object(mocked_resource.client, "url_get",
return_value=body):
result = mocked_resource.get_rate_limit()
self.assertEqual(rate_limit, result)
def test_update_rate_limit(self):
mocked_resource = self.get_mocked_resource()
old_rate_limit = 40
new_rate_limit = 50
body = {'service_properties': {'api_rate_limit': old_rate_limit}}
with mock.patch("vmware_nsxlib.v3.NsxLib.get_version",
return_value='2.2.0'),\
mock.patch.object(mocked_resource.client, "url_get",
return_value=body):
mocked_resource.update_rate_limit(new_rate_limit)
body['service_properties']['api_rate_limit'] = new_rate_limit
test_client.assert_json_call(
'put', mocked_resource,
'https://1.2.3.4/api/v1/node/services/http',
data=jsonutils.dumps(body, sort_keys=True),
headers=self.default_headers())
test_client.assert_json_call(
'post', mocked_resource,
'https://1.2.3.4/api/v1/node/services/http?action=restart',
headers=self.default_headers())
class DummyCachedResource(utils.NsxLibApiBase):
@property

View File

@ -276,6 +276,8 @@ class NsxLib(NsxLibBase):
self.client, self.nsxlib_config, nsxlib=self)
self.vpn_ipsec = vpn_ipsec.VpnIpSec(
self.client, self.nsxlib_config, nsxlib=self)
self.http_services = resources.NodeHttpServiceProperties(
self.client, self.nsxlib_config, nsxlib=self)
# Update tag limits
self.tag_limits = self.get_tag_limits()
@ -300,6 +302,7 @@ class NsxLib(NsxLibBase):
if (feature == nsx_constants.FEATURE_VLAN_ROUTER_INTERFACE or
feature == nsx_constants.FEATURE_IPSEC_VPN or
feature == nsx_constants.FEATURE_ON_BEHALF_OF or
feature == nsx_constants.FEATURE_RATE_LIMIT or
feature == nsx_constants.FEATURE_TRUNK_VLAN):
return True

View File

@ -87,14 +87,18 @@ class RESTClient(object):
def get(self, uuid, headers=None, silent=False):
return self.url_get(uuid, headers=headers, silent=silent)
def delete(self, uuid, headers=None):
return self.url_delete(uuid, headers=headers)
def delete(self, uuid, headers=None, expected_results=None):
return self.url_delete(uuid, headers=headers,
expected_results=expected_results)
def update(self, uuid, body=None, headers=None):
return self.url_put(uuid, body, headers=headers)
def update(self, uuid, body=None, headers=None, expected_results=None):
return self.url_put(uuid, body, headers=headers,
expected_results=expected_results)
def create(self, resource='', body=None, headers=None):
return self.url_post(resource, body, headers=headers)
def create(self, resource='', body=None, headers=None,
expected_results=None):
return self.url_post(resource, body, headers=headers,
expected_results=expected_results)
def url_list(self, url, headers=None, silent=False):
concatenate_response = self.url_get(url, headers=headers)
@ -112,14 +116,17 @@ class RESTClient(object):
return self._rest_call(url, method='GET', headers=headers,
silent=silent)
def url_delete(self, url, headers=None):
return self._rest_call(url, method='DELETE', headers=headers)
def url_delete(self, url, headers=None, expected_results=None):
return self._rest_call(url, method='DELETE', headers=headers,
expected_results=expected_results)
def url_put(self, url, body, headers=None):
return self._rest_call(url, method='PUT', body=body, headers=headers)
def url_put(self, url, body, headers=None, expected_results=None):
return self._rest_call(url, method='PUT', body=body, headers=headers,
expected_results=expected_results)
def url_post(self, url, body, headers=None):
return self._rest_call(url, method='POST', body=body, headers=headers)
def url_post(self, url, body, headers=None, expected_results=None):
return self._rest_call(url, method='POST', body=body, headers=headers,
expected_results=expected_results)
def _raise_error(self, status_code, operation, result_msg,
error_code=None):
@ -177,7 +184,7 @@ class RESTClient(object):
return re.sub(pattern, '"password": "********"', json)
def _rest_call(self, url, method='GET', body=None, headers=None,
silent=False):
silent=False, expected_results=None):
request_headers = headers.copy() if headers else {}
request_headers.update(self._default_headers)
if utils.INJECT_HEADERS_CALLBACK:
@ -206,8 +213,10 @@ class RESTClient(object):
result.json() if result.content else '',
te - ts)
if not expected_results:
expected_results = RESTClient._VERB_RESP_CODES[method.lower()]
self._validate_result(
result, RESTClient._VERB_RESP_CODES[method.lower()],
result, expected_results,
_("%(verb)s %(url)s") % {'verb': method, 'url': request_url},
silent=silent)
return result

View File

@ -135,6 +135,7 @@ FEATURE_LOAD_BALANCER = 'Load Balancer'
FEATURE_DHCP_RELAY = 'DHCP Relay'
FEATURE_NSX_POLICY = 'NSX Policy'
FEATURE_VLAN_ROUTER_INTERFACE = 'VLAN Router Interface'
FEATURE_RATE_LIMIT = 'Requests Rate Limit'
FEATURE_IPSEC_VPN = 'IPSec VPN'
FEATURE_ON_BEHALF_OF = 'On Behalf Of'
FEATURE_TRUNK_VLAN = 'Trunk Vlan'

View File

@ -17,6 +17,7 @@ import netaddr
from oslo_log import log
from oslo_log import versionutils
import requests
from vmware_nsxlib._i18n import _
from vmware_nsxlib.v3 import core_resources
@ -602,3 +603,69 @@ class IpPool(utils.NsxLibApiBase):
"""Return information about the allocated IPs in the pool."""
url = "%s/allocations" % pool_id
return self.client.url_get(self.get_path(url))
class NodeHttpServiceProperties(utils.NsxLibApiBase):
@property
def uri_segment(self):
return 'node/services/http'
@property
def resource_type(self):
return 'NodeHttpServiceProperties'
def get_properties(self):
return self.client.get(self.get_path())
def get_rate_limit(self):
if (self.nsxlib and
not self.nsxlib.feature_supported(
nsx_constants.FEATURE_RATE_LIMIT)):
msg = (_("Rate limit is not supported by NSX version %s") %
self.nsxlib.get_version())
raise exceptions.ManagerError(details=msg)
properties = self.get_properties()
return properties.get('service_properties', {}).get('api_rate_limit')
def update_rate_limit(self, value):
"""update the NSX rate limit. default value is 40. 0 means no limit"""
if (self.nsxlib and
not self.nsxlib.feature_supported(
nsx_constants.FEATURE_RATE_LIMIT)):
msg = (_("Rate limit is not supported by NSX version %s") %
self.nsxlib.get_version())
raise exceptions.ManagerError(details=msg)
properties = self.get_properties()
if 'service_properties' in properties:
properties['service_properties']['api_rate_limit'] = int(value)
# update the value using a PUT command, which is expected to return 202
expected_results = [requests.codes.accepted]
self.client.update(self.uri_segment, properties,
expected_results=expected_results)
# restart the http service using POST, which is expected to return 202
restart_url = self.uri_segment + '?action=restart'
self.client.create(restart_url, expected_results=expected_results)
def delete(self, uuid):
"""Not supported"""
msg = _("Delete is not supported for %s") % self.uri_segment
raise exceptions.ManagerError(details=msg)
def get(self, uuid):
"""Not supported"""
msg = _("Get is not supported for %s") % self.uri_segment
raise exceptions.ManagerError(details=msg)
def list(self):
"""Not supported"""
msg = _("List is not supported for %s") % self.uri_segment
raise exceptions.ManagerError(details=msg)
def find_by_display_name(self, display_name):
"""Not supported"""
msg = _("Find is not supported for %s") % self.uri_segment
raise exceptions.ManagerError(details=msg)