A common library that interfaces with VMware NSX.
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

# 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)