You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
838 lines
32 KiB
838 lines
32 KiB
# Copyright 2015 VMware, Inc. |
|
# All Rights Reserved |
|
# |
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may |
|
# not use this file except in compliance with the License. You may obtain |
|
# a copy of the License at |
|
# |
|
# http://www.apache.org/licenses/LICENSE-2.0 |
|
# |
|
# Unless required by applicable law or agreed to in writing, software |
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
|
# License for the specific language governing permissions and limitations |
|
# under the License. |
|
# |
|
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 |
|
from vmware_nsxlib.v3 import exceptions |
|
from vmware_nsxlib.v3 import nsx_constants |
|
from vmware_nsxlib.v3 import utils |
|
|
|
LOG = log.getLogger(__name__) |
|
|
|
# TODO(asarfaty): keeping this for backwards compatibility. |
|
# core_resources.SwitchingProfileTypeId and |
|
# core_resources.PacketAddressClassifier should be used. |
|
# This code will be removed in the future. |
|
SwitchingProfileTypeId = core_resources.SwitchingProfileTypeId |
|
PacketAddressClassifier = core_resources.PacketAddressClassifier |
|
|
|
|
|
class SwitchingProfileTypes(core_resources.SwitchingProfileTypes): |
|
# TODO(asarfaty): keeping this for backwards compatibility. |
|
# This code will be removed in the future. |
|
def __init__(self): |
|
versionutils.report_deprecated_feature( |
|
LOG, |
|
'resources.SwitchingProfileTypes is deprecated. ' |
|
'Please use core_resources.SwitchingProfileTypes instead.') |
|
|
|
|
|
class WhiteListAddressTypes(core_resources.WhiteListAddressTypes): |
|
# TODO(asarfaty): keeping this for backwards compatibility. |
|
# This code will be removed in the future. |
|
def __init__(self): |
|
versionutils.report_deprecated_feature( |
|
LOG, |
|
'resources.WhiteListAddressTypes is deprecated. ' |
|
'Please use core_resources.WhiteListAddressTypes instead.') |
|
|
|
|
|
class SwitchingProfile(core_resources.NsxLibSwitchingProfile): |
|
# TODO(asarfaty): keeping this for backwards compatibility. |
|
# This code will be removed in the future. |
|
def __init__(self, rest_client, *args, **kwargs): |
|
versionutils.report_deprecated_feature( |
|
LOG, |
|
'resources.SwitchingProfile is deprecated. ' |
|
'Please use core_resources.NsxLibSwitchingProfile instead.') |
|
super(SwitchingProfile, self).__init__(rest_client) |
|
|
|
|
|
class LogicalPort(utils.NsxLibApiBase): |
|
|
|
@property |
|
def uri_segment(self): |
|
return 'logical-ports' |
|
|
|
@property |
|
def resource_type(self): |
|
return 'LogicalPort' |
|
|
|
def _build_body_attrs( |
|
self, display_name=None, |
|
admin_state=True, tags=None, |
|
address_bindings=None, |
|
switch_profile_ids=None, |
|
attachment=None, |
|
description=None, |
|
extra_configs=None): |
|
tags = tags or [] |
|
switch_profile_ids = switch_profile_ids or [] |
|
body = {} |
|
if tags: |
|
body['tags'] = tags |
|
if display_name is not None: |
|
body['display_name'] = display_name |
|
|
|
if admin_state is not None: |
|
if admin_state: |
|
body['admin_state'] = nsx_constants.ADMIN_STATE_UP |
|
else: |
|
body['admin_state'] = nsx_constants.ADMIN_STATE_DOWN |
|
|
|
if address_bindings: |
|
bindings = [] |
|
for binding in address_bindings: |
|
address_classifier = { |
|
'ip_address': binding.ip_address, |
|
'mac_address': binding.mac_address |
|
} |
|
if binding.vlan is not None: |
|
address_classifier['vlan'] = int(binding.vlan) |
|
bindings.append(address_classifier) |
|
body['address_bindings'] = bindings |
|
elif address_bindings is not None: |
|
body['address_bindings'] = [] |
|
|
|
if switch_profile_ids: |
|
profiles = [] |
|
for profile in switch_profile_ids: |
|
profiles.append({ |
|
'value': profile.profile_id, |
|
'key': profile.profile_type |
|
}) |
|
body['switching_profile_ids'] = profiles |
|
|
|
# Note that attachment could be None, meaning reset it. |
|
if attachment is not False: |
|
body['attachment'] = attachment |
|
|
|
if description is not None: |
|
body['description'] = description |
|
|
|
if extra_configs: |
|
body['extra_configs'] = extra_configs |
|
|
|
return body |
|
|
|
def _prepare_attachment(self, attachment_type, vif_uuid, |
|
allocate_addresses, vif_type, |
|
parent_vif_id, traffic_tag, app_id, tn_uuid): |
|
if attachment_type and vif_uuid: |
|
attachment = {'attachment_type': attachment_type, |
|
'id': vif_uuid} |
|
if vif_type: |
|
context = {'resource_type': nsx_constants.VIF_RESOURCE_TYPE, |
|
'allocate_addresses': allocate_addresses, |
|
'vif_type': vif_type} |
|
if parent_vif_id: |
|
context['parent_vif_id'] = parent_vif_id |
|
context['traffic_tag'] = traffic_tag |
|
context['app_id'] = app_id |
|
elif tn_uuid: |
|
context['transport_node_uuid'] = tn_uuid |
|
context['app_id'] = app_id |
|
attachment['context'] = context |
|
return attachment |
|
elif attachment_type is None and vif_uuid is None: |
|
return None # reset attachment |
|
else: |
|
return False # no attachment change |
|
|
|
def create(self, lswitch_id, vif_uuid, tags=None, |
|
attachment_type=nsx_constants.ATTACHMENT_VIF, |
|
admin_state=True, name=None, address_bindings=None, |
|
parent_vif_id=None, traffic_tag=None, |
|
switch_profile_ids=None, vif_type=None, app_id=None, |
|
allocate_addresses=nsx_constants.ALLOCATE_ADDRESS_NONE, |
|
description=None, tn_uuid=None, |
|
extra_configs=None): |
|
tags = tags or [] |
|
body = {'logical_switch_id': lswitch_id} |
|
# NOTE(arosen): If parent_vif_id is specified we need to use |
|
# CIF attachment type. |
|
attachment = self._prepare_attachment(attachment_type, vif_uuid, |
|
allocate_addresses, vif_type, |
|
parent_vif_id, traffic_tag, |
|
app_id, tn_uuid) |
|
body.update(self._build_body_attrs( |
|
display_name=name, |
|
admin_state=admin_state, tags=tags, |
|
address_bindings=address_bindings, |
|
switch_profile_ids=switch_profile_ids, |
|
attachment=attachment, |
|
description=description, |
|
extra_configs=extra_configs)) |
|
return self.client.create(self.get_path(), body=body) |
|
|
|
def delete(self, lport_id): |
|
self._delete_with_retry('%s?detach=true' % lport_id) |
|
|
|
def update(self, lport_id, vif_uuid, |
|
name=None, admin_state=None, |
|
address_bindings=None, switch_profile_ids=None, |
|
tags_update=None, tags=None, |
|
attachment_type=nsx_constants.ATTACHMENT_VIF, |
|
parent_vif_id=None, traffic_tag=None, |
|
vif_type=None, app_id=None, |
|
allocate_addresses=nsx_constants.ALLOCATE_ADDRESS_NONE, |
|
description=None, tn_uuid=None, |
|
extra_configs=None, force=False): |
|
# Do not allow tags & tags_update at the same call |
|
if tags_update and tags: |
|
raise exceptions.ManagerError( |
|
details=_("Can't support updating logical port %s both with " |
|
"tags and tags_update attributes") % lport_id) |
|
|
|
attachment = self._prepare_attachment(attachment_type, vif_uuid, |
|
allocate_addresses, vif_type, |
|
parent_vif_id, traffic_tag, |
|
app_id, tn_uuid) |
|
lport = {} |
|
if tags_update is not None: |
|
lport['tags_update'] = tags_update |
|
lport.update(self._build_body_attrs( |
|
display_name=name, |
|
admin_state=admin_state, |
|
address_bindings=address_bindings, |
|
switch_profile_ids=switch_profile_ids, |
|
attachment=attachment, |
|
description=description, |
|
extra_configs=extra_configs, |
|
tags=tags)) |
|
|
|
headers = None |
|
if force: |
|
headers = {'X-Allow-Overwrite': 'true'} |
|
|
|
return self._update_resource( |
|
self.get_path(lport_id), lport, headers=headers, retry=True) |
|
|
|
def get_by_attachment(self, attachment_type, attachment_id): |
|
"""Return all logical port matching the attachment type and Id""" |
|
url_suffix = ('?attachment_type=%s&attachment_id=%s' % |
|
(attachment_type, attachment_id)) |
|
return self.client.get(self.get_path(url_suffix)) |
|
|
|
def get_by_logical_switch(self, logical_switch_id): |
|
"""Return all logical port of a logical switch""" |
|
url_suffix = '?logical_switch_id=%s' % logical_switch_id |
|
return self.client.get(self.get_path(url_suffix)) |
|
|
|
|
|
class LogicalRouter(core_resources.NsxLibLogicalRouter): |
|
# TODO(asarfaty): keeping this for backwards compatibility. |
|
# This code will be removed in the future. |
|
def __init__(self, rest_client, *args, **kwargs): |
|
versionutils.report_deprecated_feature( |
|
LOG, |
|
'resources.LogicalRouter is deprecated. ' |
|
'Please use core_resources.NsxLibLogicalRouter instead.') |
|
super(LogicalRouter, self).__init__(rest_client) |
|
|
|
|
|
class LogicalRouterPort(utils.NsxLibApiBase): |
|
|
|
@property |
|
def uri_segment(self): |
|
return 'logical-router-ports' |
|
|
|
@staticmethod |
|
def _get_relay_binding(relay_service_uuid): |
|
return {'service_id': {'target_type': 'LogicalService', |
|
'target_id': relay_service_uuid}} |
|
|
|
def create(self, logical_router_id, |
|
display_name, |
|
tags, |
|
resource_type, |
|
logical_port_id, |
|
address_groups, |
|
edge_cluster_member_index=None, |
|
urpf_mode=None, |
|
relay_service_uuid=None): |
|
body = {'display_name': display_name, |
|
'resource_type': resource_type, |
|
'logical_router_id': logical_router_id, |
|
'tags': tags or []} |
|
if address_groups: |
|
body['subnets'] = address_groups |
|
if resource_type in [nsx_constants.LROUTERPORT_UPLINK, |
|
nsx_constants.LROUTERPORT_DOWNLINK, |
|
nsx_constants.LROUTERPORT_CENTRALIZED]: |
|
body['linked_logical_switch_port_id'] = { |
|
'target_id': logical_port_id} |
|
elif resource_type == nsx_constants.LROUTERPORT_LINKONTIER1: |
|
body['linked_logical_router_port_id'] = { |
|
'target_id': logical_port_id} |
|
elif logical_port_id: |
|
body['linked_logical_router_port_id'] = logical_port_id |
|
if edge_cluster_member_index: |
|
body['edge_cluster_member_index'] = edge_cluster_member_index |
|
if urpf_mode: |
|
body['urpf_mode'] = urpf_mode |
|
if relay_service_uuid: |
|
if (self.nsxlib and |
|
self.nsxlib.feature_supported( |
|
nsx_constants.FEATURE_DHCP_RELAY)): |
|
body['service_bindings'] = [self._get_relay_binding( |
|
relay_service_uuid)] |
|
else: |
|
LOG.error("Ignoring relay_service_uuid for router %s port: " |
|
"This feature is not supported.", logical_router_id) |
|
|
|
return self.client.create(self.get_path(), body=body) |
|
|
|
def update(self, logical_port_id, **kwargs): |
|
logical_router_port = {} |
|
# special treatment for updating/removing the relay service |
|
if 'relay_service_uuid' in kwargs: |
|
if kwargs['relay_service_uuid']: |
|
if (self.nsxlib and |
|
self.nsxlib.feature_supported( |
|
nsx_constants.FEATURE_DHCP_RELAY)): |
|
logical_router_port['service_bindings'] = [ |
|
self._get_relay_binding( |
|
kwargs['relay_service_uuid'])] |
|
else: |
|
LOG.error("Ignoring relay_service_uuid for router " |
|
"port %s: This feature is not supported.", |
|
logical_port_id) |
|
else: |
|
# delete the current one |
|
if 'service_bindings' in logical_router_port: |
|
logical_router_port['service_bindings'] = [] |
|
del kwargs['relay_service_uuid'] |
|
|
|
for k in kwargs: |
|
logical_router_port[k] = kwargs[k] |
|
|
|
return self._update_resource( |
|
self.get_path(logical_port_id), logical_router_port, retry=True) |
|
|
|
def delete(self, logical_port_id): |
|
self._delete_with_retry(logical_port_id) |
|
|
|
def get_by_lswitch_id(self, logical_switch_id): |
|
resource = '?logical_switch_id=%s' % logical_switch_id |
|
router_ports = self.client.url_get(self.get_path(resource)) |
|
result_count = int(router_ports.get('result_count', "0")) |
|
if result_count >= 2: |
|
raise exceptions.ManagerError( |
|
details=_("Can't support more than one logical router ports " |
|
"on same logical switch %s ") % logical_switch_id) |
|
elif result_count == 1: |
|
return router_ports['results'][0] |
|
else: |
|
err_msg = (_("Logical router link port not found on logical " |
|
"switch %s") % logical_switch_id) |
|
raise exceptions.ResourceNotFound( |
|
manager=self.client.nsx_api_managers, |
|
operation=err_msg) |
|
|
|
def update_by_lswitch_id(self, logical_router_id, ls_id, **payload): |
|
port = self.get_by_lswitch_id(ls_id) |
|
return self.update(port['id'], **payload) |
|
|
|
def delete_by_lswitch_id(self, ls_id): |
|
port = self.get_by_lswitch_id(ls_id) |
|
self.delete(port['id']) |
|
|
|
def get_by_router_id(self, logical_router_id): |
|
resource = '?logical_router_id=%s' % logical_router_id |
|
logical_router_ports = self.client.url_get(self.get_path(resource)) |
|
return logical_router_ports['results'] |
|
|
|
def get_tier1_link_port(self, logical_router_id): |
|
logical_router_ports = self.get_by_router_id(logical_router_id) |
|
for port in logical_router_ports: |
|
if port['resource_type'] == nsx_constants.LROUTERPORT_LINKONTIER1: |
|
return port |
|
raise exceptions.ResourceNotFound( |
|
manager=self.client.nsx_api_managers, |
|
operation="get router link port") |
|
|
|
def get_tier0_uplink_ports(self, logical_router_id): |
|
logical_router_ports = self.get_by_router_id(logical_router_id) |
|
ports = [] |
|
for port in logical_router_ports: |
|
if port['resource_type'] == nsx_constants.LROUTERPORT_UPLINK: |
|
ports.append(port) |
|
return ports |
|
|
|
def get_tier0_uplink_port(self, logical_router_id): |
|
ports = self.get_tier0_uplink_ports(logical_router_id) |
|
if ports: |
|
return ports[0] |
|
|
|
def get_tier0_uplink_subnets(self, logical_router_id): |
|
port = self.get_tier0_uplink_port(logical_router_id) |
|
if port: |
|
return port.get('subnets', []) |
|
return [] |
|
|
|
def get_tier0_uplink_cidrs(self, logical_router_id): |
|
# return a list of tier0 uplink ip/prefix addresses |
|
subnets = self.get_tier0_uplink_subnets(logical_router_id) |
|
cidrs = [] |
|
for subnet in subnets: |
|
for ip_address in subnet.get('ip_addresses'): |
|
cidrs.append('%s/%s' % (ip_address, |
|
subnet.get('prefix_length'))) |
|
return cidrs |
|
|
|
def get_tier0_uplink_ips(self, logical_router_id): |
|
# return a list of tier0 uplink ip addresses |
|
subnets = self.get_tier0_uplink_subnets(logical_router_id) |
|
ips = [] |
|
for subnet in subnets: |
|
for ip_address in subnet.get('ip_addresses'): |
|
ips.append(ip_address) |
|
return ips |
|
|
|
|
|
class MetaDataProxy(core_resources.NsxLibMetadataProxy): |
|
# TODO(asarfaty): keeping this for backwards compatibility. |
|
# This code will be removed in the future. |
|
def __init__(self, rest_client, *args, **kwargs): |
|
versionutils.report_deprecated_feature( |
|
LOG, |
|
'resources.MetaDataProxy is deprecated. ' |
|
'Please use core_resources.NsxLibMetadataProxy instead.') |
|
super(MetaDataProxy, self).__init__(rest_client) |
|
|
|
|
|
class DhcpProfile(core_resources.NsxLibDhcpProfile): |
|
# TODO(asarfaty): keeping this for backwards compatibility. |
|
# This code will be removed in the future. |
|
def __init__(self, rest_client, *args, **kwargs): |
|
versionutils.report_deprecated_feature( |
|
LOG, |
|
'resources.DhcpProfile is deprecated. ' |
|
'Please use core_resources.NsxLibDhcpProfile instead.') |
|
super(DhcpProfile, self).__init__(rest_client) |
|
|
|
|
|
class LogicalDhcpServer(utils.NsxLibApiBase): |
|
|
|
def get_dhcp_opt_code(self, name): |
|
return utils.get_dhcp_opt_code(name) |
|
|
|
@property |
|
def uri_segment(self): |
|
return 'dhcp/servers' |
|
|
|
@property |
|
def resource_type(self): |
|
return 'LogicalDhcpServer' |
|
|
|
def _construct_server(self, body, dhcp_profile_id=None, server_ip=None, |
|
name=None, dns_nameservers=None, domain_name=None, |
|
gateway_ip=False, options=None, tags=None): |
|
if name: |
|
body['display_name'] = name |
|
if dhcp_profile_id: |
|
body['dhcp_profile_id'] = dhcp_profile_id |
|
if server_ip: |
|
body['ipv4_dhcp_server']['dhcp_server_ip'] = server_ip |
|
if dns_nameservers is not None: |
|
# Note that [] is valid for dns_nameservers, means deleting it. |
|
body['ipv4_dhcp_server']['dns_nameservers'] = dns_nameservers |
|
if domain_name: |
|
body['ipv4_dhcp_server']['domain_name'] = domain_name |
|
if gateway_ip is not False: |
|
# Note that None is valid for gateway_ip, means deleting it. |
|
body['ipv4_dhcp_server']['gateway_ip'] = gateway_ip |
|
if options: |
|
body['ipv4_dhcp_server']['options'] = options |
|
if tags: |
|
body['tags'] = tags |
|
|
|
def create(self, dhcp_profile_id, server_ip, name=None, |
|
dns_nameservers=None, domain_name=None, gateway_ip=False, |
|
options=None, tags=None): |
|
body = {'ipv4_dhcp_server': {}} |
|
self._construct_server(body, dhcp_profile_id, server_ip, name, |
|
dns_nameservers, domain_name, gateway_ip, |
|
options, tags) |
|
return self.client.create(self.get_path(), body=body) |
|
|
|
def update(self, uuid, dhcp_profile_id=None, server_ip=None, name=None, |
|
dns_nameservers=None, domain_name=None, gateway_ip=False, |
|
options=None, tags=None): |
|
body = {'ipv4_dhcp_server': {}} |
|
self._construct_server(body, dhcp_profile_id, server_ip, name, |
|
dns_nameservers, domain_name, gateway_ip, |
|
options, tags) |
|
return self._update_with_retry(uuid, body) |
|
|
|
def create_binding(self, server_uuid, mac, ip, hostname=None, |
|
lease_time=None, options=None, gateway_ip=False): |
|
body = {'mac_address': mac, 'ip_address': ip} |
|
if hostname: |
|
body['host_name'] = hostname |
|
if lease_time: |
|
body['lease_time'] = lease_time |
|
if options: |
|
body['options'] = options |
|
if gateway_ip is not False: |
|
# Note that None is valid for gateway_ip, means deleting it. |
|
body['gateway_ip'] = gateway_ip |
|
url = "%s/static-bindings" % server_uuid |
|
return self.client.url_post(self.get_path(url), body) |
|
|
|
def get_binding(self, server_uuid, binding_uuid): |
|
url = "%s/static-bindings/%s" % (server_uuid, binding_uuid) |
|
return self.get(url) |
|
|
|
def update_binding(self, server_uuid, binding_uuid, **kwargs): |
|
body = {} |
|
body.update(kwargs) |
|
url = "%s/static-bindings/%s" % (server_uuid, binding_uuid) |
|
self._update_resource(self.get_path(url), body, retry=True) |
|
|
|
def delete_binding(self, server_uuid, binding_uuid): |
|
url = "%s/static-bindings/%s" % (server_uuid, binding_uuid) |
|
return self.delete(url) |
|
|
|
|
|
class IpPool(utils.NsxLibApiBase): |
|
@property |
|
def uri_segment(self): |
|
return 'pools/ip-pools' |
|
|
|
@property |
|
def resource_type(self): |
|
return 'IpPool' |
|
|
|
def _generate_ranges(self, cidr, gateway_ip): |
|
"""Create list of ranges from the given cidr. |
|
|
|
Ignore the gateway_ip, if defined |
|
""" |
|
ip_set = netaddr.IPSet(netaddr.IPNetwork(cidr)) |
|
if gateway_ip: |
|
ip_set.remove(gateway_ip) |
|
return [{"start": str(r[0]), |
|
"end": str(r[-1])} for r in ip_set.iter_ipranges()] |
|
|
|
def create(self, cidr, allocation_ranges=None, display_name=None, |
|
description=None, gateway_ip=None, dns_nameservers=None, |
|
tags=None): |
|
"""Create an IpPool. |
|
|
|
Arguments: |
|
cidr: (required) |
|
allocation_ranges: (optional) a list of dictionaries, each with |
|
'start' and 'end' keys, and IP values. |
|
If None: the cidr will be used to create the ranges, |
|
excluding the gateway. |
|
display_name: (optional) |
|
description: (optional) |
|
gateway_ip: (optional) |
|
dns_nameservers: (optional) list of addresses |
|
""" |
|
if not cidr: |
|
raise exceptions.InvalidInput(operation="IP Pool create", |
|
arg_name="cidr", arg_val=cidr) |
|
if not allocation_ranges: |
|
# generate ranges from (cidr - gateway) |
|
allocation_ranges = self._generate_ranges(cidr, gateway_ip) |
|
|
|
subnet = {"allocation_ranges": allocation_ranges, |
|
"cidr": cidr} |
|
if gateway_ip: |
|
subnet["gateway_ip"] = gateway_ip |
|
if dns_nameservers: |
|
subnet["dns_nameservers"] = dns_nameservers |
|
|
|
body = {"subnets": [subnet]} |
|
if description: |
|
body["description"] = description |
|
if display_name: |
|
body["display_name"] = display_name |
|
if tags: |
|
body['tags'] = tags |
|
|
|
return self.client.create(self.get_path(), body=body) |
|
|
|
def delete(self, pool_id, force=False): |
|
url = pool_id |
|
if force: |
|
url += '?force=%s' % force |
|
return self._delete_by_path_with_retry(self.get_path(url)) |
|
|
|
def _update_param_in_pool(self, args_dict, key, pool_data): |
|
# update the arg only if it exists in the args dictionary |
|
if key in args_dict: |
|
if args_dict[key]: |
|
pool_data[key] = args_dict[key] |
|
else: |
|
# remove the current value |
|
del pool_data[key] |
|
|
|
def update(self, pool_id, **kwargs): |
|
"""Update the given attributes in the current pool configuration.""" |
|
# Get the current pool, and remove irrelevant fields |
|
pool = self.get(pool_id) |
|
for key in ["resource_type", "_create_time", "_create_user" |
|
"_last_modified_user", "_last_modified_time"]: |
|
pool.pop(key, None) |
|
|
|
# update only the attributes in kwargs |
|
self._update_param_in_pool(kwargs, 'display_name', pool) |
|
self._update_param_in_pool(kwargs, 'description', pool) |
|
self._update_param_in_pool(kwargs, 'tags', pool) |
|
self._update_param_in_pool(kwargs, 'gateway_ip', |
|
pool["subnets"][0]) |
|
self._update_param_in_pool(kwargs, 'dns_nameservers', |
|
pool["subnets"][0]) |
|
self._update_param_in_pool(kwargs, 'allocation_ranges', |
|
pool["subnets"][0]) |
|
self._update_param_in_pool(kwargs, 'cidr', |
|
pool["subnets"][0]) |
|
return self.client.update(self.get_path(pool_id), pool) |
|
|
|
def allocate(self, pool_id, ip_addr=None, display_name=None, tags=None): |
|
"""Allocate an IP from a pool.""" |
|
# Note: Currently the backend does not support allocation of a |
|
# specific IP, so an exception will be raised by the backend. |
|
# Depending on the backend version, this may be allowed in the future |
|
url = "%s?action=ALLOCATE" % pool_id |
|
body = {"allocation_id": ip_addr} |
|
if tags is not None: |
|
body['tags'] = tags |
|
if display_name is not None: |
|
body['display_name'] = display_name |
|
return self.client.url_post(self.get_path(url), body=body) |
|
|
|
def release(self, pool_id, ip_addr): |
|
"""Release an IP back to a pool.""" |
|
url = "%s?action=RELEASE" % pool_id |
|
body = {"allocation_id": ip_addr} |
|
return self.client.url_post(self.get_path(url), body=body) |
|
|
|
def get_allocations(self, pool_id): |
|
"""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( |
|
'client_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'][ |
|
'client_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) |
|
|
|
|
|
class NsxlibClusterNodesConfig(utils.NsxLibApiBase): |
|
@property |
|
def uri_segment(self): |
|
return 'cluster/nodes' |
|
|
|
@property |
|
def resource_type(self): |
|
return 'ClusterNodeConfig' |
|
|
|
def delete(self, uuid): |
|
"""Not supported""" |
|
msg = _("Delete is not supported for %s") % self.uri_segment |
|
raise exceptions.ManagerError(details=msg) |
|
|
|
def get_managers_ips(self): |
|
manager_ips = [] |
|
nodes_config = self.client.get(self.get_path())['results'] |
|
for node in nodes_config: |
|
if 'manager_role' in node: |
|
manager_conf = node['manager_role'] |
|
if 'api_listen_addr' in manager_conf: |
|
list_addr = manager_conf['mgmt_cluster_listen_addr'] |
|
ip = list_addr['ip_address'] |
|
if ip != '127.0.0.1': |
|
manager_ips.append(list_addr['ip_address']) |
|
|
|
return manager_ips |
|
|
|
|
|
class NsxlibHostSwitchProfiles(utils.NsxLibApiBase): |
|
@property |
|
def uri_segment(self): |
|
return 'host-switch-profiles' |
|
|
|
@property |
|
def resource_type(self): |
|
return 'UplinkHostSwitchProfile' |
|
|
|
|
|
class Inventory(utils.NsxLibApiBase): |
|
"""REST APIs to support inventory service.""" |
|
|
|
RESOURCES_PATH = {"ContainerCluster": "container-clusters", |
|
"ContainerProject": "container-projects", |
|
"ContainerApplication": "container-applications", |
|
"ContainerApplicationInstance": |
|
"container-application-instances", |
|
"ContainerClusterNode": "container-cluster-nodes", |
|
"ContainerNetworkPolicy": "container-network-policies", |
|
"ContainerIngressPolicy": "container-ingress-policies"} |
|
SEGMENT_URI = 'fabric' |
|
SEGMENT_URI_FOR_UPDATE = 'inventory/container' |
|
|
|
@property |
|
def uri_segment(self): |
|
# only serves for get, list and delete. For batch update, |
|
# another base url is used. The reason is update API is internal. |
|
return self.SEGMENT_URI |
|
|
|
@property |
|
def resource_type(self): |
|
return 'Inventory' |
|
|
|
def update(self, cluster_id, updates): |
|
"""This method supports multiple updates in a batching way.""" |
|
items = [] |
|
for update_type, update_object in updates: |
|
item = {} |
|
item["object_update_type"] = update_type |
|
item["container_object"] = update_object |
|
items.append(item) |
|
body = {"container_inventory_objects": items} |
|
request_url = "%s/%s?action=updates" % ( |
|
self.SEGMENT_URI_FOR_UPDATE, cluster_id) |
|
return self.client.url_post(request_url, body) |
|
|
|
def get(self, resource_type, resource_id): |
|
if not resource_type: |
|
msg = "null resource type is not supported" |
|
raise exceptions.ResourceNotFound(details=msg) |
|
request_url = "%s/%s" % ( |
|
self.get_path(self._get_path_for_resource(resource_type)), |
|
resource_id) |
|
return self.client.url_get(request_url) |
|
|
|
def list(self, cluster_id, resource_type): |
|
if not resource_type: |
|
msg = "null resource type is not supported" |
|
raise exceptions.ResourceNotFound(details=msg) |
|
request_url = "%s?container_cluster_id=%s" % ( |
|
self.get_path(self._get_path_for_resource(resource_type)), |
|
cluster_id) |
|
return self.client.url_list(request_url) |
|
|
|
def delete(self, resource_type, resource_id): |
|
if not resource_type: |
|
msg = "null resource type is not supported" |
|
raise exceptions.ResourceNotFound(details=msg) |
|
request_url = "%s/%s" % ( |
|
self.get_path(self._get_path_for_resource(resource_type)), |
|
resource_id) |
|
return self.client.url_delete(request_url) |
|
|
|
def create(self, resource_type, resource): |
|
if not resource_type: |
|
msg = "null resource type is not supported" |
|
raise exceptions.ResourceNotFound(details=msg) |
|
request_url = self.get_path( |
|
self._get_path_for_resource(resource_type)) |
|
return self.client.url_post(request_url, resource) |
|
|
|
def _get_path_for_resource(self, resource_type): |
|
path = self.RESOURCES_PATH.get(resource_type) |
|
if not path: |
|
msg = "backend resource %s is not supported" % resource_type |
|
raise exceptions.ResourceNotFound(details=msg) |
|
return path |
|
|
|
|
|
class SystemHealth(utils.NsxLibApiBase): |
|
@property |
|
def uri_segment(self): |
|
return 'systemhealth' |
|
|
|
@property |
|
def resource_type(self): |
|
return 'SystemHealth' |
|
|
|
def create_ncp_status(self, cluster_id, status): |
|
url = '/container-cluster/ncp/status' |
|
body = {'cluster_id': cluster_id, 'status': status} |
|
return self.client.create(self.get_path(url), body=body)
|
|
|