1d7e01ae20
This reverts commit 44d0cb5e0f
.
Change-Id: I893eb52b55746ae8edfbe43a7fda175ffc890cc5
286 lines
11 KiB
Python
286 lines
11 KiB
Python
# Copyright 2016 OpenStack Foundation
|
|
# 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 abc
|
|
|
|
from oslo_log import log
|
|
import six
|
|
|
|
from vmware_nsxlib._i18n import _
|
|
from vmware_nsxlib.v3 import client
|
|
from vmware_nsxlib.v3 import cluster
|
|
from vmware_nsxlib.v3 import exceptions
|
|
from vmware_nsxlib.v3 import utils
|
|
|
|
LOG = log.getLogger(__name__)
|
|
|
|
|
|
@six.add_metaclass(abc.ABCMeta)
|
|
class NsxLibBase(object):
|
|
def __init__(self, nsxlib_config):
|
|
|
|
self.nsx_version = None
|
|
self.nsx_api = None
|
|
self.set_config(nsxlib_config)
|
|
|
|
# create the Cluster
|
|
self.cluster = cluster.NSXClusteredAPI(self.nsxlib_config)
|
|
|
|
# create the Client
|
|
self.client = client.NSX3Client(
|
|
self.cluster,
|
|
nsx_api_managers=self.nsxlib_config.nsx_api_managers,
|
|
max_attempts=self.nsxlib_config.max_attempts,
|
|
url_path_base=self.client_url_prefix,
|
|
rate_limit_retry=self.nsxlib_config.rate_limit_retry)
|
|
|
|
self.general_apis = utils.NsxLibApiBase(
|
|
self.client, self.nsxlib_config)
|
|
|
|
self.init_api()
|
|
|
|
super(NsxLibBase, self).__init__()
|
|
|
|
def set_config(self, nsxlib_config):
|
|
"""Set config user provided and extend it according to application"""
|
|
self.nsxlib_config = nsxlib_config
|
|
self.nsxlib_config.extend(
|
|
keepalive_section=self.keepalive_section,
|
|
validate_connection_method=self.validate_connection_method,
|
|
url_base=self.client_url_prefix)
|
|
|
|
@abc.abstractproperty
|
|
def client_url_prefix(self):
|
|
pass
|
|
|
|
@abc.abstractproperty
|
|
def keepalive_section(self):
|
|
pass
|
|
|
|
@abc.abstractproperty
|
|
def validate_connection_method(self):
|
|
pass
|
|
|
|
@abc.abstractmethod
|
|
def init_api(self):
|
|
pass
|
|
|
|
@abc.abstractmethod
|
|
def feature_supported(self, feature):
|
|
pass
|
|
|
|
def build_v3_api_version_tag(self):
|
|
return self.general_apis.build_v3_api_version_tag()
|
|
|
|
def is_internal_resource(self, nsx_resource):
|
|
return self.general_apis.is_internal_resource(nsx_resource)
|
|
|
|
def build_v3_api_version_project_tag(self, project_name, project_id=None):
|
|
return self.general_apis.build_v3_api_version_project_tag(
|
|
project_name, project_id=project_id)
|
|
|
|
def build_v3_tags_payload(self, resource, resource_type, project_name):
|
|
return self.general_apis.build_v3_tags_payload(
|
|
resource, resource_type, project_name)
|
|
|
|
def reinitialize_cluster(self, resource, event, trigger, payload=None):
|
|
self.cluster._reinit_cluster()
|
|
|
|
def subscribe(self, callback, event):
|
|
self.cluster.subscribe(callback, event)
|
|
|
|
def _add_pagination_parameters(self, url, cursor, page_size):
|
|
if cursor:
|
|
url += "&cursor=%d" % cursor
|
|
if page_size:
|
|
url += "&page_size=%d" % page_size
|
|
return url
|
|
|
|
# TODO(abhiraut): Revisit this method to generate complex boolean
|
|
# queries to search resources.
|
|
def search_by_tags(self, tags, resource_type=None, cursor=None,
|
|
page_size=None):
|
|
"""Return the list of resources searched based on tags.
|
|
|
|
Currently the query only supports AND boolean operator.
|
|
:param tags: List of dictionaries containing tags. Each
|
|
NSX tag dictionary is of the form:
|
|
{'scope': <scope_key>, 'tag': <tag_value>}
|
|
:param resource_type: Optional string parameter to limit the
|
|
scope of the search to the given ResourceType.
|
|
:param cursor: Opaque cursor to be used for getting next page of
|
|
records (supplied by current result page).
|
|
:param page_size: Maximum number of results to return in this page.
|
|
"""
|
|
if not tags:
|
|
reason = _("Missing required argument 'tags'")
|
|
raise exceptions.NsxSearchInvalidQuery(reason=reason)
|
|
# Query will return nothing if the same scope is repeated.
|
|
query_tags = self._build_query(tags)
|
|
query = 'resource_type:%s' % resource_type if resource_type else None
|
|
if query:
|
|
query += " AND %s" % query_tags
|
|
else:
|
|
query = query_tags
|
|
url = self._add_pagination_parameters("search?query=%s" % query,
|
|
cursor, page_size)
|
|
|
|
# Retry the search on case of error
|
|
@utils.retry_upon_exception(exceptions.NsxSearchError,
|
|
max_attempts=self.client.max_attempts)
|
|
def do_search(url):
|
|
return self.client.url_get(url)
|
|
|
|
return do_search(url)
|
|
|
|
def search_resource_by_attributes(self, resource_type, cursor=None,
|
|
page_size=None, **attributes):
|
|
"""Search resources of a given type matching specific attributes.
|
|
|
|
It is optional to specify attributes. If multiple attributes are
|
|
specified they are ANDed together to form the search query.
|
|
|
|
:param resource_type: String parameter specifying the desired
|
|
resource_type
|
|
:param cursor: Opaque cursor to be used for getting next page of
|
|
records (supplied by current result page).
|
|
:param page_size: Maximum number of results to return in this page.
|
|
:param **attributes: an optional set of keyword arguments
|
|
specifying filters for the search query.
|
|
Wildcards will not be interpeted.
|
|
|
|
:returns: a list of resources of the requested type matching
|
|
specified filters.
|
|
"""
|
|
if not resource_type:
|
|
raise exceptions.NsxSearchInvalidQuery(
|
|
reason=_("Resource type was not specified"))
|
|
attributes_query = " AND ".join(['%s:%s' % (k, v) for (k, v)
|
|
in attributes.items()])
|
|
query = 'resource_type:%s' % resource_type + (
|
|
" AND %s" % attributes_query if attributes_query else "")
|
|
url = self._add_pagination_parameters("search?query=%s" % query,
|
|
cursor, page_size)
|
|
|
|
# Retry the search on case of error
|
|
@utils.retry_upon_exception(exceptions.NsxIndexingInProgress,
|
|
max_attempts=self.client.max_attempts)
|
|
def do_search(url):
|
|
return self.client.url_get(url)
|
|
|
|
return do_search(url)
|
|
|
|
def search_all_by_tags(self, tags, resource_type=None):
|
|
"""Return all the results searched based on tags."""
|
|
results = []
|
|
cursor = 0
|
|
while True:
|
|
response = self.search_by_tags(resource_type=resource_type,
|
|
tags=tags, cursor=cursor)
|
|
if not response['results']:
|
|
return results
|
|
results.extend(response['results'])
|
|
cursor = int(response['cursor'])
|
|
result_count = int(response['result_count'])
|
|
if cursor >= result_count:
|
|
return results
|
|
|
|
def search_all_resource_by_attributes(self, resource_type, **attributes):
|
|
"""Return all the results searched based on attributes."""
|
|
results = []
|
|
cursor = 0
|
|
while True:
|
|
response = self.search_resource_by_attributes(
|
|
resource_type=resource_type,
|
|
cursor=cursor, **attributes)
|
|
if not response['results']:
|
|
return results
|
|
results.extend(response['results'])
|
|
cursor = int(response['cursor'])
|
|
result_count = int(response['result_count'])
|
|
if cursor >= result_count:
|
|
return results
|
|
|
|
def get_id_by_resource_and_tag(self, resource_type, scope, tag,
|
|
alert_not_found=False,
|
|
alert_multiple=False):
|
|
"""Search a resource type by 1 scope&tag.
|
|
|
|
Return the id of the result only if it is single.
|
|
"""
|
|
query_tags = [{'scope': utils.escape_tag_data(scope),
|
|
'tag': utils.escape_tag_data(tag)}]
|
|
query_result = self.search_by_tags(
|
|
tags=query_tags, resource_type=resource_type)
|
|
if not query_result['result_count']:
|
|
if alert_not_found:
|
|
msg = _("No %(type)s found for tag '%(scope)s:%(tag)s'") % {
|
|
'type': resource_type,
|
|
'scope': scope,
|
|
'tag': tag}
|
|
LOG.warning(msg)
|
|
raise exceptions.ResourceNotFound(
|
|
manager=self.nsxlib_config.nsx_api_managers,
|
|
operation=msg)
|
|
elif query_result['result_count'] == 1:
|
|
return query_result['results'][0]['id']
|
|
else:
|
|
# multiple results
|
|
if alert_multiple:
|
|
msg = _("Multiple %(type)s found for tag '%(scope)s:"
|
|
"%(tag)s'") % {
|
|
'type': resource_type,
|
|
'scope': scope,
|
|
'tag': tag}
|
|
LOG.warning(msg)
|
|
raise exceptions.ManagerError(
|
|
manager=self.nsxlib_config.nsx_api_managers,
|
|
operation=msg,
|
|
details='')
|
|
|
|
def _build_tag_query(self, tag):
|
|
# Validate that the correct keys are used
|
|
if set(tag.keys()) - set(('scope', 'tag')):
|
|
reason = _("Only 'scope' and 'tag' keys are supported")
|
|
raise exceptions.NsxSearchInvalidQuery(reason=reason)
|
|
_scope = tag.get('scope')
|
|
_tag = tag.get('tag')
|
|
if _scope and _tag:
|
|
return 'tags.scope:%s AND tags.tag:%s' % (_scope, _tag)
|
|
elif _scope:
|
|
return 'tags.scope:%s' % _scope
|
|
else:
|
|
return 'tags.tag:%s' % _tag
|
|
|
|
def _build_query(self, tags):
|
|
return " AND ".join([self._build_tag_query(item) for item in tags])
|
|
|
|
def get_tag_limits(self):
|
|
try:
|
|
result = self.client.url_get('spec/vmware/types/Tag')
|
|
scope_length = result['properties']['scope']['maxLength']
|
|
tag_length = result['properties']['tag']['maxLength']
|
|
except Exception as e:
|
|
LOG.error("Unable to read tag limits. Reason: %s", e)
|
|
scope_length = utils.MAX_RESOURCE_TYPE_LEN
|
|
tag_length = utils.MAX_TAG_LEN
|
|
try:
|
|
result = self.client.url_get('spec/vmware/types/ManagedResource')
|
|
max_tags = result['properties']['tags']['maxItems']
|
|
except Exception as e:
|
|
LOG.error("Unable to read maximum tags. Reason: %s", e)
|
|
max_tags = utils.MAX_TAGS
|
|
return utils.TagLimits(scope_length, tag_length, max_tags)
|