From 253f8d54599560ce6c9b335aa19b343ef03c0687 Mon Sep 17 00:00:00 2001 From: Sijeesh Kattumunda Date: Wed, 8 Aug 2018 00:48:42 -0600 Subject: [PATCH 01/11] Restructuring the library classes to add more helper functions --- examples/fc_networks.py | 85 ++- hpOneView/exceptions.py | 21 + hpOneView/resources/networking/fc_networks.py | 131 +--- hpOneView/resources/resource.py | 648 +++++++++++++++++- 4 files changed, 719 insertions(+), 166 deletions(-) diff --git a/examples/fc_networks.py b/examples/fc_networks.py index 49394ce8..4f3145ea 100644 --- a/examples/fc_networks.py +++ b/examples/fc_networks.py @@ -23,14 +23,15 @@ from pprint import pprint from hpOneView.oneview_client import OneViewClient +from hpOneView.resources.networking.fc_networks import FcNetworks from hpOneView.exceptions import HPOneViewException from config_loader import try_load_from_file config = { - "ip": "", + "ip": "", "credentials": { - "userName": "", - "password": "" + "userName": "administrator", + "password": "ecosystem" } } @@ -42,9 +43,6 @@ "linkStabilityTime": 30, } -# FC Network ID to perform a get by ID -fc_network_id = "" - # Scope name to perform the patch operation scope_name = "" @@ -53,64 +51,75 @@ oneview_client = OneViewClient(config) +connection = oneview_client.connection + +fc_network = FcNetworks(connection, options) + +additional_params = {} +fc_network.create(additional_params) + +#Get the data of a fc network +fc_netwok_data = fc_network.get() + + # Create a FC Network -fc_network = oneview_client.fc_networks.create(options) +#fc_network = oneview_client.fc_networks.create(options) print("\nCreated fc-network '%s' successfully.\n uri = '%s'" % (fc_network['name'], fc_network['uri'])) # Find recently created network by name -fc_network = oneview_client.fc_networks.get_by('name', 'OneViewSDK Test FC Network')[0] +#fc_network = oneview_client.fc_networks.get_by('name', 'OneViewSDK Test FC Network')[0] print("\nFound fc-network by name: '%s'.\n uri = '%s'" % (fc_network['name'], fc_network['uri'])) # Update autoLoginRedistribution from recently created network -fc_network['autoLoginRedistribution'] = False -fc_network = oneview_client.fc_networks.update(fc_network) +#fc_network['autoLoginRedistribution'] = False +#fc_network = oneview_client.fc_networks.update(fc_network) print("\nUpdated fc-network '%s' successfully.\n uri = '%s'" % (fc_network['name'], fc_network['uri'])) print(" with attribute {'autoLoginRedistribution': %s}" % fc_network['autoLoginRedistribution']) # Get all, with defaults print("\nGet all fc-networks") -fc_nets = oneview_client.fc_networks.get_all() -pprint(fc_nets) +#fc_nets = oneview_client.fc_networks.get_all() +#pprint(fc_nets) # Filter by name print("\nGet all fc-networks filtering by name") -fc_nets_filtered = oneview_client.fc_networks.get_all(filter="\"'name'='OneViewSDK Test FC Network'\"") -pprint(fc_nets_filtered) +#fc_nets_filtered = oneview_client.fc_networks.get_all(filter="\"'name'='OneViewSDK Test FC Network'\"") +#pprint(fc_nets_filtered) # Get all sorting by name descending -print("\nGet all fc-networks sorting by name") -fc_nets_sorted = oneview_client.fc_networks.get_all(sort='name:descending') -pprint(fc_nets_sorted) +#print("\nGet all fc-networks sorting by name") +#fc_nets_sorted = oneview_client.fc_networks.get_all(sort='name:descending') +#pprint(fc_nets_sorted) # Get the first 10 records print("\nGet the first ten fc-networks") -fc_nets_limited = oneview_client.fc_networks.get_all(0, 10) -pprint(fc_nets_limited) +#fc_nets_limited = oneview_client.fc_networks.get_all(0, 10) +#pprint(fc_nets_limited) # Get by Id -if fc_network_id: - try: - print("\nGet a fc-network by id") - fc_nets_byid = oneview_client.fc_networks.get(fc_network_id) - pprint(fc_nets_byid) - except HPOneViewException as e: - print(e.msg) +#if fc_network_id: +# try: +# print("\nGet a fc-network by id") +# fc_nets_byid = oneview_client.fc_networks.get(fc_network_id) +# pprint(fc_nets_byid) +# except HPOneViewException as e: +# print(e.msg) # Get by Uri print("\nGet a fc-network by uri") -fc_nets_by_uri = oneview_client.fc_networks.get(fc_network['uri']) -pprint(fc_nets_by_uri) +#fc_nets_by_uri = oneview_client.fc_networks.get(fc_network['uri']) +#pprint(fc_nets_by_uri) # Adds ethernet to scope defined -if scope_name: - print("\nGet scope then add the network to it") - scope = oneview_client.scopes.get_by_name(scope_name) - fc_with_scope = oneview_client.fc_networks.patch(fc_network['uri'], - 'replace', - '/scopeUris', - [scope['uri']]) - pprint(fc_with_scope) +#if scope_name: +# print("\nGet scope then add the network to it") +# scope = oneview_client.scopes.get_by_name(scope_name) +# fc_with_scope = oneview_client.fc_networks.patch(fc_network['uri'], +# 'replace', +# '/scopeUris', +# [scope['uri']]) +# pprint(fc_with_scope) # Delete the created network -oneview_client.fc_networks.delete(fc_network) -print("\nSuccessfully deleted fc-network") +#oneview_client.fc_networks.delete(fc_network) +#print("\nSuccessfully deleted fc-network") diff --git a/hpOneView/exceptions.py b/hpOneView/exceptions.py index 46101fab..7191d2ec 100644 --- a/hpOneView/exceptions.py +++ b/hpOneView/exceptions.py @@ -149,3 +149,24 @@ class HPOneViewResourceNotFound(HPOneViewException): msg (str): Exception message. """ pass + +class HPOneViewUnavailableMethod(HPOneViewException): + """ + OneView Unavailable Method Exception. + The exception is raised when a method is not available for the resource class. + + Attributes: + msg (str): Exception message. + """ + pass + +class HPOneViewMissingUniqueIdentifiers(HPOneViewException): + """ + OneView Missing Unique Identifiers Exception. + The exception is raised when unique identifiers are missing for the resource + + Attributes: + msg (str): Exception message. + """ + pass + diff --git a/hpOneView/resources/networking/fc_networks.py b/hpOneView/resources/networking/fc_networks.py index e78f39a8..96a646b8 100644 --- a/hpOneView/resources/networking/fc_networks.py +++ b/hpOneView/resources/networking/fc_networks.py @@ -30,10 +30,10 @@ standard_library.install_aliases() -from hpOneView.resources.resource import ResourceClient +from hpOneView.resources.resource import Resource -class FcNetworks(object): +class FcNetworks(Resource): """ Fibre Channel networks API client. @@ -48,130 +48,7 @@ class FcNetworks(object): '600': {"type": "fc-networkV4"} } - def __init__(self, con): - self._connection = con - self._client = ResourceClient(con, self.URI) + def __init__(self, connection, options): + super(FcNetworks, self).__init__(connection, options) - def get_all(self, start=0, count=-1, filter='', sort=''): - """ - Gets a paginated collection of Fibre Channel networks. The collection is based on optional - sorting and filtering and is constrained by start and count parameters. - Args: - start: - The first item to return, using 0-based indexing. - If not specified, the default is 0 - start with the first available item. - count: - The number of resources to return. A count of -1 requests all items. - - The actual number of items in the response might differ from the requested - count if the sum of start and count exceeds the total number of items. - filter (list or str): - A general filter/query string to narrow the list of items returned. The - default is no filter; all resources are returned. - sort: - The sort order of the returned data set. By default, the sort order is based - on create time with the oldest entry first. - - Returns: - list: A list of Fibre Channel networks. - """ - return self._client.get_all(start, count, filter=filter, sort=sort) - - def delete(self, resource, force=False, timeout=-1): - """ - Deletes a Fibre Channel network. - Any deployed connections that are using the network are placed in the 'Failed' state. - - Args: - resource: dict object to delete - force: - If set to true, the operation completes despite any problems with - network connectivity or errors on the resource itself. The default is false. - timeout: - Timeout in seconds. Wait for task completion by default. The timeout does not abort the operation - in OneView; it just stops waiting for its completion. - - Returns: - bool: Indicates if the resource was successfully deleted. - - """ - return self._client.delete(resource, force=force, timeout=timeout) - - def get(self, id_or_uri): - """ - Gets the Fibre Channel network with the specified ID. - - Args: - id_or_uri: ID or URI of Fibre Channel network. - - Returns: - dict: The Fibre Channel network. - """ - return self._client.get(id_or_uri) - - def create(self, resource, timeout=-1): - """ - Creates a Fibre Channel network. - - Args: - resource (dict): Object to create. - timeout: - Timeout in seconds. Wait for task completion by default. The timeout does not abort the operation - in OneView, just stop waiting for its completion. - - Returns: - dict: Created resource. - - """ - return self._client.create(resource, timeout=timeout, default_values=self.DEFAULT_VALUES) - - def update(self, resource, timeout=-1): - """ - Updates a Fibre Channel network. - - Args: - resource (dict): Object to update. - timeout: - Timeout in seconds. Wait for task completion by default. The timeout does not abort the operation - in OneView, just stop waiting for its completion. - - Returns: - dict: Updated resource. - - """ - return self._client.update(resource, timeout=timeout, default_values=self.DEFAULT_VALUES) - - def get_by(self, field, value): - """ - Gets all Fibre Channel networks that match the filter. - - The search is case-insensitive. - - Args: - field: Field name to filter. - value: Value to filter. - - Returns: - list: A list of Fibre Channel networks. - """ - return self._client.get_by(field, value) - - def patch(self, id_or_uri, operation, path, value, timeout=-1): - """ - Uses the PATCH to update the given resource. - - Only one operation can be performed in each PATCH call. - - Args: - id_or_uri: Can be either the resource ID or the resource URI. - operation: Patch operation - path: Path - value: Value - timeout: Timeout in seconds. Wait for task completion by default. The timeout does not abort the operation - in OneView; it just stops waiting for its completion. - - Returns: - dict: Updated resource. - """ - return self._client.patch(id_or_uri, operation, path, value, timeout=timeout) diff --git a/hpOneView/resources/resource.py b/hpOneView/resources/resource.py index 1d0fd441..cce952c6 100755 --- a/hpOneView/resources/resource.py +++ b/hpOneView/resources/resource.py @@ -46,11 +46,26 @@ UNRECOGNIZED_URI = 'Unrecognized URI for this resource' RESOURCE_CLIENT_TASK_EXPECTED = "Failed: Expected a TaskResponse." RESOURCE_ID_OR_URI_REQUIRED = 'It is required to inform the Resource ID or URI.' - +UNAVAILABLE_METHOD = "Method is not available for this resource" +MISSING_UNIQUE_IDENTIFIERS = "Missing unique identifiers(URI/Name) for the resource" logger = logging.getLogger(__name__) +def download(self, uri, file_path): + """ + Downloads the contents of the requested URI to a stream. + + Args: + uri: URI + file_path: File path destination + + Returns: + bool: Indicates if the file was successfully downloaded. + """ + with open(file_path, 'wb') as file: + return self._connection.download_to_stream(file, uri) + def merge_resources(resource1, resource2): """ Updates a copy of resource1 with resource2 values and returns the merged dictionary. @@ -86,6 +101,637 @@ def merge_item(resource): return lmap(merge_item, resource_list) +def upload(self, file_path, uri=None, timeout=-1): + """ + Makes a multipart request. + + Args: + file_path: + File to upload. + uri: + A specific URI (optional). + timeout: + Timeout in seconds. Wait for task completion by default. The timeout does not abort the operation + in OneView; it just stops waiting for its completion. + + Returns: + dict: Response body. + """ + if not uri: + uri = self._uri + + upload_file_name = os.path.basename(file_path) + task, entity = self._connection.post_multipart_with_response_handling(uri, file_path, upload_file_name) + + if not task: + return entity + + return self._task_monitor.wait_for_task(task, timeout) + + +from functools import update_wrapper, partial + +class EnsureResourceClient(object): + + def __init__(self, func): + update_wrapper(self, func) + self.func = func + + def __get__(self, obj, objtype): + """Support instance methods.""" + return partial(self.__call__, obj) + + def __call__(self, obj, *args, **kwargs): + obj.data = {'testing': 'testing'} + return self.func(obj, *args, **kwargs) + +ensure_resource_client= EnsureResourceClient + + +class Resource(object): + """ + This class implements common functions for HpOneView API rest + """ + + URI = '/rest' + DEFAULT_VALUES = {} + + def __init__(self, connection, data): + + self._connection = connection + self.data = data + self._task_monitor = TaskMonitor(con) + +# def update_data(self, data): +# """ +# Update resource data +# """ +# self.data.update(data) + + @ensure_resource_client + def test_decorator(self): + print(self.data) + return True + + def unavailable_method(self): + """ + Raise exception if method is not available for the resource + """ + raise HPOneViewUnavailableMethod(UNAVAILABLE_METHOD) + + def get(self): + """ + Retrieve data from OneView and update data + """ + if not self.data['uri'] and not self.data['name']: + raise HPOneViewMissingUniqueIdentifiers(MISSING_UNIQUE_IDENTIFIERS) + + uri = self.data.get('uri') + + if uri: + self.data.update(self._connection.get(uri)) + + if not uri and self.data.get('name'): + self.data.update(self.get_by_name(self.data['name'])) + + return self.data + + def get_all(self, start=0, count=-1, filter='', query='', sort='', view='', fields='', scope_uris=''): + """ + Gets all items according with the given arguments. + + Args: + start: + The first item to return, using 0-based indexing. + If not specified, the default is 0 - start with the first available item. + count: + The number of resources to return. A count of -1 requests all items (default). + filter (list or str): + A general filter/query string to narrow the list of items returned. The default is no + filter; all resources are returned. + query: + A single query parameter can do what would take multiple parameters or multiple GET requests using + filter. Use query for more complex queries. NOTE: This parameter is experimental for OneView 2.0. + sort: + The sort order of the returned data set. By default, the sort order is based on create time with the + oldest entry first. + view: + Returns a specific subset of the attributes of the resource or collection by specifying the name of a + predefined view. The default view is expand (show all attributes of the resource and all elements of + the collections or resources). + fields: + Name of the fields. + uri: + A specific URI (optional) + scope_uris: + An expression to restrict the resources returned according to the scopes to + which they are assigned. + + Returns: + list: A list of items matching the specified filter. + """ + + uri = self.build_query_uri(start=start, + count=count, + filter=filter, + query=query, + sort=sort, + view=view, + fields=fields, + uri=self.URI, + scope_uris=scope_uris) + + logger.debug('Getting all resources with uri: {0}'.format(uri)) + + result = self.__do_requests_to_getall(uri, count) + + return result + + def create_with_zero_body(self, timeout=-1, custom_headers=None): + """ + Makes a POST request to create a resource when no request body is required. + + Args: + timeout: + Timeout in seconds. Wait for task completion by default. The timeout does not abort the operation + in OneView; it just stops waiting for its completion. + custom_headers: + Allows set specific HTTP headers. + + Returns: + Created resource. + """ + logger.debug('Create with zero body (uri = %s)' % self.URI) + + return self.__do_post(self.URI, {}, timeout, custom_headers) + + def create(self, options, timeout=-1, custom_headers=None): + """ + Makes a POST request to create a resource when a request body is required. + + Args: + resource: + OneView resource dictionary. + timeout: + Timeout in seconds. Wait for task completion by default. The timeout does not abort the operation + in OneView; it just stops waiting for its completion. + custom_headers: + Allows set specific HTTP headers. + Returns: + Created resource. + """ + logger.debug('Create (uri = %s, resource = %s)' % + (self.URI, str(options))) + + resource = self.merge_default_values(options, self.DEFAULT_VALUES) + + return self.__do_post(uri, resource, timeout, custom_headers) + + + def delete_all(self, filter, force=False, timeout=-1): + """ + Deletes all resources from the appliance that match the provided filter. + + Args: + filter: + A general filter/query string to narrow the list of items deleted. + force: + If set to true, the operation completes despite any problems with network connectivity or errors + on the resource itself. The default is false. + timeout: + Timeout in seconds. Wait for task completion by default. The timeout does not abort the operation + in OneView; it just stops waiting for its completion. + + Returns: + bool: Indicates if the resources were successfully deleted. + """ + uri = "{}?filter={}&force={}".format(self.URI, quote(filter), force) + logger.debug("Delete all resources (uri = %s)" % uri) + + task, body = self._connection.delete(uri) + + if not task: + # 204 NO CONTENT + # Successful return from a synchronous delete operation. + return True + + return self._task_monitor.wait_for_task(task, timeout=timeout) + + def delete(self, force=False, timeout=-1, custom_headers=None): + + if self.data and 'uri' in self.data and self.data['uri']: + uri = self.data['uri'] + else: + logger.exception(RESOURCE_CLIENT_RESOURCE_WAS_NOT_PROVIDED) + raise ValueError(RESOURCE_CLIENT_RESOURCE_WAS_NOT_PROVIDED) + + if force: + uri += '?force=True' + + logger.debug("Delete resource (uri = %s)" %(str(uri))) + + task, body = self._connection.delete(uri, custom_headers=custom_headers) + + if not task: + # 204 NO CONTENT + # Successful return from a synchronous delete operation. + return True + + task = self._task_monitor.wait_for_task(task, timeout=timeout) + + return task + + def get_schema(self): + logger.debug('Get schema (uri = %s, resource = %s)' % + (self.URI, self.URI)) + return self._connection.get(self.URI + '/schema') + + def get_collection(self, id_or_uri, filter=''): + """ + Retrieves a collection of resources. + + Use this function when the 'start' and 'count' parameters are not allowed in the GET call. + Otherwise, use get_all instead. + + Optional filtering criteria may be specified. + + Args: + id_or_uri: Can be either the resource ID or the resource URI. + filter (list or str): General filter/query string. + + Returns: + Collection of the requested resource. + """ + if filter: + filter = self.__make_query_filter(filter) + filter = "?" + filter[1:] + + uri = "{uri}{filter}".format(uri=self.build_uri(id_or_uri), filter=filter) + logger.debug('Get resource collection (uri = %s)' % uri) + response = self._connection.get(uri) + return self.__get_members(response) + + def update_with_zero_body(self, timeout=-1, custom_headers=None): + """ + Makes a PUT request to update a resource when no request body is required. + + Args: + uri: + Can be either the resource ID or the resource URI. + timeout: + Timeout in seconds. Wait for task completion by default. The timeout does not abort the operation + in OneView; it just stops waiting for its completion. + custom_headers: + Allows set specific HTTP headers. + + Returns: + Updated resource. + """ + logger.debug('Update with zero length body (uri = %s)' % self.data['uri']) + + return self.__do_put(self.data['uri'], None, timeout, custom_headers) + + def update(self, options, force=False, timeout=-1, custom_headers=None): + """ + Makes a PUT request to update a resource when a request body is required. + + Args: + resource: + OneView resource dictionary. + uri: + Can be either the resource ID or the resource URI. + force: + If set to true, the operation completes despite any problems with network connectivity or errors + on the resource itself. The default is false. + timeout: + Timeout in seconds. Wait for task completion by default. The timeout does not abort the operation + in OneView; it just stops waiting for its completion. + custom_headers: + Allows set specific HTTP headers. + + Returns: + Updated resource. + """ + logger.debug('Update async (uri = %s, resource = %s)' % + (self.URI, str(options))) + + uri = self.data['uri'] + + if force: + uri += '?force=True' + + resource = self.merge_default_values(resource, self.DEFAULT_VALUES) + + return self.__do_put(uri, resource, timeout, custom_headers) + + + def patch(self, operation, path, value, timeout=-1, custom_headers=None): + """ + Uses the PATCH to update a resource. + + Only one operation can be performed in each PATCH call. + + Args + operation: Patch operation + path: Path + value: Value + timeout: Timeout in seconds. Wait for task completion by default. The timeout does not abort the operation + in OneView; it just stops waiting for its completion. + + Returns: + Updated resource. + """ + patch_request_body = [{'op': operation, 'path': path, 'value': value}] + + return self._patch_request(body=patch_request_body, + timeout=timeout, + custom_headers=custom_headers) + + def _patch_request(self, body, timeout=-1, custom_headers=None): + """ + Uses the PATCH to update a resource. + + Only one operation can be performed in each PATCH call. + + Args: + body: Patch request body + timeout: Timeout in seconds. Wait for task completion by default. The timeout does not abort the operation + in OneView; it just stops waiting for its completion. + + Returns: + Updated resource. + """ + uri = self.data['uri'] + logger.debug('Patch resource (uri = %s, data = %s)' % (uri, body)) + + custom_headers_copy = custom_headers.copy() if custom_headers else {} + if self._connection._apiVersion >= 300 and 'Content-Type' not in custom_headers_copy: + custom_headers_copy['Content-Type'] = 'application/json-patch+json' + + task, entity = self._connection.patch(uri, body, custom_headers=custom_headers_copy) + + if not task: + return entity + + return self._task_monitor.wait_for_task(task, timeout) + + def get_by(self, field, value): + """ + This function uses get_all passing a filter. + + The search is case-insensitive. + + Args: + field: Field name to filter. + value: Value to filter. + Returns: + dict + """ + if not field: + logger.exception(RESOURCE_CLIENT_INVALID_FIELD) + raise ValueError(RESOURCE_CLIENT_INVALID_FIELD) + + filter = "\"{0}='{1}'\"".format(field, value) + results = self.get_all(filter=filter) + + # Workaround when the OneView filter does not work, it will filter again + if "." not in field: + # This filter only work for the first level + results = [item for item in results if str(item.get(field, '')).lower() == value.lower()] + + return results + + def get_by_name(self, name): + """ + Retrieve a resource by its name. + + Args: + name: Resource name. + + Returns: + dict + """ + result = self.get_by('name', name) + if not result: + return None + else: + return result[0] + + def get_utilization(self, fields=None, filter=None, refresh=False, view=None): + """ + Retrieves historical utilization data for the specified resource, metrics, and time span. + + Args: + id_or_uri: + Resource identification + fields: + Name of the supported metric(s) to be retrieved in the format METRIC[,METRIC]... + If unspecified, all metrics supported are returned. + + filter (list or str): + Filters should be in the format FILTER_NAME=VALUE[,FILTER_NAME=VALUE]... + E.g.: 'startDate=2016-05-30T11:20:44.541Z,endDate=2016-05-30T19:20:44.541Z' + + startDate + Start date of requested starting time range in ISO 8601 format. If omitted, the startDate is + determined by the endDate minus 24 hours. + endDate + End date of requested starting time range in ISO 8601 format. When omitted, the endDate includes + the latest data sample available. + + If an excessive number of samples would otherwise be returned, the results will be segmented. The + caller is responsible for comparing the returned sliceStartTime with the requested startTime in the + response. If the sliceStartTime is greater than the oldestSampleTime and the requested start time, + the caller is responsible for repeating the request with endTime set to sliceStartTime to obtain the + next segment. This process is repeated until the full data set is retrieved. + + If the resource has no data, the UtilizationData is still returned but will contain no samples and + sliceStartTime/sliceEndTime will be equal. oldestSampleTime/newestSampleTime will still be set + appropriately (null if no data is available). If the filter does not happen to overlap the data + that a resource has, then the metric history service will return null sample values for any + missing samples. + + refresh: + Specifies that if necessary, an additional request will be queued to obtain the most recent + utilization data from the iLO. The response will not include any refreshed data. To track the + availability of the newly collected data, monitor the TaskResource identified by the refreshTaskUri + property in the response. If null, no refresh was queued. + + view: + Specifies the resolution interval length of the samples to be retrieved. This is reflected in the + resolution in the returned response. Utilization data is automatically purged to stay within storage + space constraints. Supported views are listed below: + + native + Resolution of the samples returned will be one sample for each 5-minute time period. This is the + default view and matches the resolution of the data returned by the iLO. Samples at this resolution + are retained up to one year. + hour + Resolution of the samples returned will be one sample for each 60-minute time period. Samples are + calculated by averaging the available 5-minute data samples that occurred within the hour, except + for PeakPower which is calculated by reporting the peak observed 5-minute sample value data during + the hour. Samples at this resolution are retained up to three years. + day + Resolution of the samples returned will be one sample for each 24-hour time period. One day is a + 24-hour period that starts at midnight GMT regardless of the time zone in which the appliance or + client is located. Samples are calculated by averaging the available 5-minute data samples that + occurred during the day, except for PeakPower which is calculated by reporting the peak observed + 5-minute sample value data during the day. Samples at this resolution are retained up to three + years. + + Returns: + dict + """ + + uri = self.data['uri'] + query = '' + + if filter: + query += self.__make_query_filter(filter) + + if fields: + query += "&fields=" + quote(fields) + + if refresh: + query += "&refresh=true" + + if view: + query += "&view=" + quote(view) + + if query: + query = "?" + query[1:] + + uri = "{0}/utilization{1}".format(self.build_uri(uri), query) + + return self._connection.get(uri) + + def create_report(self, timeout=-1): + """ + Creates a report and returns the output. + + Args: + uri: URI + timeout: + Timeout in seconds. Wait for task completion by default. The timeout does not abort the operation + in OneView; it just stops waiting for its completion. + Returns: + list: + """ + uri = self.data['uri'] + logger.debug('Creating Report (uri = %s)'.format(uri)) + + task, _ = self._connection.post(uri, {}) + + if not task: + raise HPOneViewException(RESOURCE_CLIENT_TASK_EXPECTED) + + task = self._task_monitor.get_completed_task(task, timeout) + + return task['taskOutput'] + + def build_uri(self, id_or_uri): + if not id_or_uri: + logger.exception(RESOURCE_CLIENT_INVALID_ID) + raise ValueError(RESOURCE_CLIENT_INVALID_ID) + + if "/" in id_or_uri: + self.__validate_resource_uri(id_or_uri) + return id_or_uri + else: + return self.URI + "/" + id_or_uri + + def build_subresource_uri(self, resource_id_or_uri=None, subresource_id_or_uri=None, subresource_path=''): + if subresource_id_or_uri and "/" in subresource_id_or_uri: + return subresource_id_or_uri + else: + if not resource_id_or_uri: + raise HPOneViewValueError(RESOURCE_ID_OR_URI_REQUIRED) + + resource_uri = self.build_uri(resource_id_or_uri) + + uri = "{}/{}/{}".format(resource_uri, subresource_path, str(subresource_id_or_uri or '')) + uri = uri.replace("//", "/") + + if uri.endswith("/"): + uri = uri[:-1] + + return uri + + + def __validate_resource_uri(self, path): + if self._uri not in path: + logger.exception('Get by uri : unrecognized uri: (%s)' % path) + raise HPOneViewUnknownType(UNRECOGNIZED_URI) + + def __make_query_filter(self, filters): + if isinstance(filters, list): + formated_filter = "&filter=".join(quote(f) for f in filters) + else: + formated_filter = quote(filters) + + return "&filter=" + formated_filter + + def __get_members(self, mlist): + if mlist and 'members' in mlist and mlist['members']: + return mlist['members'] + else: + return [] + + def __do_post(self, uri, resource, timeout, custom_headers): + task, entity = self._connection.post(uri, resource, custom_headers=custom_headers) + + if not task: + return entity + + return self._task_monitor.wait_for_task(task, timeout) + + def __do_put(self, uri, resource, timeout, custom_headers): + task, body = self._connection.put(uri, resource, custom_headers=custom_headers) + + if not task: + return body + + return self._task_monitor.wait_for_task(task, timeout) + + def __do_requests_to_getall(self, uri, requested_count): + items = [] + + while uri: + logger.debug('Making HTTP request to get all resources. Uri: {0}'.format(uri)) + response = self._connection.get(uri) + members = self.__get_members(response) + items += members + + logger.debug("Response getAll: nextPageUri = {0}, members list length: {1}".format(uri, str(len(members)))) + uri = self.__get_next_page(response, items, requested_count) + + logger.debug('Total # of members found = {0}'.format(str(len(items)))) + return items + + def __get_next_page(self, response, items, requested_count): + next_page_is_empty = response.get('nextPageUri') is None + has_different_next_page = not response.get('uri') == response.get('nextPageUri') + has_next_page = not next_page_is_empty and has_different_next_page + + if len(items) >= requested_count and requested_count != -1: + return None + + return response.get('nextPageUri') if has_next_page else None + + def merge_default_values(self, resource, default_values): + if not default_values: + return resource + + merged_resource = None + + if not isinstance(resource, list): + api_version = str(self._connection._apiVersion) + data = default_values.get(api_version, {}).copy() + merged_resource = merge_resources(data, resource) + + return merged_resource or resource + + class ResourceClient(object): """ This class implements common functions for HpOneView API rest From 059ae1d14b197563f1b8c2a4244cb9d6b6e8b6b2 Mon Sep 17 00:00:00 2001 From: sijeesh Date: Wed, 8 Aug 2018 19:35:32 +0530 Subject: [PATCH 02/11] Updated fc network resource class and the examples --- examples/fc_networks.py | 95 ++++++++-------- hpOneView/resources/resource.py | 191 +++++++++++++++++++++++--------- 2 files changed, 181 insertions(+), 105 deletions(-) diff --git a/examples/fc_networks.py b/examples/fc_networks.py index 4f3145ea..5f105710 100644 --- a/examples/fc_networks.py +++ b/examples/fc_networks.py @@ -28,7 +28,7 @@ from config_loader import try_load_from_file config = { - "ip": "", + "ip": "10.30.5.228", "credentials": { "userName": "administrator", "password": "ecosystem" @@ -36,7 +36,7 @@ } options = { - "name": "OneViewSDK Test FC Network", + "name": "fc_test", "connectionTemplateUri": None, "autoLoginRedistribution": True, "fabricType": "FabricAttach", @@ -44,7 +44,7 @@ } # Scope name to perform the patch operation -scope_name = "" +scope_name = "sample" # Try load config from a file (if there is a config file) config = try_load_from_file(config) @@ -53,73 +53,68 @@ connection = oneview_client.connection +#Create FcNetWorks object fc_network = FcNetworks(connection, options) -additional_params = {} -fc_network.create(additional_params) +#Create a FcNetWork with the options provided +try: + fc_network.create() +except HPOneViewException, e: + print(e[0]) -#Get the data of a fc network -fc_netwok_data = fc_network.get() - - -# Create a FC Network -#fc_network = oneview_client.fc_networks.create(options) -print("\nCreated fc-network '%s' successfully.\n uri = '%s'" % (fc_network['name'], fc_network['uri'])) +#Get data of the created fc network +data = fc_network.get() +print(data) # Find recently created network by name -#fc_network = oneview_client.fc_networks.get_by('name', 'OneViewSDK Test FC Network')[0] -print("\nFound fc-network by name: '%s'.\n uri = '%s'" % (fc_network['name'], fc_network['uri'])) +data = fc_network.get_by_name(options['name']) +print("\nFound fc-network by name: '%s'.\n uri = '%s'" % (data['name'], data['uri'])) # Update autoLoginRedistribution from recently created network -#fc_network['autoLoginRedistribution'] = False -#fc_network = oneview_client.fc_networks.update(fc_network) -print("\nUpdated fc-network '%s' successfully.\n uri = '%s'" % (fc_network['name'], fc_network['uri'])) -print(" with attribute {'autoLoginRedistribution': %s}" % fc_network['autoLoginRedistribution']) +data_to_update = {'autoLoginRedistribution': False, + 'name':'Updated FC'} +resource = fc_network.update(data=data_to_update) +print("\nUpdated fc-network '%s' successfully.\n uri = '%s'" % (resource['name'], resource['uri'])) +print(" with attribute {'autoLoginRedistribution': %s}" % resource['autoLoginRedistribution']) # Get all, with defaults print("\nGet all fc-networks") -#fc_nets = oneview_client.fc_networks.get_all() -#pprint(fc_nets) +fc_nets = fc_network.get_all() +pprint(fc_nets) # Filter by name print("\nGet all fc-networks filtering by name") -#fc_nets_filtered = oneview_client.fc_networks.get_all(filter="\"'name'='OneViewSDK Test FC Network'\"") -#pprint(fc_nets_filtered) +fc_nets_filtered = fc_network.get_all(filter="\"'name'='Updated FC'\"") +pprint(fc_nets_filtered) # Get all sorting by name descending -#print("\nGet all fc-networks sorting by name") -#fc_nets_sorted = oneview_client.fc_networks.get_all(sort='name:descending') -#pprint(fc_nets_sorted) +print("\nGet all fc-networks sorting by name") +fc_nets_sorted = fc_network.get_all(sort='name:descending') +pprint(fc_nets_sorted) # Get the first 10 records print("\nGet the first ten fc-networks") -#fc_nets_limited = oneview_client.fc_networks.get_all(0, 10) -#pprint(fc_nets_limited) - -# Get by Id -#if fc_network_id: -# try: -# print("\nGet a fc-network by id") -# fc_nets_byid = oneview_client.fc_networks.get(fc_network_id) -# pprint(fc_nets_byid) -# except HPOneViewException as e: -# print(e.msg) - -# Get by Uri +fc_nets_limited = fc_network.get_all(0, 10) +pprint(fc_nets_limited) + +# Get by uri print("\nGet a fc-network by uri") -#fc_nets_by_uri = oneview_client.fc_networks.get(fc_network['uri']) -#pprint(fc_nets_by_uri) +fc_nets_by_uri = fc_network.get_by_uri(resource['uri']) +pprint(fc_nets_by_uri) # Adds ethernet to scope defined -#if scope_name: -# print("\nGet scope then add the network to it") -# scope = oneview_client.scopes.get_by_name(scope_name) -# fc_with_scope = oneview_client.fc_networks.patch(fc_network['uri'], -# 'replace', -# '/scopeUris', -# [scope['uri']]) -# pprint(fc_with_scope) +if scope_name: + print("\nGet scope then add the network to it") + scope = oneview_client.scopes.get_by_name(scope_name) # TODO: This has to updated + try: + fc_with_scope = fc_network.patch(resource['uri'], + 'replace', + '/scopeUris', + [scope['uri']]) + pprint(fc_with_scope) + except HPOneViewException, e: + print(e) # Delete the created network -#oneview_client.fc_networks.delete(fc_network) -#print("\nSuccessfully deleted fc-network") +fc_network.delete() +print("\nSuccessfully deleted fc-network") diff --git a/hpOneView/resources/resource.py b/hpOneView/resources/resource.py index cce952c6..f136b3e3 100755 --- a/hpOneView/resources/resource.py +++ b/hpOneView/resources/resource.py @@ -33,8 +33,9 @@ import logging import os - +from copy import deepcopy from urllib.parse import quote + from hpOneView.resources.task_monitor import TaskMonitor from hpOneView.exceptions import HPOneViewUnknownType, HPOneViewException from hpOneView.exceptions import HPOneViewValueError @@ -51,7 +52,6 @@ logger = logging.getLogger(__name__) - def download(self, uri, file_path): """ Downloads the contents of the requested URI to a stream. @@ -131,22 +131,21 @@ def upload(self, file_path, uri=None, timeout=-1): from functools import update_wrapper, partial -class EnsureResourceClient(object): +class EnsureResourceData(object): def __init__(self, func): update_wrapper(self, func) self.func = func def __get__(self, obj, objtype): - """Support instance methods.""" return partial(self.__call__, obj) def __call__(self, obj, *args, **kwargs): - obj.data = {'testing': 'testing'} + obj.get() return self.func(obj, *args, **kwargs) -ensure_resource_client= EnsureResourceClient - +#Decorator to ensure the resource data +ensure_resource_data= EnsureResourceData class Resource(object): """ @@ -160,39 +159,98 @@ def __init__(self, connection, data): self._connection = connection self.data = data - self._task_monitor = TaskMonitor(con) + self._merge_default_values() + self._task_monitor = TaskMonitor(connection) -# def update_data(self, data): -# """ -# Update resource data -# """ -# self.data.update(data) + def build_query_uri(self, start=0, count=-1, filter='', query='', sort='', view='', fields='', uri=None, scope_uris=''): + """ + Builds the URI given the parameters. - @ensure_resource_client - def test_decorator(self): - print(self.data) - return True + More than one request can be send to get the items, regardless the query parameter 'count', because the actual + number of items in the response might differ from the requested count. Some types of resource have a limited + number of items returned on each call. For those resources, additional calls are made to the API to retrieve + any other items matching the given filter. The actual number of items can also differ from the requested call + if the requested number of items would take too long. - def unavailable_method(self): - """ - Raise exception if method is not available for the resource + The use of optional parameters for OneView 2.0 is described at: + http://h17007.www1.hpe.com/docs/enterprise/servers/oneview2.0/cic-api/en/api-docs/current/index.html + + Note: + Single quote - "'" - inside a query parameter is not supported by OneView API. + + Args: + start: + The first item to return, using 0-based indexing. + If not specified, the default is 0 - start with the first available item. + count: + The number of resources to return. A count of -1 requests all items (default). + filter (list or str): + A general filter/query string to narrow the list of items returned. The default is no + filter; all resources are returned. + query: + A single query parameter can do what would take multiple parameters or multiple GET requests using + filter. Use query for more complex queries. NOTE: This parameter is experimental for OneView 2.0. + sort: + The sort order of the returned data set. By default, the sort order is based on create time with the + oldest entry first. + view: + Returns a specific subset of the attributes of the resource or collection by specifying the name of a + predefined view. The default view is expand (show all attributes of the resource and all elements of + the collections or resources). + fields: + Name of the fields. + uri: + A specific URI (optional) + scope_uris: + An expression to restrict the resources returned according to the scopes to + which they are assigned. + + Returns: + uri: The complete uri """ - raise HPOneViewUnavailableMethod(UNAVAILABLE_METHOD) + + if filter: + filter = self.__make_query_filter(filter) + + if query: + query = "&query=" + quote(query) + + if sort: + sort = "&sort=" + quote(sort) + + if view: + view = "&view=" + quote(view) + + if fields: + fields = "&fields=" + quote(fields) + + if scope_uris: + scope_uris = "&scopeUris=" + quote(scope_uris) + + path = uri if uri else self.URI + self.__validate_resource_uri(path) + + symbol = '?' if '?' not in path else '&' + + uri = "{0}{1}start={2}&count={3}{4}{5}{6}{7}{8}{9}".format(path, symbol, start, count, filter, query, sort, + view, fields, scope_uris) + return uri def get(self): """ Retrieve data from OneView and update data """ - if not self.data['uri'] and not self.data['name']: - raise HPOneViewMissingUniqueIdentifiers(MISSING_UNIQUE_IDENTIFIERS) - uri = self.data.get('uri') + name = self.data.get('name') + if not uri and not name: + raise HPOneViewMissingUniqueIdentifiers(MISSING_UNIQUE_IDENTIFIERS) + if uri: self.data.update(self._connection.get(uri)) - if not uri and self.data.get('name'): - self.data.update(self.get_by_name(self.data['name'])) + if name: + self.data.update(self.get_by_name(name)) return self.data @@ -265,13 +323,13 @@ def create_with_zero_body(self, timeout=-1, custom_headers=None): return self.__do_post(self.URI, {}, timeout, custom_headers) - def create(self, options, timeout=-1, custom_headers=None): + def create(self, data=None, timeout=-1, custom_headers=None): """ Makes a POST request to create a resource when a request body is required. Args: - resource: - OneView resource dictionary. + data: + Additional fields can be passed to create the resource. timeout: Timeout in seconds. Wait for task completion by default. The timeout does not abort the operation in OneView; it just stops waiting for its completion. @@ -281,12 +339,15 @@ def create(self, options, timeout=-1, custom_headers=None): Created resource. """ logger.debug('Create (uri = %s, resource = %s)' % - (self.URI, str(options))) + (self.URI, str(data))) - resource = self.merge_default_values(options, self.DEFAULT_VALUES) - - return self.__do_post(uri, resource, timeout, custom_headers) + resource = deepcopy(self.data) + if data: + resource.update(self.data) + + self.data = self.__do_post(self.URI, resource, timeout, custom_headers) + return self.data def delete_all(self, filter, force=False, timeout=-1): """ @@ -317,6 +378,7 @@ def delete_all(self, filter, force=False, timeout=-1): return self._task_monitor.wait_for_task(task, timeout=timeout) + @ensure_resource_data def delete(self, force=False, timeout=-1, custom_headers=None): if self.data and 'uri' in self.data and self.data['uri']: @@ -371,6 +433,7 @@ def get_collection(self, id_or_uri, filter=''): response = self._connection.get(uri) return self.__get_members(response) + @ensure_resource_data def update_with_zero_body(self, timeout=-1, custom_headers=None): """ Makes a PUT request to update a resource when no request body is required. @@ -391,15 +454,14 @@ def update_with_zero_body(self, timeout=-1, custom_headers=None): return self.__do_put(self.data['uri'], None, timeout, custom_headers) - def update(self, options, force=False, timeout=-1, custom_headers=None): + @ensure_resource_data + def update(self, data=None, force=False, timeout=-1, custom_headers=None): """ Makes a PUT request to update a resource when a request body is required. Args: - resource: - OneView resource dictionary. - uri: - Can be either the resource ID or the resource URI. + data: + Data to update the resource. force: If set to true, the operation completes despite any problems with network connectivity or errors on the resource itself. The default is false. @@ -413,18 +475,20 @@ def update(self, options, force=False, timeout=-1, custom_headers=None): Updated resource. """ logger.debug('Update async (uri = %s, resource = %s)' % - (self.URI, str(options))) + (self.URI, str(data))) uri = self.data['uri'] + resource = deepcopy(self.data) if force: uri += '?force=True' - resource = self.merge_default_values(resource, self.DEFAULT_VALUES) - - return self.__do_put(uri, resource, timeout, custom_headers) - - + if data: + resource.update(data) + self.data = self.__do_put(uri, resource, timeout, custom_headers) + + return self.data + def patch(self, operation, path, value, timeout=-1, custom_headers=None): """ Uses the PATCH to update a resource. @@ -447,6 +511,7 @@ def patch(self, operation, path, value, timeout=-1, custom_headers=None): timeout=timeout, custom_headers=custom_headers) + @ensure_resource_data def _patch_request(self, body, timeout=-1, custom_headers=None): """ Uses the PATCH to update a resource. @@ -517,6 +582,14 @@ def get_by_name(self, name): else: return result[0] + def get_by_uri(self, uri): + """ + Retrieve a resource by its id + """ + self. __validate_resource_uri(uri) + return self._connection.get(uri) + + @ensure_resource_data def get_utilization(self, fields=None, filter=None, refresh=False, view=None): """ Retrieves historical utilization data for the specified resource, metrics, and time span. @@ -605,6 +678,7 @@ def get_utilization(self, fields=None, filter=None, refresh=False, view=None): return self._connection.get(uri) + @ensure_resource_data def create_report(self, timeout=-1): """ Creates a report and returns the output. @@ -657,9 +731,14 @@ def build_subresource_uri(self, resource_id_or_uri=None, subresource_id_or_uri=N return uri + def unavailable_method(self): + """ + Raise exception if method is not available for the resource + """ + raise HPOneViewUnavailableMethod(UNAVAILABLE_METHOD) def __validate_resource_uri(self, path): - if self._uri not in path: + if self.URI not in path: logger.exception('Get by uri : unrecognized uri: (%s)' % path) raise HPOneViewUnknownType(UNRECOGNIZED_URI) @@ -718,18 +797,20 @@ def __get_next_page(self, response, items, requested_count): return response.get('nextPageUri') if has_next_page else None - def merge_default_values(self, resource, default_values): - if not default_values: - return resource - - merged_resource = None - - if not isinstance(resource, list): + def _merge_default_values(self): + """ + Pick the default values for the api version and append that with resource data + """ + if self.DEFAULT_VALUES: api_version = str(self._connection._apiVersion) - data = default_values.get(api_version, {}).copy() - merged_resource = merge_resources(data, resource) + values = self.DEFAULT_VALUES.get(api_version, {}).copy() + self.data.update(values) + + def __validate_resource_uri(self, path): + if self.URI not in path: + logger.exception('Get by uri : unrecognized uri: (%s)' % path) + raise HPOneViewUnknownType(UNRECOGNIZED_URI) - return merged_resource or resource class ResourceClient(object): From 8a806d9ab5860159a814aedbc3b1fe1ebe5c8c9e Mon Sep 17 00:00:00 2001 From: sijeesh Date: Fri, 10 Aug 2018 15:06:10 +0530 Subject: [PATCH 03/11] Updated enclosure --- hpOneView/resources/resource.py | 239 ++++++++++---------- hpOneView/resources/servers/enclosures.py | 256 ++++------------------ 2 files changed, 162 insertions(+), 333 deletions(-) diff --git a/hpOneView/resources/resource.py b/hpOneView/resources/resource.py index f136b3e3..8483b507 100755 --- a/hpOneView/resources/resource.py +++ b/hpOneView/resources/resource.py @@ -35,6 +35,7 @@ import os from copy import deepcopy from urllib.parse import quote +from functools import update_wrapper, partial from hpOneView.resources.task_monitor import TaskMonitor from hpOneView.exceptions import HPOneViewUnknownType, HPOneViewException @@ -52,86 +53,8 @@ logger = logging.getLogger(__name__) -def download(self, uri, file_path): - """ - Downloads the contents of the requested URI to a stream. - - Args: - uri: URI - file_path: File path destination - Returns: - bool: Indicates if the file was successfully downloaded. - """ - with open(file_path, 'wb') as file: - return self._connection.download_to_stream(file, uri) - -def merge_resources(resource1, resource2): - """ - Updates a copy of resource1 with resource2 values and returns the merged dictionary. - - Args: - resource1: original resource - resource2: resource to update resource1 - - Returns: - dict: merged resource - """ - merged = resource1.copy() - merged.update(resource2) - return merged - - -def merge_default_values(resource_list, default_values): - """ - Generate a new list where each item of original resource_list will be merged with the default_values. - - Args: - resource_list: list with items to be merged - default_values: properties to be merged with each item list. If the item already contains some property - the original value will be maintained. - - Returns: - list: list containing each item merged with default_values - """ - - def merge_item(resource): - return merge_resources(default_values, resource) - - return lmap(merge_item, resource_list) - - -def upload(self, file_path, uri=None, timeout=-1): - """ - Makes a multipart request. - - Args: - file_path: - File to upload. - uri: - A specific URI (optional). - timeout: - Timeout in seconds. Wait for task completion by default. The timeout does not abort the operation - in OneView; it just stops waiting for its completion. - - Returns: - dict: Response body. - """ - if not uri: - uri = self._uri - - upload_file_name = os.path.basename(file_path) - task, entity = self._connection.post_multipart_with_response_handling(uri, file_path, upload_file_name) - - if not task: - return entity - - return self._task_monitor.wait_for_task(task, timeout) - - -from functools import update_wrapper, partial - -class EnsureResourceData(object): +class EnsureResourceClient(object): def __init__(self, func): update_wrapper(self, func) @@ -141,11 +64,11 @@ def __get__(self, obj, objtype): return partial(self.__call__, obj) def __call__(self, obj, *args, **kwargs): - obj.get() + obj.load_resource() return self.func(obj, *args, **kwargs) -#Decorator to ensure the resource data -ensure_resource_data= EnsureResourceData +#Decorator to ensure the resource client +ensure_resource_client= EnsureResourceClient class Resource(object): """ @@ -236,9 +159,9 @@ def build_query_uri(self, start=0, count=-1, filter='', query='', sort='', view= view, fields, scope_uris) return uri - def get(self): + def load_resource(self): """ - Retrieve data from OneView and update data + Retrieve data from OneView and update resource data """ uri = self.data.get('uri') name = self.data.get('name') @@ -247,13 +170,20 @@ def get(self): raise HPOneViewMissingUniqueIdentifiers(MISSING_UNIQUE_IDENTIFIERS) if uri: - self.data.update(self._connection.get(uri)) + self.data.update(self.do_get(uri)) if name: self.data.update(self.get_by_name(name)) return self.data + @ensure_resource_client + def get(self): + """ + Return resource data + """ + return self.data + def get_all(self, start=0, count=-1, filter='', query='', sort='', view='', fields='', scope_uris=''): """ Gets all items according with the given arguments. @@ -321,7 +251,7 @@ def create_with_zero_body(self, timeout=-1, custom_headers=None): """ logger.debug('Create with zero body (uri = %s)' % self.URI) - return self.__do_post(self.URI, {}, timeout, custom_headers) + return self.do_post(self.URI, {}, timeout, custom_headers) def create(self, data=None, timeout=-1, custom_headers=None): """ @@ -338,16 +268,14 @@ def create(self, data=None, timeout=-1, custom_headers=None): Returns: Created resource. """ + uri = self.self.URI + resource = deepcopy(self.data) + resource.update(data) + logger.debug('Create (uri = %s, resource = %s)' % - (self.URI, str(data))) + (uri, str(resource))) - resource = deepcopy(self.data) - if data: - resource.update(self.data) - - self.data = self.__do_post(self.URI, resource, timeout, custom_headers) - - return self.data + return self.do_post(uri, resource, timeout, custom_headers) def delete_all(self, filter, force=False, timeout=-1): """ @@ -378,7 +306,7 @@ def delete_all(self, filter, force=False, timeout=-1): return self._task_monitor.wait_for_task(task, timeout=timeout) - @ensure_resource_data + @ensure_resource_client def delete(self, force=False, timeout=-1, custom_headers=None): if self.data and 'uri' in self.data and self.data['uri']: @@ -433,8 +361,8 @@ def get_collection(self, id_or_uri, filter=''): response = self._connection.get(uri) return self.__get_members(response) - @ensure_resource_data - def update_with_zero_body(self, timeout=-1, custom_headers=None): + @ensure_resource_client + def update_with_zero_body(self, path=None, timeout=-1, custom_headers=None): """ Makes a PUT request to update a resource when no request body is required. @@ -450,11 +378,16 @@ def update_with_zero_body(self, timeout=-1, custom_headers=None): Returns: Updated resource. """ - logger.debug('Update with zero length body (uri = %s)' % self.data['uri']) + if path: + uri = '{}/{}'.format(self.URI, path) + else: + uri = self.data['uri'] + + logger.debug('Update with zero length body (uri = %s)' % uri) - return self.__do_put(self.data['uri'], None, timeout, custom_headers) + return self.do_put(uri, None, timeout, custom_headers) - @ensure_resource_data + @ensure_resource_client def update(self, data=None, force=False, timeout=-1, custom_headers=None): """ Makes a PUT request to update a resource when a request body is required. @@ -474,21 +407,18 @@ def update(self, data=None, force=False, timeout=-1, custom_headers=None): Returns: Updated resource. """ - logger.debug('Update async (uri = %s, resource = %s)' % - (self.URI, str(data))) - uri = self.data['uri'] + resource = deepcopy(self.data) + resource.update(data) + logger.debug('Update async (uri = %s, resource = %s)' % + (uri, str(resource))) if force: uri += '?force=True' - if data: - resource.update(data) - self.data = self.__do_put(uri, resource, timeout, custom_headers) + return self.do_put(uri, resource, timeout, custom_headers) - return self.data - def patch(self, operation, path, value, timeout=-1, custom_headers=None): """ Uses the PATCH to update a resource. @@ -511,7 +441,7 @@ def patch(self, operation, path, value, timeout=-1, custom_headers=None): timeout=timeout, custom_headers=custom_headers) - @ensure_resource_data + @ensure_resource_client def _patch_request(self, body, timeout=-1, custom_headers=None): """ Uses the PATCH to update a resource. @@ -587,9 +517,9 @@ def get_by_uri(self, uri): Retrieve a resource by its id """ self. __validate_resource_uri(uri) - return self._connection.get(uri) + return self.do_get(uri) - @ensure_resource_data + @ensure_resource_client def get_utilization(self, fields=None, filter=None, refresh=False, view=None): """ Retrieves historical utilization data for the specified resource, metrics, and time span. @@ -676,9 +606,9 @@ def get_utilization(self, fields=None, filter=None, refresh=False, view=None): uri = "{0}/utilization{1}".format(self.build_uri(uri), query) - return self._connection.get(uri) + return self.do_get(uri) - @ensure_resource_data + @ensure_resource_client def create_report(self, timeout=-1): """ Creates a report and returns the output. @@ -731,12 +661,6 @@ def build_subresource_uri(self, resource_id_or_uri=None, subresource_id_or_uri=N return uri - def unavailable_method(self): - """ - Raise exception if method is not available for the resource - """ - raise HPOneViewUnavailableMethod(UNAVAILABLE_METHOD) - def __validate_resource_uri(self, path): if self.URI not in path: logger.exception('Get by uri : unrecognized uri: (%s)' % path) @@ -756,7 +680,16 @@ def __get_members(self, mlist): else: return [] - def __do_post(self, uri, resource, timeout, custom_headers): + def do_get(self, uri): + """ + Method to support get requests of the resource + """ + return self._connection.get(uri) + + def do_post(self, uri, resource, timeout, custom_headers): + """ + Method to support post requests of the resource + """ task, entity = self._connection.post(uri, resource, custom_headers=custom_headers) if not task: @@ -764,7 +697,10 @@ def __do_post(self, uri, resource, timeout, custom_headers): return self._task_monitor.wait_for_task(task, timeout) - def __do_put(self, uri, resource, timeout, custom_headers): + def do_put(self, uri, resource, timeout, custom_headers): + """ + Method to support the put requests of the resource + """ task, body = self._connection.put(uri, resource, custom_headers=custom_headers) if not task: @@ -810,7 +746,68 @@ def __validate_resource_uri(self, path): if self.URI not in path: logger.exception('Get by uri : unrecognized uri: (%s)' % path) raise HPOneViewUnknownType(UNRECOGNIZED_URI) + + def merge_resources(resource_to_merge): + """ + Updates a copy of resource1 with resource2 values and returns the merged dictionary. + + Args: + resource1: original resource + resource2: resource to update resource1 + + Returns: + dict: merged resource + """ + merged = deepcopy(self.data) + merged.update(resource_to_merge) + return merged + def upload(self, file_path, uri=None, timeout=-1): + """ + Makes a multipart request. + + Args: + file_path: + File to upload. + uri: + A specific URI (optional). + timeout: + Timeout in seconds. Wait for task completion by default. The timeout does not abort the operation + in OneView; it just stops waiting for its completion. + + Returns: + dict: Response body. + """ + if not uri: + uri = self.URI + + upload_file_name = os.path.basename(file_path) + task, entity = self._connection.post_multipart_with_response_handling(uri, file_path, upload_file_name) + + if not task: + return entity + + return self._task_monitor.wait_for_task(task, timeout) + + def download(self, uri, file_path): + """ + Downloads the contents of the requested URI to a stream. + + Args: + uri: URI + file_path: File path destination + + Returns: + bool: Indicates if the file was successfully downloaded. + """ + with open(file_path, 'wb') as file: + return self._connection.download_to_stream(file, uri) + + def unavailable_method(self): + """ + Raise exception if method is not available for the resource + """ + raise HPOneViewUnavailableMethod(UNAVAILABLE_METHOD) class ResourceClient(object): diff --git a/hpOneView/resources/servers/enclosures.py b/hpOneView/resources/servers/enclosures.py index d2178d54..9e0e147c 100644 --- a/hpOneView/resources/servers/enclosures.py +++ b/hpOneView/resources/servers/enclosures.py @@ -30,60 +30,20 @@ standard_library.install_aliases() -from hpOneView.resources.resource import ResourceClient +from hpOneView.resources.resource import Resource, ensure_resource_client -class Enclosures(object): +class Enclosures(Resource): """ Enclosures API client. """ URI = '/rest/enclosures' - def __init__(self, con): - self._connection = con - self._client = ResourceClient(con, self.URI) - - def get_all(self, start=0, count=-1, filter='', sort='', scope_uris=''): - """ - Gets a paginated collection of Enclosures. The collection is based on optional sorting and filtering, and - constrained by start and count parameters. - - Args: - start: - The first item to return, using 0-based indexing. - If not specified, the default is 0 - start with the first available item. - count: - The number of resources to return. A count of -1 requests all items. - The actual number of items in the response might differ from the requested - count if the sum of start and count exceeds the total number of items. - filter (list or str): - A general filter/query string to narrow the list of items returned. The - default is no filter; all resources are returned. - sort: - The sort order of the returned data set. By default, the sort order is based - on create time with the oldest entry first. - - Returns: - list: A list of Enclosures. - """ - return self._client.get_all(start, count, filter=filter, sort=sort, scope_uris=scope_uris) - - def get_by(self, field, value): - """ - Gets all Enclosures that match the filter. - - The search is case-insensitive. - - Args: - field: Field name to filter. - value: Value to filter. - - Returns: - list: A list of Enclosures. - """ - return self._client.get_by(field, value) + def __init__(self, connection, options): + super(FcNetworks, self).__init__(connection, options) + def add(self, information, timeout=-1): """ C7000: @@ -104,78 +64,26 @@ def add(self, information, timeout=-1): dict: Enclosure. """ - return self._client.create(information, timeout=timeout) - - def get(self, id_or_uri): - """ - Returns the enclosure with the specified ID, if it exists. - - Args: - id_or_uri: ID or URI of the Enclosure. - - Returns: - dict: Enclosure. - """ - return self._client.get(id_or_uri) - - def patch(self, id_or_uri, operation, path, value, timeout=-1): - """ - Uses the PATCH to update a resource for a given enclosure. - - Only one operation can be performed in each PATCH call. - - Args: - id_or_uri: Can be either the resource ID or the resource URI. - operation: Patch operation - path: Path - value: Value - timeout: Timeout in seconds. Wait for task completion by default. The timeout does not abort the operation - in OneView; it just stops waiting for its completion. - - Returns: - dict: Updated resource. - """ - headers = {'If-Match': '*'} - return self._client.patch(id_or_uri, operation, path, value, timeout=timeout, custom_headers=headers) - - def remove(self, resource, force=False, timeout=-1): - """ - Removes and unconfigures the specified enclosure from the appliance. All components of the enclosure (for - example: blades and interconnects) are unconfigured/removed as part of this process. - - If the force option is set to "true", then any errors encountered as part of unconfiguring the enclosure or its - components are ignored and the enclosure is removed regardless of any errors that occur. - - Args: - resource: Dict object to delete; - force: - If set to true, the operation completes despite any problems with - network connectivity or errors on the resource itself. The default is false. - timeout: Timeout in seconds. Wait for task completion by default. The timeout does not abort the operation - in OneView; it just stops waiting for its completion. - - Returns: - bool: Indicates if the resource was successfully removed. - """ - return self._client.delete(resource, force=force, timeout=timeout) - - def update_configuration(self, id_or_uri, timeout=-1): + return self.create(information, timeout=timeout) + + @ensure_resource_client + def update_configuration(self, timeout=-1): """ Reapplies the appliance's configuration on the enclosure. This includes running the same configure steps that were performed as part of the enclosure add. Args: - id_or_uri: Can be either the resource ID or the resource URI. timeout: Timeout in seconds. Wait for task completion by default. The timeout does not abort the operation in OneView; it just stops waiting for its completion. Returns: Enclosure """ - uri = self._client.build_uri(id_or_uri) + "/configuration" - return self._client.update_with_zero_body(uri, timeout=timeout) + path = "/configuration" + return self.update_with_zero_body(path=path, timeout=timeout) - def get_environmental_configuration(self, id_or_uri): + @ensure_resource_client + def get_environmental_configuration(self): """ Gets the settings that describe the environmental configuration (supported feature set, calibrated minimum & maximum power, location & dimensions, ...) of the enclosure resource. @@ -186,10 +94,11 @@ def get_environmental_configuration(self, id_or_uri): Returns: Settings that describe the environmental configuration. """ - uri = self._client.build_uri(id_or_uri) + '/environmentalConfiguration' - return self._client.get(uri) + uri = '{}/environmentalConfiguration'.format(self.data['uri']) + return self.do_get(uri) - def update_environmental_configuration(self, id_or_uri, configuration, timeout=-1): + @ensure_resource_client + def update_environmental_configuration(self, configuration, timeout=-1): """ Sets the calibrated max power of an unmanaged or unsupported enclosure. @@ -202,10 +111,11 @@ def update_environmental_configuration(self, id_or_uri, configuration, timeout=- Returns: Settings that describe the environmental configuration. """ - uri = self._client.build_uri(id_or_uri) + '/environmentalConfiguration' - return self._client.update(configuration, uri=uri, timeout=timeout) + uri = '{}/environmentalConfiguration'.format(self.data['uri']) + return self.do_put(uri, configuration, timeout) - def refresh_state(self, id_or_uri, configuration, timeout=-1): + @ensure_resource_client + def refresh_state(self, configuration, timeout=-1): """ Refreshes the enclosure along with all of its components, including interconnects and servers. Any new hardware is added and any hardware that is no longer present within the enclosure is removed. The @@ -213,7 +123,6 @@ def refresh_state(self, id_or_uri, configuration, timeout=-1): provide information to re-claim the enclosure (for example: IP address, user name, password, etc.). Args: - id_or_uri: Can be either the resource ID or the resource URI. configuration: Configuration timeout: Timeout in seconds. Wait for task completion by default. The timeout does not abort the operation in OneView; it just stops waiting for its completion. @@ -221,10 +130,11 @@ def refresh_state(self, id_or_uri, configuration, timeout=-1): Returns: Enclosure """ - uri = self._client.build_uri(id_or_uri) + "/refreshState" - return self._client.update(configuration, uri=uri, timeout=timeout) + uri = "{}/refreshState".format(self.data['uri']) + return self.do_put(uri, configuration, timeout) - def get_script(self, id_or_uri): + @ensure_resource_client + def get_script(self): """ Gets the script of the enclosure. @@ -234,105 +144,26 @@ def get_script(self, id_or_uri): Returns: Enclosure script. """ - uri = self._client.build_uri(id_or_uri) + "/script" - return self._client.get(uri) + uri = "{}/script".format(self.data['uri']) + return self.do_get(uri) - def get_sso(self, id_or_uri, role): + @ensure_resource_client + def get_sso(self, role): """ Builds the SSO (Single Sign-On) URL parameters for the specified enclosure. This allows the user to log in to the enclosure without providing credentials. This API is currently only supported by C7000 enclosures. Args: - id_or_uri: Can be either the resource ID or the resource URI. role: Role Returns: SSO (Single Sign-On) URL parameters. """ - uri = self._client.build_uri(id_or_uri) + "/sso?role=%s" % role - return self._client.get(uri) + uri = "{}/sso?role={}".format(self.data['uri'], role) + return self.do_get(uri) - def get_utilization(self, id_or_uri, fields=None, filter=None, refresh=False, view=None): - """ - Retrieves historical utilization data for the specified enclosure, metrics, and time span. - - Args: - id_or_uri: Can be either the resource ID or the resource URI. - fields: Name of the metrics to be retrieved in the format METRIC[,METRIC]... - - If unspecified, all metrics supported are returned. - - Enclosures support the following utilization metrics: - AmbientTemperature - Inlet air temperature in degrees Celsius during this sample interval. - AveragePower - Average power consumption in Watts during this sample interval. - PeakPower - Peak power consumption in Watts during this sample interval. - PowerCap - Dynamic power cap setting on the server hardware in Watts during this sample interval. - DeratedCapacity - Enclosure dynamic power cap derated capacity setting in Watts during this sample interval. - RatedCapacity - Enclosure dynamic power cap rated capacity setting in Watts during this sample interval. - - filter (list or str): - Provides an expression of the requested time range of data. One condition (startDate/endDate) is - specified per filter specification as described below. The condition must be specified via the - equals (=) operator. - - startDate - Start date of requested starting time range in ISO 8601 format (2016-05-31T07:20:00.000Z). - If omitted, the startDate is determined by the endDate minus 24 hours. - endDate - End date of requested starting time range in ISO 8601 format. When omitted the endDate includes the - latest data sample available. - - If an excessive number of samples would otherwise be returned, the results will be segmented. The caller - is responsible for comparing the returned sliceStartTime with the requested startTime in the response. - If the sliceStartTime is greater than the oldestSampleTime and the requested start time, the caller is - responsible for repeating the request with endTime set to sliceStartTime to obtain the next segment. - This process is repeated until the full data set is retrieved. - - If the resource has no data, the UtilizationData is still returned, but will contain no samples and - sliceStartTime/sliceEndTime will be equal. oldestSampleTime/newestSampleTime will still be set - appropriately (null if no data is available). If the filter just does not happen to overlap the data - that a resource does have, then the metric history service will return null sample values for any - missing samples. - - refresh: - Specifies that if necessary an additional request will be queued to obtain the most recent utilization - data from the enclosure. The response will not include any refreshed data. To track the availability - of the newly collected data, monitor the TaskResource identified by the refreshTaskUri property in - the response. If null, no refresh was queued. - view: - Specifies the resolution interval length of the samples to be retrieved. This is reflected in the - resolution in the returned response. Utilization data is automatically purged to stay within storage - space constraints. Supported views are listed below. - - native (DEFAULT) - Resolution of the samples returned will be one sample for each 5-minute time period. This is the - default view and matches the resolution of the data returned by the enclosure. Samples at this - resolution are retained up to one year. - hour - Resolution of the samples returned will be one sample for each 60-minute time period. Samples are - calculated by averaging the available 5-minute data samples that occurred within the hour, except - for PeakPower, which is calculated by reporting the peak observed 5-minute sample value data during - the hour. Samples at this resolution are retained up to three years. - day - Resolution of the samples returned will be one sample for each 24-hour time period. One day is a - 24-hour period that starts at midnight GMT, regardless of the time zone in which the appliance or - client is located. Samples are calculated by averaging the available 5-minute data samples that - occurred during the day, except for PeakPower, which is calculated by reporting the peak observed - 5-minute sample value data during the day. Samples at this resolution are retained up to three - years. - - Returns: - dict - """ - return self._client.get_utilization(id_or_uri, fields=fields, filter=filter, refresh=refresh, view=view) - - def generate_csr(self, csr_data, id_or_uri, bay_number=None): + @ensure_resource_client + def generate_csr(self, csr_data, bay_number=None): """ Creates a Certificate Signing Request (CSR) for an enclosure. @@ -344,16 +175,17 @@ def generate_csr(self, csr_data, id_or_uri, bay_number=None): Returns: Enclosure. """ - - uri = self._client.build_uri(id_or_uri) + "/https/certificaterequest" + uri = "{}/https/certificaterequest".format(self.data['uri']) if bay_number: uri += "?bayNumber=%d" % (bay_number) headers = {'Content-Type': 'application/json'} - return self._client.create(csr_data, uri=uri, custom_headers=headers) - def get_csr(self, id_or_uri, bay_number=None): + return self.do_post(uri, csr_data, -1, headers) + + @ensure_resource_client + def get_csr(self, bay_number=None): """ Get an enclosure's Certificate Signing Request (CSR) that was generated by previous POST to the same URI. @@ -365,30 +197,30 @@ def get_csr(self, id_or_uri, bay_number=None): dict """ - uri = self._client.build_uri(id_or_uri) + "/https/certificaterequest" + uri = "{}/https/certificaterequest".format(self.data['uri']) if bay_number: uri += "?bayNumber=%d" % (bay_number) - return self._client.get(uri) + return self.do_get(uri) - def import_certificate(self, certificate_data, id_or_uri, bay_number=None): + @ensure_resource_client + def import_certificate(self, certificate_data, bay_number=None): """ Imports a signed server certificate into the enclosure. Args: certificate_data: Dictionary with Signed certificate and type. - id_or_uri: Can be either the resource ID or the resource URI. bay_number: OA to which the signed certificate will be imported. Returns: Enclosure. """ - uri = self._client.build_uri(id_or_uri) + "/https/certificaterequest" + uri = "{}/https/certificaterequest".format(self.data['uri']) if bay_number: uri += "?bayNumber=%d" % (bay_number) headers = {'Content-Type': 'application/json'} - return self._client.update(certificate_data, uri=uri, custom_headers=headers) + return self.do_put(uri, certificate_data, headers) From caa5a7f2833620e73d153c4ec11edab79676763f Mon Sep 17 00:00:00 2001 From: sijeesh Date: Mon, 13 Aug 2018 12:48:49 +0530 Subject: [PATCH 04/11] Upgraded enclosure resource class and example file by using the new base class --- examples/enclosures.py | 80 +++++------ examples/fc_networks.py | 28 ++-- hpOneView/oneview_client.py | 8 +- hpOneView/resources/networking/fc_networks.py | 2 +- hpOneView/resources/resource.py | 125 ++++++++++++++---- hpOneView/resources/servers/enclosures.py | 22 ++- 6 files changed, 169 insertions(+), 96 deletions(-) diff --git a/examples/enclosures.py b/examples/enclosures.py index 49c8611c..79cd5f51 100644 --- a/examples/enclosures.py +++ b/examples/enclosures.py @@ -22,18 +22,23 @@ ### from pprint import pprint + from hpOneView.oneview_client import OneViewClient -from hpOneView.exceptions import HPOneViewException +from hpOneView.exceptions import HPOneViewException, HPOneViewTaskError from config_loader import try_load_from_file # This example is compatible only for C7000 enclosures config = { - "ip": "", + "ip": "10.30.5.228", "credentials": { - "userName": "", - "password": "" - } + "userName": "administrator", + "password": "ecosystem" + }, + "enclosure_group_uri": "/rest/enclosure-groups/00bdf757-7ce2-4ffd-be7e-1a568dd8ecfc", + "enclosure_hostname": "172.18.1.11", + "enclosure_username": "dcs", + "enclosure_password": "dcs" } # Declare a CA signed certificate file path. @@ -44,6 +49,7 @@ # The hostname, enclosure group URI, username, and password must be set on the configuration file options = { + "name": "Encl1-Updated-Updated-Updated", "enclosureGroupUri": config['enclosure_group_uri'], "hostname": config['enclosure_hostname'], "username": config['enclosure_username'], @@ -51,46 +57,47 @@ "licensingIntent": "OneView" } +# Get OneView client oneview_client = OneViewClient(config) -# Add an Enclosure -enclosure = oneview_client.enclosures.add(options) -enclosure_uri = enclosure['uri'] -print("Added enclosure '{name}'.\n URI = '{uri}'".format(**enclosure)) - -# Perform a patch operation, replacing the name of the enclosure -enclosure_name = enclosure['name'] + "-Updated" +# Add an enclosure +try: + # This will return an enclosure object + enclosure = oneview_client.enclosures.add(options) + print("Added enclosure '{name}'.\n URI = '{uri}'".format(**enclosure.data)) +except HPOneViewTaskError, e: + print(e) + #Get the reosurce by name + enclosure = oneview_client.enclosures.get_by_name(options['name']) + +# Perform a patch operation on the enclosure, replacing the name of the enclosure +enclosure_name = "Encl1-Updated-11" print("Updating the enclosure to have a name of " + enclosure_name) -enclosure = oneview_client.enclosures.patch(enclosure_uri, 'replace', '/name', enclosure_name) -print(" Done.\n URI = '{uri}', name = {name}".format(**enclosure)) - -# Find the recently added enclosure by name -print("Find an enclosure by name") -enclosure = oneview_client.enclosures.get_by('name', enclosure['name'])[0] -print(" URI = '{uri}'".format(**enclosure)) +enclosure.patch('replace', '/name', enclosure_name) +print(" Done.\n URI = '{uri}', name = {name}".format(**enclosure.data)) # Get by URI print("Find an enclosure by URI") -enclosure = oneview_client.enclosures.get(enclosure_uri) -pprint(enclosure) +enclosure = enclosure.get_by_uri(enclosure.data['uri']) +pprint(enclosure.data) # Get all enclosures print("Get all enclosures") -enclosures = oneview_client.enclosures.get_all() +enclosures = enclosure.get_all() for enc in enclosures: print(' {name}'.format(**enc)) # Update configuration print("Reapplying the appliance's configuration on the enclosure") try: - oneview_client.enclosures.update_configuration(enclosure_uri) + enclosure.update_configuration() print(" Done.") except HPOneViewException as e: print(e.msg) print("Retrieve the environmental configuration data for the enclosure") try: - environmental_configuration = oneview_client.enclosures.get_environmental_configuration(enclosure_uri) + environmental_configuration = enclosure.get_environmental_configuration() print(" Enclosure calibratedMaxPower = {calibratedMaxPower}".format(**environmental_configuration)) except HPOneViewException as e: print(e.msg) @@ -99,7 +106,7 @@ print("Refreshing the enclosure") try: refresh_state = {"refreshState": "RefreshPending"} - enclosure = oneview_client.enclosures.refresh_state(enclosure_uri, refresh_state) + enclosure.refresh_state(refresh_state) print(" Done") except HPOneViewException as e: print(e.msg) @@ -107,15 +114,15 @@ # Get the enclosure script print("Get the enclosure script") try: - script = oneview_client.enclosures.get_script(enclosure_uri) + script = enclosure.get_script() pprint(script) except HPOneViewException as e: print(e.msg) -# Buid the SSO URL parameters +#### Buid the SSO URL parameters print("Build the SSO (Single Sign-On) URL parameters for the enclosure") try: - sso_url_parameters = oneview_client.enclosures.get_sso(enclosure_uri, 'Active') + sso_url_parameters = enclosure.get_sso('Active') pprint(sso_url_parameters) except HPOneViewException as e: print(e.msg) @@ -123,10 +130,9 @@ # Get Statistics specifying parameters print("Get the enclosure statistics") try: - enclosure_statistics = oneview_client.enclosures.get_utilization(enclosure_uri, - fields='AveragePower', - filter='startDate=2016-06-30T03:29:42.000Z', - view='day') + enclosure_statistics = enclosure.get_utilization(fields='AveragePower', + filter='startDate=2016-06-30T03:29:42.000Z', + view='day') pprint(enclosure_statistics) except HPOneViewException as e: print(e.msg) @@ -143,14 +149,14 @@ "commonName": "" } try: - oneview_client.enclosures.generate_csr(csr_data, enclosure_uri, bay_number=bay_number) + enclosure.generate_csr(csr_data, bay_number=bay_number) print("Generated CSR for the enclosure.") except HPOneViewException as e: print(e.msg) # Get the certificate Signing Request (CSR) that was generated by previous POST. try: - csr = oneview_client.enclosures.get_csr(enclosure_uri, bay_number=bay_number) + csr = enclosure.get_csr(bay_number=bay_number) with open('enclosure.csr', 'w') as csr_file: csr_file.write(csr["base64Data"]) print("Saved CSR(generated by previous POST) to 'enclosure.csr' file") @@ -160,7 +166,7 @@ try: # Get Enclosure by scope_uris if oneview_client.api_version >= 600: - enclosures_by_scope_uris = oneview_client.enclosures.get_all(scope_uris="\"'/rest/scopes/3bb0c754-fd38-45af-be8a-4d4419de06e9'\"") + enclosures_by_scope_uris = enclosure.get_all(scope_uris="\"'/rest/scopes/3bb0c754-fd38-45af-be8a-4d4419de06e9'\"") if len(enclosures_by_scope_uris) > 0: print("Found %d Enclosures" % (len(enclosures_by_scope_uris))) i = 0 @@ -185,11 +191,11 @@ "base64Data": certificate } - oneview_client.enclosures.import_certificate(certificate_data, enclosure_uri, bay_number=bay_number) + enclosure.import_certificate(certificate_data, enclosure_uri, bay_number=bay_number) print("Imported Signed Certificate to the enclosure.") except HPOneViewException as e: print(e.msg) # Remove the recently added enclosure -oneview_client.enclosures.remove(enclosure) +enclosure.remove() print("Enclosure removed successfully") diff --git a/examples/fc_networks.py b/examples/fc_networks.py index 5f105710..dd58b3f1 100644 --- a/examples/fc_networks.py +++ b/examples/fc_networks.py @@ -51,31 +51,23 @@ oneview_client = OneViewClient(config) -connection = oneview_client.connection - -#Create FcNetWorks object -fc_network = FcNetworks(connection, options) - -#Create a FcNetWork with the options provided +# Create a FcNetWork with the options provided try: - fc_network.create() + fc_network = oneview_client.fc_networks.create(data=options) + print("\nCreated a fc-network with name: '%s'.\n uri = '%s'" % (fc_network.data['name'], fc_network.data['uri'])) except HPOneViewException, e: print(e[0]) -#Get data of the created fc network -data = fc_network.get() -print(data) - # Find recently created network by name -data = fc_network.get_by_name(options['name']) -print("\nFound fc-network by name: '%s'.\n uri = '%s'" % (data['name'], data['uri'])) +fc_network = oneview_client.fc_networks.get_by_name(options['name']) +print("\nFound fc-network by name: '%s'.\n uri = '%s'" % (fc_network.data['name'], fc_network.data['uri'])) # Update autoLoginRedistribution from recently created network data_to_update = {'autoLoginRedistribution': False, 'name':'Updated FC'} resource = fc_network.update(data=data_to_update) -print("\nUpdated fc-network '%s' successfully.\n uri = '%s'" % (resource['name'], resource['uri'])) -print(" with attribute {'autoLoginRedistribution': %s}" % resource['autoLoginRedistribution']) +print("\nUpdated fc-network '%s' successfully.\n uri = '%s'" % (resource.data['name'], resource.data['uri'])) +print(" with attribute {'autoLoginRedistribution': %s}" % resource.data['autoLoginRedistribution']) # Get all, with defaults print("\nGet all fc-networks") @@ -99,15 +91,15 @@ # Get by uri print("\nGet a fc-network by uri") -fc_nets_by_uri = fc_network.get_by_uri(resource['uri']) -pprint(fc_nets_by_uri) +fc_nets_by_uri = fc_network.get_by_uri(resource.data['uri']) +pprint(fc_nets_by_uri.data) # Adds ethernet to scope defined if scope_name: print("\nGet scope then add the network to it") scope = oneview_client.scopes.get_by_name(scope_name) # TODO: This has to updated try: - fc_with_scope = fc_network.patch(resource['uri'], + fc_with_scope = fc_network.patch(resource.data['uri'], 'replace', '/scopeUris', [scope['uri']]) diff --git a/hpOneView/oneview_client.py b/hpOneView/oneview_client.py index 6c1a7bf5..56c2c342 100755 --- a/hpOneView/oneview_client.py +++ b/hpOneView/oneview_client.py @@ -341,9 +341,7 @@ def fc_networks(self): Returns: FcNetworks: """ - if not self.__fc_networks: - self.__fc_networks = FcNetworks(self.__connection) - return self.__fc_networks + return FcNetworks(self.__connection) @property def fcoe_networks(self): @@ -618,9 +616,7 @@ def enclosures(self): Returns: Enclosures: """ - if not self.__enclosures: - self.__enclosures = Enclosures(self.__connection) - return self.__enclosures + return Enclosures(self.__connection) @property def logical_enclosures(self): diff --git a/hpOneView/resources/networking/fc_networks.py b/hpOneView/resources/networking/fc_networks.py index 96a646b8..922ef700 100644 --- a/hpOneView/resources/networking/fc_networks.py +++ b/hpOneView/resources/networking/fc_networks.py @@ -48,7 +48,7 @@ class FcNetworks(Resource): '600': {"type": "fc-networkV4"} } - def __init__(self, connection, options): + def __init__(self, connection, options=None): super(FcNetworks, self).__init__(connection, options) diff --git a/hpOneView/resources/resource.py b/hpOneView/resources/resource.py index 8483b507..117334be 100755 --- a/hpOneView/resources/resource.py +++ b/hpOneView/resources/resource.py @@ -38,8 +38,8 @@ from functools import update_wrapper, partial from hpOneView.resources.task_monitor import TaskMonitor -from hpOneView.exceptions import HPOneViewUnknownType, HPOneViewException -from hpOneView.exceptions import HPOneViewValueError +from hpOneView.exceptions import HPOneViewUnknownType, HPOneViewException, HPOneViewMissingUniqueIdentifiers +from hpOneView.exceptions import HPOneViewValueError, HPOneViewResourceNotFound RESOURCE_CLIENT_RESOURCE_WAS_NOT_PROVIDED = 'Resource was not provided' RESOURCE_CLIENT_INVALID_FIELD = 'Invalid field was provided' @@ -50,6 +50,7 @@ RESOURCE_ID_OR_URI_REQUIRED = 'It is required to inform the Resource ID or URI.' UNAVAILABLE_METHOD = "Method is not available for this resource" MISSING_UNIQUE_IDENTIFIERS = "Missing unique identifiers(URI/Name) for the resource" +RESOURCE_DOES_NOT_EXISTS = "Resource does not exists with provided unique identifiers" logger = logging.getLogger(__name__) @@ -74,14 +75,22 @@ class Resource(object): """ This class implements common functions for HpOneView API rest """ - + # Base URI for the rest calls URI = '/rest' + + # Unique identifiers to query the resource + UNIQUE_IDENTIFIERS = ['uri', 'name'] + + # Add fields to be removed from the request body of a post request + EXCLUDE_FROM_REQUEST = [] + + # Default values required for the api versions DEFAULT_VALUES = {} - def __init__(self, connection, data): + def __init__(self, connection, data=None): self._connection = connection - self.data = data + self.data = data if data else {} self._merge_default_values() self._task_monitor = TaskMonitor(connection) @@ -163,26 +172,37 @@ def load_resource(self): """ Retrieve data from OneView and update resource data """ - uri = self.data.get('uri') - name = self.data.get('name') - - if not uri and not name: + #Check for unique identifier in the resource data + if not any(key in self.data for key in self.UNIQUE_IDENTIFIERS): raise HPOneViewMissingUniqueIdentifiers(MISSING_UNIQUE_IDENTIFIERS) - - if uri: - self.data.update(self.do_get(uri)) - if name: - self.data.update(self.get_by_name(name)) + resource_data = None - return self.data + if 'uri' in self.UNIQUE_IDENTIFIERS and self.data.get('uri'): + uri = self.data['uri'] + if self.URI in uri: + resource_data = self.do_get(uri) + else: + for identifier in self.UNIQUE_IDENTIFIERS: + identifier_value = self.data.get(identifier) + + if identifier_value: + result = self.get_by(identifier, identifier_value) + if result and isinstance(result, list): + resource_data = result[0] + break + + if not resource_data: + raise HPOneViewResourceNotFound(RESOURCE_DOES_NOT_EXISTS) + + self.data.update(resource_data) @ensure_resource_client def get(self): """ Return resource data """ - return self.data + return self def get_all(self, start=0, count=-1, filter='', query='', sort='', view='', fields='', scope_uris=''): """ @@ -251,7 +271,9 @@ def create_with_zero_body(self, timeout=-1, custom_headers=None): """ logger.debug('Create with zero body (uri = %s)' % self.URI) - return self.do_post(self.URI, {}, timeout, custom_headers) + self.do_post(self.URI, {}, timeout, custom_headers) + + return self def create(self, data=None, timeout=-1, custom_headers=None): """ @@ -268,14 +290,18 @@ def create(self, data=None, timeout=-1, custom_headers=None): Returns: Created resource. """ - uri = self.self.URI + uri = self.URI resource = deepcopy(self.data) - resource.update(data) + + if data: + resource.update(data) logger.debug('Create (uri = %s, resource = %s)' % (uri, str(resource))) - return self.do_post(uri, resource, timeout, custom_headers) + self.data = self.do_post(uri, resource, timeout, custom_headers) + + return self def delete_all(self, filter, force=False, timeout=-1): """ @@ -417,7 +443,9 @@ def update(self, data=None, force=False, timeout=-1, custom_headers=None): if force: uri += '?force=True' - return self.do_put(uri, resource, timeout, custom_headers) + self.data = self.do_put(uri, resource, timeout, custom_headers) + + return self def patch(self, operation, path, value, timeout=-1, custom_headers=None): """ @@ -437,9 +465,10 @@ def patch(self, operation, path, value, timeout=-1, custom_headers=None): """ patch_request_body = [{'op': operation, 'path': path, 'value': value}] - return self._patch_request(body=patch_request_body, - timeout=timeout, - custom_headers=custom_headers) + self.data = self._patch_request(body=patch_request_body, + timeout=timeout, + custom_headers=custom_headers) + return self @ensure_resource_client def _patch_request(self, body, timeout=-1, custom_headers=None): @@ -488,7 +517,7 @@ def get_by(self, field, value): filter = "\"{0}='{1}'\"".format(field, value) results = self.get_all(filter=filter) - + print('get all', results) # Workaround when the OneView filter does not work, it will filter again if "." not in field: # This filter only work for the first level @@ -510,14 +539,17 @@ def get_by_name(self, name): if not result: return None else: - return result[0] + self.data = result[0] + return self def get_by_uri(self, uri): """ Retrieve a resource by its id """ self. __validate_resource_uri(uri) - return self.do_get(uri) + self.data = self.do_get(uri) + + return self @ensure_resource_client def get_utilization(self, fields=None, filter=None, refresh=False, view=None): @@ -690,6 +722,10 @@ def do_post(self, uri, resource, timeout, custom_headers): """ Method to support post requests of the resource """ + + for field in self.EXCLUDE_FROM_REQUEST: + resource.pop(field, None) + task, entity = self._connection.post(uri, resource, custom_headers=custom_headers) if not task: @@ -1534,6 +1570,41 @@ def merge_default_values(self, resource, default_values): return merged_resource or resource +def merge_resources(resource1, resource2): + """ + Updates a copy of resource1 with resource2 values and returns the merged dictionary. + + Args: + resource1: original resource + resource2: resource to update resource1 + + Returns: + dict: merged resource + """ + merged = resource1.copy() + merged.update(resource2) + return merged + + +def merge_default_values(resource_list, default_values): + """ + Generate a new list where each item of original resource_list will be merged with the default_values. + + Args: + resource_list: list with items to be merged + default_values: properties to be merged with each item list. If the item already contains some property + the original value will be maintained. + + Returns: + list: list containing each item merged with default_values + """ + + def merge_item(resource): + return merge_resources(default_values, resource) + + return lmap(merge_item, resource_list) + + def transform_list_to_dict(list): """ Transforms a list into a dictionary, putting values as keys diff --git a/hpOneView/resources/servers/enclosures.py b/hpOneView/resources/servers/enclosures.py index 9e0e147c..4b73f26d 100644 --- a/hpOneView/resources/servers/enclosures.py +++ b/hpOneView/resources/servers/enclosures.py @@ -29,6 +29,7 @@ standard_library.install_aliases() +from copy import deepcopy from hpOneView.resources.resource import Resource, ensure_resource_client @@ -40,8 +41,8 @@ class Enclosures(Resource): """ URI = '/rest/enclosures' - def __init__(self, connection, options): - super(FcNetworks, self).__init__(connection, options) + def __init__(self, connection, options=None): + super(Enclosures, self).__init__(connection, options) def add(self, information, timeout=-1): @@ -64,8 +65,15 @@ def add(self, information, timeout=-1): dict: Enclosure. """ - return self.create(information, timeout=timeout) - + self.EXCLUDE_FROM_REQUEST = ['name'] + return self.create(data=information, timeout=timeout) + + def remove(self): + """ + Remove enclosure + """ + self.delete() + @ensure_resource_client def update_configuration(self, timeout=-1): """ @@ -112,7 +120,7 @@ def update_environmental_configuration(self, configuration, timeout=-1): Settings that describe the environmental configuration. """ uri = '{}/environmentalConfiguration'.format(self.data['uri']) - return self.do_put(uri, configuration, timeout) + return self.do_put(uri, configuration, timeout, None) @ensure_resource_client def refresh_state(self, configuration, timeout=-1): @@ -131,7 +139,7 @@ def refresh_state(self, configuration, timeout=-1): Enclosure """ uri = "{}/refreshState".format(self.data['uri']) - return self.do_put(uri, configuration, timeout) + return self.do_put(uri, configuration, timeout, None) @ensure_resource_client def get_script(self): @@ -223,4 +231,4 @@ def import_certificate(self, certificate_data, bay_number=None): uri += "?bayNumber=%d" % (bay_number) headers = {'Content-Type': 'application/json'} - return self.do_put(uri, certificate_data, headers) + return self.do_put(uri, certificate_data, -1, headers) From 7a498dcbd8a4bcb332f885dcbae96ce22c6d1796 Mon Sep 17 00:00:00 2001 From: Sijeesh Kattumunda Date: Tue, 14 Aug 2018 00:37:12 -0600 Subject: [PATCH 05/11] Updated unit test cases of enclosure and fc network --- examples/enclosures.py | 8 +- examples/fc_networks.py | 13 +- hpOneView/exceptions.py | 3 +- hpOneView/resources/networking/fc_networks.py | 2 - hpOneView/resources/resource.py | 87 ++++---- hpOneView/resources/servers/enclosures.py | 9 +- .../resources/networking/test_fc_networks.py | 29 ++- .../unit/resources/servers/test_enclosures.py | 202 ++++++------------ tests/unit/test_oneview_client.py | 8 - 9 files changed, 142 insertions(+), 219 deletions(-) diff --git a/examples/enclosures.py b/examples/enclosures.py index 79cd5f51..f2ffeec9 100644 --- a/examples/enclosures.py +++ b/examples/enclosures.py @@ -60,14 +60,14 @@ # Get OneView client oneview_client = OneViewClient(config) -# Add an enclosure +# Add an enclosure try: # This will return an enclosure object enclosure = oneview_client.enclosures.add(options) print("Added enclosure '{name}'.\n URI = '{uri}'".format(**enclosure.data)) except HPOneViewTaskError, e: print(e) - #Get the reosurce by name + # Get the reosurce by name enclosure = oneview_client.enclosures.get_by_name(options['name']) # Perform a patch operation on the enclosure, replacing the name of the enclosure @@ -119,7 +119,7 @@ except HPOneViewException as e: print(e.msg) -#### Buid the SSO URL parameters +# Buid the SSO URL parameters print("Build the SSO (Single Sign-On) URL parameters for the enclosure") try: sso_url_parameters = enclosure.get_sso('Active') @@ -191,7 +191,7 @@ "base64Data": certificate } - enclosure.import_certificate(certificate_data, enclosure_uri, bay_number=bay_number) + enclosure.import_certificate(certificate_data, enclosure.data['uri'], bay_number=bay_number) print("Imported Signed Certificate to the enclosure.") except HPOneViewException as e: print(e.msg) diff --git a/examples/fc_networks.py b/examples/fc_networks.py index dd58b3f1..3337673f 100644 --- a/examples/fc_networks.py +++ b/examples/fc_networks.py @@ -23,7 +23,6 @@ from pprint import pprint from hpOneView.oneview_client import OneViewClient -from hpOneView.resources.networking.fc_networks import FcNetworks from hpOneView.exceptions import HPOneViewException from config_loader import try_load_from_file @@ -53,10 +52,10 @@ # Create a FcNetWork with the options provided try: - fc_network = oneview_client.fc_networks.create(data=options) - print("\nCreated a fc-network with name: '%s'.\n uri = '%s'" % (fc_network.data['name'], fc_network.data['uri'])) + fc_network = oneview_client.fc_networks.create(data=options) + print("\nCreated a fc-network with name: '%s'.\n uri = '%s'" % (fc_network.data['name'], fc_network.data['uri'])) except HPOneViewException, e: - print(e[0]) + print(e[0]) # Find recently created network by name fc_network = oneview_client.fc_networks.get_by_name(options['name']) @@ -64,7 +63,7 @@ # Update autoLoginRedistribution from recently created network data_to_update = {'autoLoginRedistribution': False, - 'name':'Updated FC'} + 'name': 'Updated FC'} resource = fc_network.update(data=data_to_update) print("\nUpdated fc-network '%s' successfully.\n uri = '%s'" % (resource.data['name'], resource.data['uri'])) print(" with attribute {'autoLoginRedistribution': %s}" % resource.data['autoLoginRedistribution']) @@ -97,8 +96,8 @@ # Adds ethernet to scope defined if scope_name: print("\nGet scope then add the network to it") - scope = oneview_client.scopes.get_by_name(scope_name) # TODO: This has to updated - try: + scope = oneview_client.scopes.get_by_name(scope_name) # TODO: This has to updated + try: fc_with_scope = fc_network.patch(resource.data['uri'], 'replace', '/scopeUris', diff --git a/hpOneView/exceptions.py b/hpOneView/exceptions.py index 7191d2ec..5842475b 100644 --- a/hpOneView/exceptions.py +++ b/hpOneView/exceptions.py @@ -150,6 +150,7 @@ class HPOneViewResourceNotFound(HPOneViewException): """ pass + class HPOneViewUnavailableMethod(HPOneViewException): """ OneView Unavailable Method Exception. @@ -160,6 +161,7 @@ class HPOneViewUnavailableMethod(HPOneViewException): """ pass + class HPOneViewMissingUniqueIdentifiers(HPOneViewException): """ OneView Missing Unique Identifiers Exception. @@ -169,4 +171,3 @@ class HPOneViewMissingUniqueIdentifiers(HPOneViewException): msg (str): Exception message. """ pass - diff --git a/hpOneView/resources/networking/fc_networks.py b/hpOneView/resources/networking/fc_networks.py index 922ef700..02150725 100644 --- a/hpOneView/resources/networking/fc_networks.py +++ b/hpOneView/resources/networking/fc_networks.py @@ -50,5 +50,3 @@ class FcNetworks(Resource): def __init__(self, connection, options=None): super(FcNetworks, self).__init__(connection, options) - - diff --git a/hpOneView/resources/resource.py b/hpOneView/resources/resource.py index 117334be..e5d9851a 100755 --- a/hpOneView/resources/resource.py +++ b/hpOneView/resources/resource.py @@ -38,8 +38,7 @@ from functools import update_wrapper, partial from hpOneView.resources.task_monitor import TaskMonitor -from hpOneView.exceptions import HPOneViewUnknownType, HPOneViewException, HPOneViewMissingUniqueIdentifiers -from hpOneView.exceptions import HPOneViewValueError, HPOneViewResourceNotFound +from hpOneView import exceptions RESOURCE_CLIENT_RESOURCE_WAS_NOT_PROVIDED = 'Resource was not provided' RESOURCE_CLIENT_INVALID_FIELD = 'Invalid field was provided' @@ -56,7 +55,9 @@ class EnsureResourceClient(object): - + """ + Decorator class to make sure the resource client + """ def __init__(self, func): update_wrapper(self, func) self.func = func @@ -68,8 +69,10 @@ def __call__(self, obj, *args, **kwargs): obj.load_resource() return self.func(obj, *args, **kwargs) -#Decorator to ensure the resource client -ensure_resource_client= EnsureResourceClient + +# Decorator to ensure the resource client +ensure_resource_client = EnsureResourceClient + class Resource(object): """ @@ -172,28 +175,28 @@ def load_resource(self): """ Retrieve data from OneView and update resource data """ - #Check for unique identifier in the resource data + # Check for unique identifier in the resource data if not any(key in self.data for key in self.UNIQUE_IDENTIFIERS): - raise HPOneViewMissingUniqueIdentifiers(MISSING_UNIQUE_IDENTIFIERS) + raise exceptions.HPOneViewMissingUniqueIdentifiers(MISSING_UNIQUE_IDENTIFIERS) resource_data = None if 'uri' in self.UNIQUE_IDENTIFIERS and self.data.get('uri'): - uri = self.data['uri'] + uri = self.data['uri'] if self.URI in uri: - resource_data = self.do_get(uri) + resource_data = self.do_get(uri) else: for identifier in self.UNIQUE_IDENTIFIERS: identifier_value = self.data.get(identifier) - - if identifier_value: + + if identifier_value: result = self.get_by(identifier, identifier_value) if result and isinstance(result, list): resource_data = result[0] - break + break if not resource_data: - raise HPOneViewResourceNotFound(RESOURCE_DOES_NOT_EXISTS) + raise exceptions.HPOneViewResourceNotFound(RESOURCE_DOES_NOT_EXISTS) self.data.update(resource_data) @@ -201,7 +204,7 @@ def load_resource(self): def get(self): """ Return resource data - """ + """ return self def get_all(self, start=0, count=-1, filter='', query='', sort='', view='', fields='', scope_uris=''): @@ -295,9 +298,8 @@ def create(self, data=None, timeout=-1, custom_headers=None): if data: resource.update(data) - - logger.debug('Create (uri = %s, resource = %s)' % - (uri, str(resource))) + + logger.debug('Create (uri = %s, resource = %s)' % (uri, str(resource))) self.data = self.do_post(uri, resource, timeout, custom_headers) @@ -344,7 +346,7 @@ def delete(self, force=False, timeout=-1, custom_headers=None): if force: uri += '?force=True' - logger.debug("Delete resource (uri = %s)" %(str(uri))) + logger.debug("Delete resource (uri = %s)" % (str(uri))) task, body = self._connection.delete(uri, custom_headers=custom_headers) @@ -405,9 +407,9 @@ def update_with_zero_body(self, path=None, timeout=-1, custom_headers=None): Updated resource. """ if path: - uri = '{}/{}'.format(self.URI, path) + uri = '{}/{}'.format(self.URI, path) else: - uri = self.data['uri'] + uri = self.data['uri'] logger.debug('Update with zero length body (uri = %s)' % uri) @@ -446,7 +448,7 @@ def update(self, data=None, force=False, timeout=-1, custom_headers=None): self.data = self.do_put(uri, resource, timeout, custom_headers) return self - + def patch(self, operation, path, value, timeout=-1, custom_headers=None): """ Uses the PATCH to update a resource. @@ -544,11 +546,11 @@ def get_by_name(self, name): def get_by_uri(self, uri): """ - Retrieve a resource by its id + Retrieve a resource by its id """ self. __validate_resource_uri(uri) self.data = self.do_get(uri) - + return self @ensure_resource_client @@ -659,7 +661,7 @@ def create_report(self, timeout=-1): task, _ = self._connection.post(uri, {}) if not task: - raise HPOneViewException(RESOURCE_CLIENT_TASK_EXPECTED) + raise exceptions.HPOneViewException(RESOURCE_CLIENT_TASK_EXPECTED) task = self._task_monitor.get_completed_task(task, timeout) @@ -681,7 +683,7 @@ def build_subresource_uri(self, resource_id_or_uri=None, subresource_id_or_uri=N return subresource_id_or_uri else: if not resource_id_or_uri: - raise HPOneViewValueError(RESOURCE_ID_OR_URI_REQUIRED) + raise exceptions.HPOneViewValueError(RESOURCE_ID_OR_URI_REQUIRED) resource_uri = self.build_uri(resource_id_or_uri) @@ -696,7 +698,7 @@ def build_subresource_uri(self, resource_id_or_uri=None, subresource_id_or_uri=N def __validate_resource_uri(self, path): if self.URI not in path: logger.exception('Get by uri : unrecognized uri: (%s)' % path) - raise HPOneViewUnknownType(UNRECOGNIZED_URI) + raise exceptions.HPOneViewUnknownType(UNRECOGNIZED_URI) def __make_query_filter(self, filters): if isinstance(filters, list): @@ -714,17 +716,19 @@ def __get_members(self, mlist): def do_get(self, uri): """ - Method to support get requests of the resource + Method to support get requests of the resource """ - return self._connection.get(uri) + self.__validate_resource_uri(uri) + return self._connection.get(uri) def do_post(self, uri, resource, timeout, custom_headers): """ - Method to support post requests of the resource - """ + Method to support post requests of the resource + """ + self.__validate_resource_uri(uri) for field in self.EXCLUDE_FROM_REQUEST: - resource.pop(field, None) + resource.pop(field, None) task, entity = self._connection.post(uri, resource, custom_headers=custom_headers) @@ -737,6 +741,8 @@ def do_put(self, uri, resource, timeout, custom_headers): """ Method to support the put requests of the resource """ + self.__validate_resource_uri(uri) + task, body = self._connection.put(uri, resource, custom_headers=custom_headers) if not task: @@ -771,19 +777,14 @@ def __get_next_page(self, response, items, requested_count): def _merge_default_values(self): """ - Pick the default values for the api version and append that with resource data + Pick the default values for the api version and append that with resource data """ if self.DEFAULT_VALUES: api_version = str(self._connection._apiVersion) values = self.DEFAULT_VALUES.get(api_version, {}).copy() self.data.update(values) - def __validate_resource_uri(self, path): - if self.URI not in path: - logger.exception('Get by uri : unrecognized uri: (%s)' % path) - raise HPOneViewUnknownType(UNRECOGNIZED_URI) - - def merge_resources(resource_to_merge): + def merge_resources(self, resource_to_merge): """ Updates a copy of resource1 with resource2 values and returns the merged dictionary. @@ -843,7 +844,7 @@ def unavailable_method(self): """ Raise exception if method is not available for the resource """ - raise HPOneViewUnavailableMethod(UNAVAILABLE_METHOD) + raise exceptions.HPOneViewUnavailableMethod(UNAVAILABLE_METHOD) class ResourceClient(object): @@ -1014,7 +1015,7 @@ def delete(self, resource, force=False, timeout=-1, custom_headers=None): uri = resource['uri'] else: logger.exception(RESOURCE_CLIENT_UNKNOWN_OBJECT_TYPE) - raise HPOneViewUnknownType(RESOURCE_CLIENT_UNKNOWN_OBJECT_TYPE) + raise exceptions.HPOneViewUnknownType(RESOURCE_CLIENT_UNKNOWN_OBJECT_TYPE) else: uri = self.build_uri(resource) @@ -1448,7 +1449,7 @@ def create_report(self, uri, timeout=-1): task, _ = self._connection.post(uri, {}) if not task: - raise HPOneViewException(RESOURCE_CLIENT_TASK_EXPECTED) + raise exceptions.HPOneViewException(RESOURCE_CLIENT_TASK_EXPECTED) task = self._task_monitor.get_completed_task(task, timeout) @@ -1470,7 +1471,7 @@ def build_subresource_uri(self, resource_id_or_uri=None, subresource_id_or_uri=N return subresource_id_or_uri else: if not resource_id_or_uri: - raise HPOneViewValueError(RESOURCE_ID_OR_URI_REQUIRED) + raise exceptions.HPOneViewValueError(RESOURCE_ID_OR_URI_REQUIRED) resource_uri = self.build_uri(resource_id_or_uri) @@ -1499,7 +1500,7 @@ def download(self, uri, file_path): def __validate_resource_uri(self, path): if self._uri not in path: logger.exception('Get by uri : unrecognized uri: (%s)' % path) - raise HPOneViewUnknownType(UNRECOGNIZED_URI) + raise exceptions.HPOneViewUnknownType(UNRECOGNIZED_URI) def __make_query_filter(self, filters): if isinstance(filters, list): diff --git a/hpOneView/resources/servers/enclosures.py b/hpOneView/resources/servers/enclosures.py index 4b73f26d..a5bcea9e 100644 --- a/hpOneView/resources/servers/enclosures.py +++ b/hpOneView/resources/servers/enclosures.py @@ -29,8 +29,6 @@ standard_library.install_aliases() -from copy import deepcopy - from hpOneView.resources.resource import Resource, ensure_resource_client @@ -44,7 +42,6 @@ class Enclosures(Resource): def __init__(self, connection, options=None): super(Enclosures, self).__init__(connection, options) - def add(self, information, timeout=-1): """ C7000: @@ -65,14 +62,14 @@ def add(self, information, timeout=-1): dict: Enclosure. """ - self.EXCLUDE_FROM_REQUEST = ['name'] + self.EXCLUDE_FROM_REQUEST = ['name'] return self.create(data=information, timeout=timeout) - def remove(self): + def remove(self, force=False): """ Remove enclosure """ - self.delete() + self.delete(force=force) @ensure_resource_client def update_configuration(self, timeout=-1): diff --git a/tests/unit/resources/networking/test_fc_networks.py b/tests/unit/resources/networking/test_fc_networks.py index 98499a6a..0bb73d1a 100644 --- a/tests/unit/resources/networking/test_fc_networks.py +++ b/tests/unit/resources/networking/test_fc_networks.py @@ -27,25 +27,26 @@ from hpOneView.connection import connection from hpOneView.resources.networking.fc_networks import FcNetworks -from hpOneView.resources.resource import ResourceClient +from hpOneView.resources.resource import Resource class FcNetworksTest(unittest.TestCase): + def setUp(self): self.host = '127.0.0.1' self.connection = connection(self.host) self._fc_networks = FcNetworks(self.connection) - @mock.patch.object(ResourceClient, 'get_all') + @mock.patch.object(Resource, 'get_all') def test_get_all_called_once(self, mock_get_all): filter = 'name=TestName' sort = 'name:ascending' self._fc_networks.get_all(2, 500, filter, sort) - mock_get_all.assert_called_once_with(2, 500, filter=filter, sort=sort) + mock_get_all.assert_called_once_with(2, 500, filter, sort) - @mock.patch.object(ResourceClient, 'create') + @mock.patch.object(Resource, 'create') def test_create_should_use_given_values(self, mock_create): resource = { 'name': 'OneViewSDK Test FC Network', @@ -58,10 +59,9 @@ def test_create_should_use_given_values(self, mock_create): mock_create.return_value = {} self._fc_networks.create(resource, 30) - mock_create.assert_called_once_with(resource_rest_call, timeout=30, - default_values=self._fc_networks.DEFAULT_VALUES) + mock_create.assert_called_once_with(resource_rest_call, 30) - @mock.patch.object(ResourceClient, 'update') + @mock.patch.object(Resource, 'update') def test_update_should_use_given_values(self, mock_update): resource = { 'name': 'OneViewSDK Test FC Network', @@ -75,39 +75,38 @@ def test_update_should_use_given_values(self, mock_update): mock_update.return_value = {} self._fc_networks.update(resource, 60) - mock_update.assert_called_once_with(resource_rest_call, timeout=60, - default_values=self._fc_networks.DEFAULT_VALUES) + mock_update.assert_called_once_with(resource_rest_call, 60) - @mock.patch.object(ResourceClient, 'delete') + @mock.patch.object(Resource, 'delete') def test_delete_called_once(self, mock_delete): id = 'ad28cf21-8b15-4f92-bdcf-51cb2042db32' self._fc_networks.delete(id, force=False, timeout=-1) mock_delete.assert_called_once_with(id, force=False, timeout=-1) - @mock.patch.object(ResourceClient, 'get_by') + @mock.patch.object(Resource, 'get_by') def test_get_by_called_once(self, mock_get_by): self._fc_networks.get_by('name', 'OneViewSDK "Test FC Network') mock_get_by.assert_called_once_with('name', 'OneViewSDK "Test FC Network') - @mock.patch.object(ResourceClient, 'get') + @mock.patch.object(Resource, 'get') def test_get_called_once(self, mock_get): self._fc_networks.get('3518be0e-17c1-4189-8f81-83f3724f6155') mock_get.assert_called_once_with('3518be0e-17c1-4189-8f81-83f3724f6155') - @mock.patch.object(ResourceClient, 'get') + @mock.patch.object(Resource, 'get') def test_get_with_uri_called_once(self, mock_get): uri = '/rest/fc-networks/3518be0e-17c1-4189-8f81-83f3724f6155' self._fc_networks.get(uri) mock_get.assert_called_once_with(uri) - @mock.patch.object(ResourceClient, 'patch') + @mock.patch.object(Resource, 'patch') def test_patch_should_use_user_defined_values(self, mock_patch): mock_patch.return_value = {} self._fc_networks.patch('/rest/fake/fc123', 'replace', '/scopeUris', ['/rest/fake/scope123'], 1) mock_patch.assert_called_once_with('/rest/fake/fc123', 'replace', '/scopeUris', - ['/rest/fake/scope123'], timeout=1) + ['/rest/fake/scope123'], 1) diff --git a/tests/unit/resources/servers/test_enclosures.py b/tests/unit/resources/servers/test_enclosures.py index 6359dea6..c5aaa1c8 100644 --- a/tests/unit/resources/servers/test_enclosures.py +++ b/tests/unit/resources/servers/test_enclosures.py @@ -27,7 +27,7 @@ from hpOneView.connection import connection from hpOneView.resources.servers.enclosures import Enclosures -from hpOneView.resources.resource import ResourceClient +from hpOneView.resources.resource import Resource class EnclosuresTest(TestCase): @@ -35,30 +35,31 @@ def setUp(self): self.host = '127.0.0.1' self.connection = connection(self.host) self._enclosures = Enclosures(self.connection) + self._enclosures.data = {'uri': '/rest/enclosures/ad28cf21-8b15-4f92-bdcf-51cb2042db32'} - @mock.patch.object(ResourceClient, 'get_all') + @mock.patch.object(Resource, 'get_all') def test_get_all_called_once(self, mock_get_all): filter = 'name=TestName' sort = 'name:ascending' scope_uris = 'rest/scopes/cd237b60-09e2-45c4-829e-082e318a6d2a' - self._enclosures.get_all(2, 500, filter, sort, scope_uris) + self._enclosures.get_all(2, 500, filter, sort, scope_uris=scope_uris) - mock_get_all.assert_called_once_with(2, 500, filter=filter, sort=sort, scope_uris=scope_uris) + mock_get_all.assert_called_once_with(2, 500, filter, sort, scope_uris=scope_uris) - @mock.patch.object(ResourceClient, 'get_all') + @mock.patch.object(Resource, 'get_all') def test_get_all_called_once_with_default_values(self, mock_get_all): - self._enclosures.get_all() + self._enclosures.get_all(0, -1) - mock_get_all.assert_called_once_with(0, -1, filter='', sort='', scope_uris='') + mock_get_all.assert_called_once_with(0, -1) - @mock.patch.object(ResourceClient, 'get_by') + @mock.patch.object(Resource, 'get_by') def test_get_by_called_once(self, mock_get_by): self._enclosures.get_by('name', 'OneViewSDK-Test-Enclosure') mock_get_by.assert_called_once_with('name', 'OneViewSDK-Test-Enclosure') - @mock.patch.object(ResourceClient, 'create') + @mock.patch.object(Resource, 'create') def test_add_called_once(self, mock_create): information = { 'enclosureGroupUri': '/rest/enclosure-groups/id-enclosure-group' @@ -66,160 +67,103 @@ def test_add_called_once(self, mock_create): mock_create.return_value = {} self._enclosures.add(information) - mock_create.assert_called_once_with(information.copy(), timeout=-1) + mock_create.assert_called_once_with(data=information.copy(), timeout=-1) - @mock.patch.object(ResourceClient, 'get') + @mock.patch.object(Resource, 'get') def test_get_called_once(self, mock_get): self._enclosures.get('3518be0e-17c1-4189-8f81-83f3724f6155') mock_get.assert_called_once_with('3518be0e-17c1-4189-8f81-83f3724f6155') - @mock.patch.object(ResourceClient, 'get') + @mock.patch.object(Resource, 'get') def test_get_with_uri_called_once(self, mock_get): uri = '/rest/enclosures/3518be0e-17c1-4189-8f81-83f3724f6155' self._enclosures.get(uri) mock_get.assert_called_once_with(uri) - @mock.patch.object(ResourceClient, 'patch') + @mock.patch.object(Resource, 'patch') def test_patch_should_use_user_defined_values(self, mock_patch): mock_patch.return_value = {} self._enclosures.patch('123a53cz', 'replace', '/name', 'new_name', 1) - mock_patch.assert_called_once_with('123a53cz', 'replace', '/name', 'new_name', - custom_headers={'If-Match': '*'}, timeout=1) + mock_patch.assert_called_once_with('123a53cz', 'replace', '/name', 'new_name', 1) - @mock.patch.object(ResourceClient, 'delete') + @mock.patch.object(Resource, 'delete') def test_remove_called_once(self, mock_delete): - id = 'ad28cf21-8b15-4f92-bdcf-51cb2042db32' - self._enclosures.remove(id, force=False) + self._enclosures.remove(force=False) - mock_delete.assert_called_once_with(id, force=False, timeout=-1) + mock_delete.assert_called_once_with(force=False) - @mock.patch.object(ResourceClient, 'delete') + @mock.patch.object(Resource, 'delete') def test_remove_called_once_with_force(self, mock_delete): - id = 'ad28cf21-8b15-4f92-bdcf-51cb2042db32' - self._enclosures.remove(id, force=True) + self._enclosures.remove(force=True) - mock_delete.assert_called_once_with(id, force=True, timeout=-1) + mock_delete.assert_called_once_with(force=True) - @mock.patch.object(ResourceClient, 'update_with_zero_body') - def test_update_configuration_by_uri(self, mock_update_with_zero_body): - uri_enclosure = '/rest/enclosures/ad28cf21-8b15-4f92-bdcf-51cb2042db32' - uri_rest_call = '/rest/enclosures/ad28cf21-8b15-4f92-bdcf-51cb2042db32/configuration' + @mock.patch.object(Resource, 'load_resource') + @mock.patch.object(Resource, 'update_with_zero_body') + def test_update_configuration_by_uri(self, mock_update_with_zero_body, load_resource): + self._enclosures.update_configuration() - self._enclosures.update_configuration(uri_enclosure) + mock_update_with_zero_body.assert_called_once_with(path='/configuration', timeout=-1) - mock_update_with_zero_body.assert_called_once_with(uri_rest_call, timeout=-1) - - @mock.patch.object(ResourceClient, 'update_with_zero_body') - def test_update_configuration_by_id(self, mock_update_with_zero_body): - id_enclosure = 'ad28cf21-8b15-4f92-bdcf-51cb2042db32' - uri_rest_call = '/rest/enclosures/ad28cf21-8b15-4f92-bdcf-51cb2042db32/configuration' - - self._enclosures.update_configuration(id_enclosure) - - mock_update_with_zero_body.assert_called_once_with(uri_rest_call, timeout=-1) - - @mock.patch.object(ResourceClient, 'get') - def test_get_environmental_configuration_by_uri(self, mock_get): - uri_enclosure = '/rest/enclosures/ad28cf21-8b15-4f92-bdcf-51cb2042db32' + @mock.patch.object(Resource, 'load_resource') + @mock.patch.object(Resource, 'do_get') + def test_get_environmental_configuration_by_uri(self, mock_get, load_resource): uri_rest_call = '/rest/enclosures/ad28cf21-8b15-4f92-bdcf-51cb2042db32/environmentalConfiguration' - self._enclosures.get_environmental_configuration(uri_enclosure) + self._enclosures.get_environmental_configuration() mock_get.assert_called_once_with(uri_rest_call) - @mock.patch.object(ResourceClient, 'get') - def test_get_environmental_configuration_by_id(self, mock_get): - id_enclosure = 'ad28cf21-8b15-4f92-bdcf-51cb2042db32' + @mock.patch.object(Resource, 'load_resource') + @mock.patch.object(Resource, 'do_get') + def test_get_environmental_configuration_by_id(self, mock_get, load_resource): uri_rest_call = '/rest/enclosures/ad28cf21-8b15-4f92-bdcf-51cb2042db32/environmentalConfiguration' - self._enclosures.get_environmental_configuration(id_enclosure) + self._enclosures.get_environmental_configuration() mock_get.assert_called_once_with(uri_rest_call) - @mock.patch.object(ResourceClient, 'update') - def test_update_environmental_configuration_by_uri(self, mock_update): - uri_enclosure = '/rest/enclosures/ad28cf21-8b15-4f92-bdcf-51cb2042db32' - uri_rest_call = '/rest/enclosures/ad28cf21-8b15-4f92-bdcf-51cb2042db32/environmentalConfiguration' - configuration = {"calibratedMaxPower": 2500} - configuration_rest_call = configuration.copy() - - self._enclosures.update_environmental_configuration(uri_enclosure, configuration) - - mock_update.assert_called_once_with(configuration_rest_call, uri=uri_rest_call, timeout=-1) - - @mock.patch.object(ResourceClient, 'update') - def test_update_environmental_configuration_by_id(self, mock_update): - id_enclosure = 'ad28cf21-8b15-4f92-bdcf-51cb2042db32' + @mock.patch.object(Resource, 'load_resource') + @mock.patch.object(Resource, 'do_put') + def test_update_environmental_configuration_by_uri(self, mock_put, load_resource): uri_rest_call = '/rest/enclosures/ad28cf21-8b15-4f92-bdcf-51cb2042db32/environmentalConfiguration' configuration = {"calibratedMaxPower": 2500} configuration_rest_call = configuration.copy() - self._enclosures.update_environmental_configuration(id_enclosure, configuration) - - mock_update.assert_called_once_with(configuration_rest_call, uri=uri_rest_call, timeout=-1) - - @mock.patch.object(ResourceClient, 'update') - def test_refresh_state_by_uri(self, mock_update): - uri_enclosure = '/rest/enclosures/ad28cf21-8b15-4f92-bdcf-51cb2042db32' - uri_rest_call = '/rest/enclosures/ad28cf21-8b15-4f92-bdcf-51cb2042db32/refreshState' - configuration = {"refreshState": "RefreshPending"} - configuration_rest_call = configuration.copy() - - self._enclosures.refresh_state(uri_enclosure, configuration) + self._enclosures.update_environmental_configuration(configuration, timeout=-1) - mock_update.assert_called_once_with(configuration_rest_call, uri=uri_rest_call, timeout=-1) + mock_put.assert_called_once_with(uri_rest_call, configuration_rest_call, -1, None) - @mock.patch.object(ResourceClient, 'update') - def test_refresh_state_by_id(self, mock_update): - id_enclosure = 'ad28cf21-8b15-4f92-bdcf-51cb2042db32' + @mock.patch.object(Resource, 'load_resource') + @mock.patch.object(Resource, 'do_put') + def test_refresh_state_by_uri(self, mock_put, load_resource): uri_rest_call = '/rest/enclosures/ad28cf21-8b15-4f92-bdcf-51cb2042db32/refreshState' configuration = {"refreshState": "RefreshPending"} configuration_rest_call = configuration.copy() - self._enclosures.refresh_state(id_enclosure, configuration) + self._enclosures.refresh_state(configuration) + mock_put.assert_called_once_with(uri_rest_call, configuration_rest_call, -1, None) - mock_update.assert_called_once_with(configuration_rest_call, uri=uri_rest_call, timeout=-1) - - @mock.patch.object(ResourceClient, 'get') - def test_get_script_by_uri(self, mock_get): - uri_enclosure = '/rest/enclosures/ad28cf21-8b15-4f92-bdcf-51cb2042db32' - uri_rest_call = '/rest/enclosures/ad28cf21-8b15-4f92-bdcf-51cb2042db32/script' - - self._enclosures.get_script(uri_enclosure) - - mock_get.assert_called_once_with(uri_rest_call) - - @mock.patch.object(ResourceClient, 'get') - def test_get_script_by_id(self, mock_get): - id_enclosure = 'ad28cf21-8b15-4f92-bdcf-51cb2042db32' + @mock.patch.object(Resource, 'load_resource') + @mock.patch.object(Resource, 'do_get') + def test_get_script_by_uri(self, mock_get, load_resource): uri_rest_call = '/rest/enclosures/ad28cf21-8b15-4f92-bdcf-51cb2042db32/script' - self._enclosures.get_script(id_enclosure) - - mock_get.assert_called_once_with(uri_rest_call) - - @mock.patch.object(ResourceClient, 'get') - def test_get_sso_by_uri(self, mock_get): - uri_enclosure = '/rest/enclosures/ad28cf21-8b15-4f92-bdcf-51cb2042db32' - uri_rest_call = '/rest/enclosures/ad28cf21-8b15-4f92-bdcf-51cb2042db32/sso?role=Active' - - self._enclosures.get_sso(uri_enclosure, 'Active') - + self._enclosures.get_script() mock_get.assert_called_once_with(uri_rest_call) - @mock.patch.object(ResourceClient, 'get') - def test_get_sso_by_id(self, mock_get): - id_enclosure = 'ad28cf21-8b15-4f92-bdcf-51cb2042db32' + @mock.patch.object(Resource, 'load_resource') + @mock.patch.object(Resource, 'do_get') + def test_get_sso_by_uri(self, mock_get, load_resource): uri_rest_call = '/rest/enclosures/ad28cf21-8b15-4f92-bdcf-51cb2042db32/sso?role=Active' - self._enclosures.get_sso(id_enclosure, 'Active') - + self._enclosures.get_sso('Active') mock_get.assert_called_once_with(uri_rest_call) - @mock.patch.object(ResourceClient, 'get_utilization') + @mock.patch.object(Resource, 'get_utilization') def test_get_utilization_with_all_args(self, mock_get_utilization): self._enclosures.get_utilization('09USE7335NW3', fields='AmbientTemperature,AveragePower,PeakPower', filter='startDate=2016-05-30T03:29:42.361Z', @@ -229,23 +173,16 @@ def test_get_utilization_with_all_args(self, mock_get_utilization): filter='startDate=2016-05-30T03:29:42.361Z', refresh=True, view='day') - @mock.patch.object(ResourceClient, 'get_utilization') - def test_get_utilization_by_id_with_defaults(self, mock_get): - self._enclosures.get_utilization('09USE7335NW3') - - mock_get.assert_called_once_with('09USE7335NW3', fields=None, filter=None, refresh=False, view=None) - - @mock.patch.object(ResourceClient, 'get_utilization') + @mock.patch.object(Resource, 'get_utilization') def test_get_utilization_by_uri_with_defaults(self, mock_get): self._enclosures.get_utilization('/rest/enclosures/09USE7335NW3') - mock_get.assert_called_once_with('/rest/enclosures/09USE7335NW3', - fields=None, filter=None, refresh=False, view=None) + mock_get.assert_called_once_with('/rest/enclosures/09USE7335NW3') - @mock.patch.object(ResourceClient, 'create') - def test_generate_csr(self, mock_create): + @mock.patch.object(Resource, 'load_resource') + @mock.patch.object(Resource, 'do_post') + def test_generate_csr(self, mock_post, load_resource): bay_number = 1 - id_enclosure = 'ad28cf21-8b15-4f92-bdcf-51cb2042db32' uri_rest_call = '/rest/enclosures/ad28cf21-8b15-4f92-bdcf-51cb2042db32/https/certificaterequest?bayNumber=%d' % (bay_number) csr_data = { 'type': 'CertificateDtoV2', @@ -258,24 +195,23 @@ def test_generate_csr(self, mock_create): } headers = {'Content-Type': 'application/json'} - self._enclosures.generate_csr(csr_data, id_enclosure, bay_number=bay_number) + self._enclosures.generate_csr(csr_data, bay_number=bay_number) - mock_create.assert_called_once_with(csr_data, uri=uri_rest_call, custom_headers=headers) + mock_post.assert_called_once_with(uri_rest_call, csr_data, -1, headers) - @mock.patch.object(ResourceClient, 'get') - def test_get_csr(self, mock_get): + @mock.patch.object(Resource, 'load_resource') + @mock.patch.object(Resource, 'do_get') + def test_get_csr(self, mock_get, load_resource): bay_number = 1 - id_enclosure = 'ad28cf21-8b15-4f92-bdcf-51cb2042db32' uri_rest_call = '/rest/enclosures/ad28cf21-8b15-4f92-bdcf-51cb2042db32/https/certificaterequest?bayNumber=%d' % (bay_number) - self._enclosures.get_csr(id_enclosure, bay_number=bay_number) - + self._enclosures.get_csr(bay_number=bay_number) mock_get.assert_called_once_with(uri_rest_call) - @mock.patch.object(ResourceClient, 'update') - def test_import_certificate(self, mock_update): + @mock.patch.object(Resource, 'load_resource') + @mock.patch.object(Resource, 'do_put') + def test_import_certificate(self, mock_put, load_resource): bay_number = 1 - id_enclosure = 'ad28cf21-8b15-4f92-bdcf-51cb2042db32' uri_rest_call = '/rest/enclosures/ad28cf21-8b15-4f92-bdcf-51cb2042db32/https/certificaterequest?bayNumber=%d' % (bay_number) certificate_data = { 'type': 'CertificateDataV2', @@ -283,6 +219,6 @@ def test_import_certificate(self, mock_update): } headers = {'Content-Type': 'application/json'} - self._enclosures.import_certificate(certificate_data, id_enclosure, bay_number=bay_number) + self._enclosures.import_certificate(certificate_data, bay_number=bay_number) - mock_update.assert_called_once_with(certificate_data, uri=uri_rest_call, custom_headers=headers) + mock_put.assert_called_once_with(uri_rest_call, certificate_data, -1, headers) diff --git a/tests/unit/test_oneview_client.py b/tests/unit/test_oneview_client.py index f3ae5b7f..21dfb6c4 100755 --- a/tests/unit/test_oneview_client.py +++ b/tests/unit/test_oneview_client.py @@ -387,10 +387,6 @@ def test_fc_networks_has_right_type(self): def test_fc_networks_has_value(self): self.assertIsNotNone(self._oneview.fc_networks) - def test_lazy_loading_fc_networks(self): - fcn = self._oneview.fc_networks - self.assertEqual(fcn, self._oneview.fc_networks) - def test_connection_type(self): self.assertIsInstance(self._oneview.connection, connection) @@ -438,10 +434,6 @@ def test_lazy_loading_metric_streaming(self): metric = self._oneview.metric_streaming self.assertEqual(metric, self._oneview.metric_streaming) - def test_lazy_loading_enclosures(self): - enclosures = self._oneview.enclosures - self.assertEqual(enclosures, self._oneview.enclosures) - def test_lazy_loading_switches(self): switches = self._oneview.switches self.assertEqual(switches, self._oneview.switches) From bc9948815af152c8145c0f0ef459c5595a8387ec Mon Sep 17 00:00:00 2001 From: Sijeesh Kattumunda Date: Tue, 14 Aug 2018 02:58:11 -0600 Subject: [PATCH 06/11] Style fixes and document updation --- examples/enclosures.py | 2 +- hpOneView/resources/resource.py | 244 +++++++++--------- hpOneView/resources/servers/enclosures.py | 9 - .../resources/networking/test_fc_networks.py | 18 +- .../unit/resources/servers/test_enclosures.py | 22 +- 5 files changed, 123 insertions(+), 172 deletions(-) diff --git a/examples/enclosures.py b/examples/enclosures.py index f2ffeec9..750fd9f9 100644 --- a/examples/enclosures.py +++ b/examples/enclosures.py @@ -70,7 +70,7 @@ # Get the reosurce by name enclosure = oneview_client.enclosures.get_by_name(options['name']) -# Perform a patch operation on the enclosure, replacing the name of the enclosure +# Perform a patch operation on the enclosure, replacing name of the enclosure enclosure_name = "Encl1-Updated-11" print("Updating the enclosure to have a name of " + enclosure_name) enclosure.patch('replace', '/name', enclosure_name) diff --git a/hpOneView/resources/resource.py b/hpOneView/resources/resource.py index e5d9851a..eb0cd86a 100755 --- a/hpOneView/resources/resource.py +++ b/hpOneView/resources/resource.py @@ -55,19 +55,19 @@ class EnsureResourceClient(object): - """ - Decorator class to make sure the resource client - """ - def __init__(self, func): - update_wrapper(self, func) - self.func = func + """ + Decorator class to sync resource data with server + """ + def __init__(self, func): + update_wrapper(self, func) + self.func = func - def __get__(self, obj, objtype): - return partial(self.__call__, obj) + def __get__(self, obj, objtype): + return partial(self.__call__, obj) - def __call__(self, obj, *args, **kwargs): - obj.load_resource() - return self.func(obj, *args, **kwargs) + def __call__(self, obj, *args, **kwargs): + obj.load_resource() + return self.func(obj, *args, **kwargs) # Decorator to ensure the resource client @@ -76,7 +76,7 @@ def __call__(self, obj, *args, **kwargs): class Resource(object): """ - This class implements common functions for HpOneView API rest + Base class for OneView resources """ # Base URI for the rest calls URI = '/rest' @@ -84,19 +84,52 @@ class Resource(object): # Unique identifiers to query the resource UNIQUE_IDENTIFIERS = ['uri', 'name'] - # Add fields to be removed from the request body of a post request + # Add fields to be removed from the request body EXCLUDE_FROM_REQUEST = [] # Default values required for the api versions DEFAULT_VALUES = {} def __init__(self, connection, data=None): - + # OneView connection object self._connection = connection + + # Resource data self.data = data if data else {} + + # Merge resoure data with the default values self._merge_default_values() + self._task_monitor = TaskMonitor(connection) + def load_resource(self): + """ + Retrieve data from OneView and update resource data + """ + # Check for unique identifier in the resource data + if not any(key in self.data for key in self.UNIQUE_IDENTIFIERS): + raise exceptions.HPOneViewMissingUniqueIdentifiers(MISSING_UNIQUE_IDENTIFIERS) + + resource_data = None + + if 'uri' in self.UNIQUE_IDENTIFIERS and self.data.get('uri'): + uri = self.data['uri'] + resource_data = self.do_get(uri) + else: + for identifier in self.UNIQUE_IDENTIFIERS: + identifier_value = self.data.get(identifier) + + if identifier_value: + result = self.get_by(identifier, identifier_value) + if result and isinstance(result, list): + resource_data = result[0] + break + + if not resource_data: + raise exceptions.HPOneViewResourceNotFound(RESOURCE_DOES_NOT_EXISTS) + + self.data.update(resource_data) + def build_query_uri(self, start=0, count=-1, filter='', query='', sort='', view='', fields='', uri=None, scope_uris=''): """ Builds the URI given the parameters. @@ -171,42 +204,6 @@ def build_query_uri(self, start=0, count=-1, filter='', query='', sort='', view= view, fields, scope_uris) return uri - def load_resource(self): - """ - Retrieve data from OneView and update resource data - """ - # Check for unique identifier in the resource data - if not any(key in self.data for key in self.UNIQUE_IDENTIFIERS): - raise exceptions.HPOneViewMissingUniqueIdentifiers(MISSING_UNIQUE_IDENTIFIERS) - - resource_data = None - - if 'uri' in self.UNIQUE_IDENTIFIERS and self.data.get('uri'): - uri = self.data['uri'] - if self.URI in uri: - resource_data = self.do_get(uri) - else: - for identifier in self.UNIQUE_IDENTIFIERS: - identifier_value = self.data.get(identifier) - - if identifier_value: - result = self.get_by(identifier, identifier_value) - if result and isinstance(result, list): - resource_data = result[0] - break - - if not resource_data: - raise exceptions.HPOneViewResourceNotFound(RESOURCE_DOES_NOT_EXISTS) - - self.data.update(resource_data) - - @ensure_resource_client - def get(self): - """ - Return resource data - """ - return self - def get_all(self, start=0, count=-1, filter='', query='', sort='', view='', fields='', scope_uris=''): """ Gets all items according with the given arguments. @@ -336,7 +333,9 @@ def delete_all(self, filter, force=False, timeout=-1): @ensure_resource_client def delete(self, force=False, timeout=-1, custom_headers=None): - + """ + Deletes current resource + """ if self.data and 'uri' in self.data and self.data['uri']: uri = self.data['uri'] else: @@ -364,7 +363,8 @@ def get_schema(self): (self.URI, self.URI)) return self._connection.get(self.URI + '/schema') - def get_collection(self, id_or_uri, filter=''): + @ensure_resource_client + def get_collection(self, filter=''): """ Retrieves a collection of resources. @@ -374,7 +374,6 @@ def get_collection(self, id_or_uri, filter=''): Optional filtering criteria may be specified. Args: - id_or_uri: Can be either the resource ID or the resource URI. filter (list or str): General filter/query string. Returns: @@ -384,9 +383,10 @@ def get_collection(self, id_or_uri, filter=''): filter = self.__make_query_filter(filter) filter = "?" + filter[1:] - uri = "{uri}{filter}".format(uri=self.build_uri(id_or_uri), filter=filter) + uri = "{uri}{filter}".format(uri=self.data['uri'], filter=filter) logger.debug('Get resource collection (uri = %s)' % uri) response = self._connection.get(uri) + return self.__get_members(response) @ensure_resource_client @@ -395,8 +395,6 @@ def update_with_zero_body(self, path=None, timeout=-1, custom_headers=None): Makes a PUT request to update a resource when no request body is required. Args: - uri: - Can be either the resource ID or the resource URI. timeout: Timeout in seconds. Wait for task completion by default. The timeout does not abort the operation in OneView; it just stops waiting for its completion. @@ -519,7 +517,7 @@ def get_by(self, field, value): filter = "\"{0}='{1}'\"".format(field, value) results = self.get_all(filter=filter) - print('get all', results) + # Workaround when the OneView filter does not work, it will filter again if "." not in field: # This filter only work for the first level @@ -559,8 +557,6 @@ def get_utilization(self, fields=None, filter=None, refresh=False, view=None): Retrieves historical utilization data for the specified resource, metrics, and time span. Args: - id_or_uri: - Resource identification fields: Name of the supported metric(s) to be retrieved in the format METRIC[,METRIC]... If unspecified, all metrics supported are returned. @@ -648,7 +644,6 @@ def create_report(self, timeout=-1): Creates a report and returns the output. Args: - uri: URI timeout: Timeout in seconds. Wait for task completion by default. The timeout does not abort the operation in OneView; it just stops waiting for its completion. @@ -667,17 +662,6 @@ def create_report(self, timeout=-1): return task['taskOutput'] - def build_uri(self, id_or_uri): - if not id_or_uri: - logger.exception(RESOURCE_CLIENT_INVALID_ID) - raise ValueError(RESOURCE_CLIENT_INVALID_ID) - - if "/" in id_or_uri: - self.__validate_resource_uri(id_or_uri) - return id_or_uri - else: - return self.URI + "/" + id_or_uri - def build_subresource_uri(self, resource_id_or_uri=None, subresource_id_or_uri=None, subresource_path=''): if subresource_id_or_uri and "/" in subresource_id_or_uri: return subresource_id_or_uri @@ -695,25 +679,6 @@ def build_subresource_uri(self, resource_id_or_uri=None, subresource_id_or_uri=N return uri - def __validate_resource_uri(self, path): - if self.URI not in path: - logger.exception('Get by uri : unrecognized uri: (%s)' % path) - raise exceptions.HPOneViewUnknownType(UNRECOGNIZED_URI) - - def __make_query_filter(self, filters): - if isinstance(filters, list): - formated_filter = "&filter=".join(quote(f) for f in filters) - else: - formated_filter = quote(filters) - - return "&filter=" + formated_filter - - def __get_members(self, mlist): - if mlist and 'members' in mlist and mlist['members']: - return mlist['members'] - else: - return [] - def do_get(self, uri): """ Method to support get requests of the resource @@ -739,7 +704,7 @@ def do_post(self, uri, resource, timeout, custom_headers): def do_put(self, uri, resource, timeout, custom_headers): """ - Method to support the put requests of the resource + Method to support put requests of the resource """ self.__validate_resource_uri(uri) @@ -750,47 +715,12 @@ def do_put(self, uri, resource, timeout, custom_headers): return self._task_monitor.wait_for_task(task, timeout) - def __do_requests_to_getall(self, uri, requested_count): - items = [] - - while uri: - logger.debug('Making HTTP request to get all resources. Uri: {0}'.format(uri)) - response = self._connection.get(uri) - members = self.__get_members(response) - items += members - - logger.debug("Response getAll: nextPageUri = {0}, members list length: {1}".format(uri, str(len(members)))) - uri = self.__get_next_page(response, items, requested_count) - - logger.debug('Total # of members found = {0}'.format(str(len(items)))) - return items - - def __get_next_page(self, response, items, requested_count): - next_page_is_empty = response.get('nextPageUri') is None - has_different_next_page = not response.get('uri') == response.get('nextPageUri') - has_next_page = not next_page_is_empty and has_different_next_page - - if len(items) >= requested_count and requested_count != -1: - return None - - return response.get('nextPageUri') if has_next_page else None - - def _merge_default_values(self): - """ - Pick the default values for the api version and append that with resource data - """ - if self.DEFAULT_VALUES: - api_version = str(self._connection._apiVersion) - values = self.DEFAULT_VALUES.get(api_version, {}).copy() - self.data.update(values) - def merge_resources(self, resource_to_merge): """ Updates a copy of resource1 with resource2 values and returns the merged dictionary. Args: - resource1: original resource - resource2: resource to update resource1 + resource_to_merge: data to update resource Returns: dict: merged resource @@ -818,6 +748,8 @@ def upload(self, file_path, uri=None, timeout=-1): if not uri: uri = self.URI + self.__validate_resource_uri(uri) + upload_file_name = os.path.basename(file_path) task, entity = self._connection.post_multipart_with_response_handling(uri, file_path, upload_file_name) @@ -837,10 +769,66 @@ def download(self, uri, file_path): Returns: bool: Indicates if the file was successfully downloaded. """ + self.__validate_resource_uri(uri) + with open(file_path, 'wb') as file: return self._connection.download_to_stream(file, uri) - def unavailable_method(self): + def __validate_resource_uri(self, path): + if self.URI not in path: + logger.exception('Get by uri : unrecognized uri: (%s)' % path) + raise exceptions.HPOneViewUnknownType(UNRECOGNIZED_URI) + + def __make_query_filter(self, filters): + if isinstance(filters, list): + formated_filter = "&filter=".join(quote(f) for f in filters) + else: + formated_filter = quote(filters) + + return "&filter=" + formated_filter + + def __get_members(self, mlist): + if mlist and 'members' in mlist and mlist['members']: + return mlist['members'] + else: + return [] + + def __do_requests_to_getall(self, uri, requested_count): + items = [] + + while uri: + logger.debug('Making HTTP request to get all resources. Uri: {0}'.format(uri)) + response = self._connection.get(uri) + members = self.__get_members(response) + items += members + + logger.debug("Response getAll: nextPageUri = {0}, members list length: {1}".format(uri, str(len(members)))) + uri = self.__get_next_page(response, items, requested_count) + + logger.debug('Total # of members found = {0}'.format(str(len(items)))) + return items + + def __get_next_page(self, response, items, requested_count): + next_page_is_empty = response.get('nextPageUri') is None + has_different_next_page = not response.get('uri') == response.get('nextPageUri') + has_next_page = not next_page_is_empty and has_different_next_page + + if len(items) >= requested_count and requested_count != -1: + return None + + return response.get('nextPageUri') if has_next_page else None + + def _merge_default_values(self): + """ + Pick the default values for the api version and append that with resource data + """ + if self.DEFAULT_VALUES: + api_version = str(self._connection._apiVersion) + values = self.DEFAULT_VALUES.get(api_version, {}).copy() + self.data.update(values) + + @staticmethod + def unavailable_method(): """ Raise exception if method is not available for the resource """ diff --git a/hpOneView/resources/servers/enclosures.py b/hpOneView/resources/servers/enclosures.py index a5bcea9e..7e9a570e 100644 --- a/hpOneView/resources/servers/enclosures.py +++ b/hpOneView/resources/servers/enclosures.py @@ -93,9 +93,6 @@ def get_environmental_configuration(self): Gets the settings that describe the environmental configuration (supported feature set, calibrated minimum & maximum power, location & dimensions, ...) of the enclosure resource. - Args: - id_or_uri: Can be either the resource ID or the resource URI. - Returns: Settings that describe the environmental configuration. """ @@ -108,7 +105,6 @@ def update_environmental_configuration(self, configuration, timeout=-1): Sets the calibrated max power of an unmanaged or unsupported enclosure. Args: - id_or_uri: Can be either the resource ID or the resource URI. configuration: Configuration timeout: Timeout in seconds. Wait for task completion by default. The timeout does not abort the operation in OneView; it just stops waiting for its completion. @@ -143,9 +139,6 @@ def get_script(self): """ Gets the script of the enclosure. - Args: - id_or_uri: Can be either the resource ID or the resource URI. - Returns: Enclosure script. """ @@ -174,7 +167,6 @@ def generate_csr(self, csr_data, bay_number=None): Args: csr_data: Dictionary with csr details. - id_or_uri: Can be either the resource ID or the resource URI. bay_number: OA from which the CSR should be generated. Returns: @@ -195,7 +187,6 @@ def get_csr(self, bay_number=None): Get an enclosure's Certificate Signing Request (CSR) that was generated by previous POST to the same URI. Args: - id_or_uri: Can be either the resource ID or the resource URI. bay_number: OA to retrieve the previously generated CSR. Returns: diff --git a/tests/unit/resources/networking/test_fc_networks.py b/tests/unit/resources/networking/test_fc_networks.py index 0bb73d1a..d51ba201 100644 --- a/tests/unit/resources/networking/test_fc_networks.py +++ b/tests/unit/resources/networking/test_fc_networks.py @@ -43,7 +43,6 @@ def test_get_all_called_once(self, mock_get_all): sort = 'name:ascending' self._fc_networks.get_all(2, 500, filter, sort) - mock_get_all.assert_called_once_with(2, 500, filter, sort) @mock.patch.object(Resource, 'create') @@ -79,28 +78,19 @@ def test_update_should_use_given_values(self, mock_update): @mock.patch.object(Resource, 'delete') def test_delete_called_once(self, mock_delete): - id = 'ad28cf21-8b15-4f92-bdcf-51cb2042db32' - self._fc_networks.delete(id, force=False, timeout=-1) - - mock_delete.assert_called_once_with(id, force=False, timeout=-1) + self._fc_networks.delete(force=False, timeout=-1) + mock_delete.assert_called_once_with(force=False, timeout=-1) @mock.patch.object(Resource, 'get_by') def test_get_by_called_once(self, mock_get_by): self._fc_networks.get_by('name', 'OneViewSDK "Test FC Network') - mock_get_by.assert_called_once_with('name', 'OneViewSDK "Test FC Network') - @mock.patch.object(Resource, 'get') - def test_get_called_once(self, mock_get): - self._fc_networks.get('3518be0e-17c1-4189-8f81-83f3724f6155') - - mock_get.assert_called_once_with('3518be0e-17c1-4189-8f81-83f3724f6155') - - @mock.patch.object(Resource, 'get') + @mock.patch.object(Resource, 'get_by_uri') def test_get_with_uri_called_once(self, mock_get): uri = '/rest/fc-networks/3518be0e-17c1-4189-8f81-83f3724f6155' - self._fc_networks.get(uri) + self._fc_networks.get_by_uri(uri) mock_get.assert_called_once_with(uri) @mock.patch.object(Resource, 'patch') diff --git a/tests/unit/resources/servers/test_enclosures.py b/tests/unit/resources/servers/test_enclosures.py index c5aaa1c8..6163e8ee 100644 --- a/tests/unit/resources/servers/test_enclosures.py +++ b/tests/unit/resources/servers/test_enclosures.py @@ -44,19 +44,16 @@ def test_get_all_called_once(self, mock_get_all): scope_uris = 'rest/scopes/cd237b60-09e2-45c4-829e-082e318a6d2a' self._enclosures.get_all(2, 500, filter, sort, scope_uris=scope_uris) - mock_get_all.assert_called_once_with(2, 500, filter, sort, scope_uris=scope_uris) @mock.patch.object(Resource, 'get_all') def test_get_all_called_once_with_default_values(self, mock_get_all): self._enclosures.get_all(0, -1) - mock_get_all.assert_called_once_with(0, -1) @mock.patch.object(Resource, 'get_by') def test_get_by_called_once(self, mock_get_by): self._enclosures.get_by('name', 'OneViewSDK-Test-Enclosure') - mock_get_by.assert_called_once_with('name', 'OneViewSDK-Test-Enclosure') @mock.patch.object(Resource, 'create') @@ -69,17 +66,11 @@ def test_add_called_once(self, mock_create): self._enclosures.add(information) mock_create.assert_called_once_with(data=information.copy(), timeout=-1) - @mock.patch.object(Resource, 'get') - def test_get_called_once(self, mock_get): - self._enclosures.get('3518be0e-17c1-4189-8f81-83f3724f6155') - - mock_get.assert_called_once_with('3518be0e-17c1-4189-8f81-83f3724f6155') - - @mock.patch.object(Resource, 'get') + @mock.patch.object(Resource, 'get_by_uri') def test_get_with_uri_called_once(self, mock_get): uri = '/rest/enclosures/3518be0e-17c1-4189-8f81-83f3724f6155' - self._enclosures.get(uri) + self._enclosures.get_by_uri(uri) mock_get.assert_called_once_with(uri) @mock.patch.object(Resource, 'patch') @@ -92,20 +83,17 @@ def test_patch_should_use_user_defined_values(self, mock_patch): @mock.patch.object(Resource, 'delete') def test_remove_called_once(self, mock_delete): self._enclosures.remove(force=False) - mock_delete.assert_called_once_with(force=False) @mock.patch.object(Resource, 'delete') def test_remove_called_once_with_force(self, mock_delete): self._enclosures.remove(force=True) - mock_delete.assert_called_once_with(force=True) @mock.patch.object(Resource, 'load_resource') @mock.patch.object(Resource, 'update_with_zero_body') def test_update_configuration_by_uri(self, mock_update_with_zero_body, load_resource): self._enclosures.update_configuration() - mock_update_with_zero_body.assert_called_once_with(path='/configuration', timeout=-1) @mock.patch.object(Resource, 'load_resource') @@ -114,7 +102,6 @@ def test_get_environmental_configuration_by_uri(self, mock_get, load_resource): uri_rest_call = '/rest/enclosures/ad28cf21-8b15-4f92-bdcf-51cb2042db32/environmentalConfiguration' self._enclosures.get_environmental_configuration() - mock_get.assert_called_once_with(uri_rest_call) @mock.patch.object(Resource, 'load_resource') @@ -123,7 +110,6 @@ def test_get_environmental_configuration_by_id(self, mock_get, load_resource): uri_rest_call = '/rest/enclosures/ad28cf21-8b15-4f92-bdcf-51cb2042db32/environmentalConfiguration' self._enclosures.get_environmental_configuration() - mock_get.assert_called_once_with(uri_rest_call) @mock.patch.object(Resource, 'load_resource') @@ -134,7 +120,6 @@ def test_update_environmental_configuration_by_uri(self, mock_put, load_resource configuration_rest_call = configuration.copy() self._enclosures.update_environmental_configuration(configuration, timeout=-1) - mock_put.assert_called_once_with(uri_rest_call, configuration_rest_call, -1, None) @mock.patch.object(Resource, 'load_resource') @@ -176,7 +161,6 @@ def test_get_utilization_with_all_args(self, mock_get_utilization): @mock.patch.object(Resource, 'get_utilization') def test_get_utilization_by_uri_with_defaults(self, mock_get): self._enclosures.get_utilization('/rest/enclosures/09USE7335NW3') - mock_get.assert_called_once_with('/rest/enclosures/09USE7335NW3') @mock.patch.object(Resource, 'load_resource') @@ -196,7 +180,6 @@ def test_generate_csr(self, mock_post, load_resource): headers = {'Content-Type': 'application/json'} self._enclosures.generate_csr(csr_data, bay_number=bay_number) - mock_post.assert_called_once_with(uri_rest_call, csr_data, -1, headers) @mock.patch.object(Resource, 'load_resource') @@ -220,5 +203,4 @@ def test_import_certificate(self, mock_put, load_resource): headers = {'Content-Type': 'application/json'} self._enclosures.import_certificate(certificate_data, bay_number=bay_number) - mock_put.assert_called_once_with(uri_rest_call, certificate_data, -1, headers) From 46748d03a9554bcbcc147717823bf1b0a591d727 Mon Sep 17 00:00:00 2001 From: Sijeesh Kattumunda Date: Tue, 14 Aug 2018 03:40:03 -0600 Subject: [PATCH 07/11] added build uri --- hpOneView/resources/resource.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/hpOneView/resources/resource.py b/hpOneView/resources/resource.py index eb0cd86a..afdf27a8 100755 --- a/hpOneView/resources/resource.py +++ b/hpOneView/resources/resource.py @@ -662,6 +662,17 @@ def create_report(self, timeout=-1): return task['taskOutput'] + def build_uri(self, id_or_uri): + if not id_or_uri: + logger.exception(RESOURCE_CLIENT_INVALID_ID) + raise ValueError(RESOURCE_CLIENT_INVALID_ID) + + if "/" in id_or_uri: + self.__validate_resource_uri(id_or_uri) + return id_or_uri + else: + return self._uri + "/" + id_or_uri + def build_subresource_uri(self, resource_id_or_uri=None, subresource_id_or_uri=None, subresource_path=''): if subresource_id_or_uri and "/" in subresource_id_or_uri: return subresource_id_or_uri From c9a2d3b155251d5059aa8efabce0370c3f4ee13a Mon Sep 17 00:00:00 2001 From: sijeesh Date: Tue, 14 Aug 2018 15:45:08 +0530 Subject: [PATCH 08/11] Override patch method for enclosure to add extra headers required for api version 600 --- examples/enclosures.py | 5 +++-- examples/fc_networks.py | 3 ++- hpOneView/resources/servers/enclosures.py | 24 +++++++++++++++++++++++ 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/examples/enclosures.py b/examples/enclosures.py index 750fd9f9..227c5c6e 100644 --- a/examples/enclosures.py +++ b/examples/enclosures.py @@ -38,7 +38,8 @@ "enclosure_group_uri": "/rest/enclosure-groups/00bdf757-7ce2-4ffd-be7e-1a568dd8ecfc", "enclosure_hostname": "172.18.1.11", "enclosure_username": "dcs", - "enclosure_password": "dcs" + "enclosure_password": "dcs", + "api_version": 600 } # Declare a CA signed certificate file path. @@ -71,7 +72,7 @@ enclosure = oneview_client.enclosures.get_by_name(options['name']) # Perform a patch operation on the enclosure, replacing name of the enclosure -enclosure_name = "Encl1-Updated-11" +enclosure_name = "Encl1-Updated" print("Updating the enclosure to have a name of " + enclosure_name) enclosure.patch('replace', '/name', enclosure_name) print(" Done.\n URI = '{uri}', name = {name}".format(**enclosure.data)) diff --git a/examples/fc_networks.py b/examples/fc_networks.py index 3337673f..dcc69003 100644 --- a/examples/fc_networks.py +++ b/examples/fc_networks.py @@ -31,7 +31,8 @@ "credentials": { "userName": "administrator", "password": "ecosystem" - } + }, + "api_version": 600 } options = { diff --git a/hpOneView/resources/servers/enclosures.py b/hpOneView/resources/servers/enclosures.py index 7e9a570e..0d90d8d7 100644 --- a/hpOneView/resources/servers/enclosures.py +++ b/hpOneView/resources/servers/enclosures.py @@ -65,6 +65,30 @@ def add(self, information, timeout=-1): self.EXCLUDE_FROM_REQUEST = ['name'] return self.create(data=information, timeout=timeout) + def patch(self, operation, path, value, timeout=-1): + """ + Uses the PATCH to update a resource. + + Only one operation can be performed in each PATCH call. + + Args + operation: Patch operation + path: Path + value: Value + timeout: Timeout in seconds. Wait for task completion by default. The timeout does not abort the operation + in OneView; it just stops waiting for its completion. + + Returns: + Updated resource. + """ + patch_request_body = [{'op': operation, 'path': path, 'value': value}] + + headers = {'If-Match': '*'} + self.data = self._patch_request(body=patch_request_body, + timeout=timeout, + custom_headers=headers) + return self + def remove(self, force=False): """ Remove enclosure From 0ca11f067fd7253822a572785b63d615c2eec18d Mon Sep 17 00:00:00 2001 From: Sijeesh Kattumunda Date: Tue, 14 Aug 2018 05:16:35 -0600 Subject: [PATCH 09/11] Cleaned example and fixed some style issues --- examples/enclosures.py | 15 +++++++-------- examples/fc_networks.py | 7 +++---- hpOneView/resources/resource.py | 2 +- hpOneView/resources/servers/enclosures.py | 8 ++++---- tests/unit/resources/servers/test_enclosures.py | 10 ++++++---- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/examples/enclosures.py b/examples/enclosures.py index 227c5c6e..ab7f5ba9 100644 --- a/examples/enclosures.py +++ b/examples/enclosures.py @@ -30,16 +30,15 @@ # This example is compatible only for C7000 enclosures config = { - "ip": "10.30.5.228", + "ip": "", "credentials": { - "userName": "administrator", - "password": "ecosystem" + "userName": "", + "password": "" }, - "enclosure_group_uri": "/rest/enclosure-groups/00bdf757-7ce2-4ffd-be7e-1a568dd8ecfc", - "enclosure_hostname": "172.18.1.11", - "enclosure_username": "dcs", - "enclosure_password": "dcs", - "api_version": 600 + "enclosure_group_uri": "", + "enclosure_hostname": "", + "enclosure_username": "", + "enclosure_password": "", } # Declare a CA signed certificate file path. diff --git a/examples/fc_networks.py b/examples/fc_networks.py index dcc69003..c7fe6126 100644 --- a/examples/fc_networks.py +++ b/examples/fc_networks.py @@ -27,12 +27,11 @@ from config_loader import try_load_from_file config = { - "ip": "10.30.5.228", + "ip": "", "credentials": { - "userName": "administrator", - "password": "ecosystem" + "userName": "", + "password": "" }, - "api_version": 600 } options = { diff --git a/hpOneView/resources/resource.py b/hpOneView/resources/resource.py index afdf27a8..f6f0064d 100755 --- a/hpOneView/resources/resource.py +++ b/hpOneView/resources/resource.py @@ -471,7 +471,7 @@ def patch(self, operation, path, value, timeout=-1, custom_headers=None): return self @ensure_resource_client - def _patch_request(self, body, timeout=-1, custom_headers=None): + def patch_request(self, body, timeout=-1, custom_headers=None): """ Uses the PATCH to update a resource. diff --git a/hpOneView/resources/servers/enclosures.py b/hpOneView/resources/servers/enclosures.py index 0d90d8d7..d80c3d5a 100644 --- a/hpOneView/resources/servers/enclosures.py +++ b/hpOneView/resources/servers/enclosures.py @@ -83,10 +83,10 @@ def patch(self, operation, path, value, timeout=-1): """ patch_request_body = [{'op': operation, 'path': path, 'value': value}] - headers = {'If-Match': '*'} - self.data = self._patch_request(body=patch_request_body, - timeout=timeout, - custom_headers=headers) + headers = {'If-Match': '*'} + self.data = self.patch_request(body=patch_request_body, + timeout=timeout, + custom_headers=headers) return self def remove(self, force=False): diff --git a/tests/unit/resources/servers/test_enclosures.py b/tests/unit/resources/servers/test_enclosures.py index 6163e8ee..57a72886 100644 --- a/tests/unit/resources/servers/test_enclosures.py +++ b/tests/unit/resources/servers/test_enclosures.py @@ -73,12 +73,14 @@ def test_get_with_uri_called_once(self, mock_get): self._enclosures.get_by_uri(uri) mock_get.assert_called_once_with(uri) - @mock.patch.object(Resource, 'patch') - def test_patch_should_use_user_defined_values(self, mock_patch): + @mock.patch.object(Resource, 'load_resource') + @mock.patch.object(Resource, 'patch_request') + def test_patch_should_use_user_defined_values(self, mock_patch, load_resource): mock_patch.return_value = {} - self._enclosures.patch('123a53cz', 'replace', '/name', 'new_name', 1) - mock_patch.assert_called_once_with('123a53cz', 'replace', '/name', 'new_name', 1) + self._enclosures.patch('replace', '/name', 'new_name', 1) + mock_patch.assert_called_once_with(body=[{u'path': '/name', u'value': 'new_name', u'op': 'replace'}], + custom_headers={u'If-Match': u'*'}, timeout=1) @mock.patch.object(Resource, 'delete') def test_remove_called_once(self, mock_delete): From 80a26b8c253deb30e07754c07ded364a4ad730cd Mon Sep 17 00:00:00 2001 From: Jyothis Date: Mon, 20 Aug 2018 17:38:58 +0530 Subject: [PATCH 10/11] Id pool support for V500 and V600. Compatible with generic class. --- CHANGELOG.md | 5 + endpoints-support.md | 16 ++-- examples/id_pools.py | 38 +++----- hpOneView/oneview_client.py | 4 +- hpOneView/resources/resource.py | 2 +- hpOneView/resources/servers/id_pools.py | 91 +++++++++---------- tests/unit/resources/servers/test_id_pools.py | 85 +++++++++-------- tests/unit/test_oneview_client.py | 4 - 8 files changed, 120 insertions(+), 125 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eac8fe11..fac39910 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# 5.0.0 (Unrelased) + +#### Features supported with current release: +- Id pool + # 4.7.1 #### Bug fixes - [#364] (https://github.com/HewlettPackard/python-hpOneView/issues/364) Bug in index_resources.get_all() diff --git a/endpoints-support.md b/endpoints-support.md index d1e7df72..5bfc4c2b 100755 --- a/endpoints-support.md +++ b/endpoints-support.md @@ -137,14 +137,14 @@ |/rest/firmware-drivers/{id} | GET | :white_check_mark: | :white_check_mark: | :white_check_mark: | |/rest/firmware-drivers/{id} | DELETE | :white_check_mark: | :white_check_mark: | :white_check_mark: | | **ID Pools** | -|/rest/id-pools/{poolType} | GET | :white_check_mark: | :white_check_mark: | -|/rest/id-pools/{poolType} | PUT | :white_check_mark: | :white_check_mark: | -|/rest/id-pools/{poolType}/allocator | PUT | :white_check_mark: | :white_check_mark: | -|/rest/id-pools/{poolType}/checkrangeavailability | GET | :white_check_mark: | :white_check_mark: | -|/rest/id-pools/{poolType}/collector | PUT | :white_check_mark: | :white_check_mark: | -|/rest/id-pools/{poolType}/generate | GET | :white_check_mark: | :white_check_mark: | -|/rest/id-pools/{poolType}/validate | GET | :white_check_mark: | :white_check_mark: | -|/rest/id-pools/{poolType}/validate | PUT | :white_check_mark: | :white_check_mark: | +|/rest/id-pools/{poolType} | GET | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | +|/rest/id-pools/{poolType} | PUT | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | +|/rest/id-pools/{poolType}/allocator | PUT | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | +|/rest/id-pools/{poolType}/checkrangeavailability | GET | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | +|/rest/id-pools/{poolType}/collector | PUT | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | +|/rest/id-pools/{poolType}/generate | GET | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | +|/rest/id-pools/{poolType}/validate | GET | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | +|/rest/id-pools/{poolType}/validate | PUT | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | | **ID Pools IPv4 Ranges** | |/rest/id-pools/ipv4/ranges | POST | :white_check_mark: | :white_check_mark: | |/rest/id-pools/ipv4/ranges/{id} | GET | :white_check_mark: | :white_check_mark: | diff --git a/examples/id_pools.py b/examples/id_pools.py index 7ab8f71a..2daad88e 100644 --- a/examples/id_pools.py +++ b/examples/id_pools.py @@ -45,56 +45,48 @@ pool_type_ipv4 = 'ipv4' print("\n Gets the Pool: " + pool_type_vsn) -id_pool = oneview_client.id_pools.get(pool_type_vsn) -pprint(id_pool) +id_pool_1 = oneview_client.id_pools.get_by_name(pool_type_vsn) +pprint(id_pool_1.data) print("\n Gets the Pool: " + pool_type_vwwn) -id_pool = oneview_client.id_pools.get(pool_type_vwwn) -pprint(id_pool) +id_pool_2 = oneview_client.id_pools.get_by_name(pool_type_vwwn) +pprint(id_pool_2.data) print("\n Gets the Pool: " + pool_type_vmac) -id_pool = oneview_client.id_pools.get(pool_type_vmac) -pprint(id_pool) +id_pool_3 = oneview_client.id_pools.get_by_name(pool_type_vmac) +pprint(id_pool_3.data) print("\n Gets the Pool: " + pool_type_ipv4) -id_pool = oneview_client.id_pools.get(pool_type_ipv4) -pprint(id_pool) +id_pool_4 = oneview_client.id_pools.get_by_name(pool_type_ipv4) +pprint(id_pool_4.data) print("\n Enable the Id Pool") -id_pool = oneview_client.id_pools.enable({"type": "Pool", - "enabled": True}, - pool_type_vsn) +id_pool_1.enable({"type": "Pool", "enabled": True}) print(" Id Pool enabled") print("\n Generates a random range") -rnd_range = oneview_client.id_pools.generate(pool_type_vsn) +rnd_range = id_pool_1.generate() pprint(rnd_range) print("\n Allocates a set of IDs from a pool") -allocated_ids = oneview_client.id_pools.allocate({"count": 10 - }, pool_type_vsn) +allocated_ids = id_pool_1.allocate({"count": 10}) pprint(allocated_ids) print("\n Checks the range availability in the Id pool") -range_availability = oneview_client.id_pools.get_check_range_availability(pool_type_vsn, - ['VCGYOAF00P', - 'VCGYOAF002']) +range_availability = id_pool_1.get_check_range_availability(['VCGYOAF00P', 'VCGYOAF002']) pprint(range_availability) print("\n Validates a set of user specified IDs to reserve in the pool") -validated = oneview_client.id_pools.validate({'idList': ['VCGYOAA023', - 'VCGYOAA024']}, pool_type_vsn) +validated = id_pool_1.validate({'idList': ['VCGYOAA023', 'VCGYOAA024']}) pprint(validated) print("\n Validates an Id Pool") -get_validate = oneview_client.id_pools.validate_id_pool(pool_type_ipv4, - ['172.18.9.11']) +get_validate = id_pool_1.validate_id_pool(['172.18.9.11']) pprint(get_validate) print("\n Collect a set of IDs back to Id Pool") try: - collected_ids = oneview_client.id_pools.collect({"idList": allocated_ids['idList']}, - pool_type_vsn) + collected_ids = id_pool_1.collect({"idList": allocated_ids['idList']}) pprint(collected_ids) except HPOneViewException as e: print(e.msg) diff --git a/hpOneView/oneview_client.py b/hpOneView/oneview_client.py index 56c2c342..21774e74 100755 --- a/hpOneView/oneview_client.py +++ b/hpOneView/oneview_client.py @@ -520,9 +520,7 @@ def id_pools(self): Returns: IdPools: """ - if not self.__id_pools: - self.__id_pools = IdPools(self.__connection) - return self.__id_pools + return IdPools(self.__connection) @property def switches(self): diff --git a/hpOneView/resources/resource.py b/hpOneView/resources/resource.py index f6f0064d..19d9996a 100755 --- a/hpOneView/resources/resource.py +++ b/hpOneView/resources/resource.py @@ -713,7 +713,7 @@ def do_post(self, uri, resource, timeout, custom_headers): return self._task_monitor.wait_for_task(task, timeout) - def do_put(self, uri, resource, timeout, custom_headers): + def do_put(self, uri, resource, timeout, custom_headers=None): """ Method to support put requests of the resource """ diff --git a/hpOneView/resources/servers/id_pools.py b/hpOneView/resources/servers/id_pools.py index 211f6e3c..5c3b2e1c 100644 --- a/hpOneView/resources/servers/id_pools.py +++ b/hpOneView/resources/servers/id_pools.py @@ -29,37 +29,39 @@ standard_library.install_aliases() -from hpOneView.resources.resource import ResourceClient +from hpOneView.resources.resource import Resource, ensure_resource_client -class IdPools(object): +class IdPools(Resource): """ Class for Id Pools API client. """ URI = '/rest/id-pools' - def __init__(self, con): - self._client = ResourceClient(con, self.URI) + def __init__(self, connection, options=None): + super(IdPools, self).__init__(connection, options) - def get(self, id_or_uri): + def get_by_name(self, name): """ - Gets a pool. - - Args: - id_or_uri: Can be either the range ID or URI. + Retrieve a id pool + """ + uri = '{}/{}'.format(self.URI, name) + self.data = self.do_get(uri) + return self - Returns: - dict: Pool resource. + def get_by_uri(self, uri): """ - return self._client.get(id_or_uri) + Retrieve a id pool + """ + self.data = self.do_get(uri) + return self - def enable(self, information, id_or_uri, timeout=-1): + def enable(self, information, timeout=-1): """ Enables or disables a pool. Args: information (dict): Information to update. - id_or_uri: ID or URI of range. timeout: Timeout in seconds. Wait for task completion by default. The timeout does not abort the operation in OneView; it just stops waiting for its completion. @@ -67,26 +69,26 @@ def enable(self, information, id_or_uri, timeout=-1): dict: Updated resource. """ - uri = self._client.build_uri(id_or_uri) - return self._client.update(information, uri, timeout=timeout) + return self.update(information, timeout=timeout) - def validate_id_pool(self, id_or_uri, ids_pools): + @ensure_resource_client + def validate_id_pool(self, ids_pools): """ Validates an ID pool. Args: - id_or_uri: - ID or URI of range. ids_pools (list): List of Id Pools. Returns: dict: A dict containing a list with IDs. """ - uri = self._client.build_uri(id_or_uri) + "/validate?idList=" + "&idList=".join(ids_pools) - return self._client.get(uri) - def validate(self, information, id_or_uri, timeout=-1): + uri = self.data['uri'] + "/validate?idList=" + "&idList=".join(ids_pools) + return self.do_get(uri) + + @ensure_resource_client + def validate(self, information, timeout=-1): """ Validates a set of user specified IDs to reserve in the pool. @@ -95,8 +97,6 @@ def validate(self, information, id_or_uri, timeout=-1): Args: information (dict): Information to update. Can result in system specified IDs or the system reserving user-specified IDs. - id_or_uri: - ID or URI of vSN range. timeout: Timeout in seconds. Wait for task completion by default. The timeout does not abort the operation in OneView; it just stops waiting for its completion. @@ -104,10 +104,12 @@ def validate(self, information, id_or_uri, timeout=-1): Returns: dict: A dict containing a list with IDs. """ - uri = self._client.build_uri(id_or_uri) + "/validate" - return self._client.update(information, uri, timeout=timeout) - def allocate(self, information, id_or_uri, timeout=-1): + uri = '{}/validate'.format(self.data['uri']) + return self.do_put(uri, information, timeout=timeout) + + @ensure_resource_client + def allocate(self, information, timeout=-1): """ Allocates a set of IDs from range. @@ -116,8 +118,6 @@ def allocate(self, information, id_or_uri, timeout=-1): Args: information (dict): Information to update. Can result in system specified IDs or the system reserving user-specified IDs. - id_or_uri: - ID or URI of vSN range. timeout: Timeout in seconds. Wait for task completion by default. The timeout does not abort the operation in OneView; it just stops waiting for its completion. @@ -125,19 +125,18 @@ def allocate(self, information, id_or_uri, timeout=-1): Returns: dict: A dict containing a list with IDs. """ - uri = self._client.build_uri(id_or_uri) + "/allocator" - return self._client.update(information, uri, timeout=timeout) + uri = '{}/allocator'.format(self.data['uri']) + return self.do_put(uri, information, timeout=timeout) - def collect(self, information, id_or_uri, timeout=-1): + @ensure_resource_client + def collect(self, information, timeout=-1): """ Collects one or more IDs to be returned to a pool. Args: information (dict): The list of IDs to be collected - id_or_uri: - ID or URI of range timeout: Timeout in seconds. Wait for task completion by default. The timeout does not abort the operation in OneView; it just stops waiting for its completion. @@ -145,17 +144,16 @@ def collect(self, information, id_or_uri, timeout=-1): Returns: dict: Collector containing list of collected IDs successfully collected. """ - uri = self._client.build_uri(id_or_uri) + "/collector" - return self._client.update(information, uri, timeout=timeout) + uri = '{}/collector'.format(self.data['uri']) + return self.do_put(uri, information, timeout=timeout) - def get_check_range_availability(self, id_or_uri, ids_pools): + @ensure_resource_client + def get_check_range_availability(self, ids_pools): """ Checks the range availability in the ID pool. Args: - id_or_uri: - ID or URI of range. ids_pools (list): List of Id Pools. @@ -163,19 +161,20 @@ def get_check_range_availability(self, id_or_uri, ids_pools): dict: A dict containing a list with IDs. """ - uri = self._client.build_uri(id_or_uri) + "/checkrangeavailability?idList=" + "&idList=".join(ids_pools) - return self._client.get(uri) + uri = self.data['uri'] + "/checkrangeavailability?idList=" + "&idList=".join(ids_pools) + return self.do_get(uri) - def generate(self, id_or_uri): + @ensure_resource_client + def generate(self): """ Generates and returns a random range. Args: - id_or_uri: - ID or URI of range. + None Returns: dict: A dict containing a list with IDs. """ - uri = self._client.build_uri(id_or_uri) + "/generate" - return self._client.get(uri) + + uri = '{}/generate'.format(self.data['uri']) + return self.do_get(uri) diff --git a/tests/unit/resources/servers/test_id_pools.py b/tests/unit/resources/servers/test_id_pools.py index 9dd86c7d..c9445c0e 100644 --- a/tests/unit/resources/servers/test_id_pools.py +++ b/tests/unit/resources/servers/test_id_pools.py @@ -24,7 +24,7 @@ import unittest from hpOneView.connection import connection -from hpOneView.resources.resource import ResourceClient +from hpOneView.resources.resource import Resource from hpOneView.resources.servers.id_pools import IdPools @@ -36,53 +36,58 @@ class TestIdPools(unittest.TestCase): def setUp(self): self.host = '127.0.0.1' self.connection = connection(self.host) - self.client = IdPools(self.connection) + self._id_pools = IdPools(self.connection) + self._id_pools.data = {'uri': '/rest/id-pools/ipv4'} - @mock.patch.object(ResourceClient, 'get') - def test_get_called_once_by_id(self, mock_get): - id_pools_range_id = "f0a0a113-ec97-41b4-83ce-d7c92b900e7c" - self.client.get(id_pools_range_id) - mock_get.assert_called_once_with(id_pools_range_id) + @mock.patch.object(Resource, 'do_get') + def test_get_called_once_by_name(self, mock_do_get): + id_pools_range_id = "ipv4" + self._id_pools.get_by_name(id_pools_range_id) + mock_do_get.assert_called_once_with(self.example_uri) - @mock.patch.object(ResourceClient, 'get') - def test_get_called_once_by_uri(self, mock_get): - self.client.get(self.example_uri) - mock_get.assert_called_once_with(self.example_uri) + @mock.patch.object(Resource, 'do_get') + def test_get_called_once_by_uri(self, mock_do_get): + self._id_pools.get_by_uri(self.example_uri) + mock_do_get.assert_called_once_with(self.example_uri) - @mock.patch.object(ResourceClient, 'get') - def test_generate_called_once(self, mock_get): - self.client.generate(self.example_uri) - mock_get.assert_called_once_with(self.example_uri + '/generate') + @mock.patch.object(Resource, 'load_resource') + @mock.patch.object(Resource, 'do_get') + def test_generate_called_once(self, mock_do_get, load_resource): + self._id_pools.generate() + mock_do_get.assert_called_once_with(self.example_uri + '/generate') - @mock.patch.object(ResourceClient, 'get') - def test_validate_id_pool_called_once(self, mock_get): - self.client.validate_id_pool(self.example_uri, ['VCGYOAA023', - 'VCGYOAA024']) - mock_get.assert_called_once_with(self.example_uri + "/validate?idList=VCGYOAA023&idList=VCGYOAA024") + @mock.patch.object(Resource, 'load_resource') + @mock.patch.object(Resource, 'do_get') + def test_validate_id_pool_called_once(self, mock_do_get, load_resource): + self._id_pools.validate_id_pool(['VCGYOAA023', 'VCGYOAA024']) + mock_do_get.assert_called_once_with(self.example_uri + "/validate?idList=VCGYOAA023&idList=VCGYOAA024") - @mock.patch.object(ResourceClient, 'update') - def test_validate_called_once(self, update): - self.client.validate(self.resource_info.copy(), self.example_uri) - update.assert_called_once_with(self.resource_info.copy(), self.example_uri + "/validate", timeout=-1) + @mock.patch.object(Resource, 'load_resource') + @mock.patch.object(Resource, 'do_put') + def test_validate_called_once(self, mock_do_put, load_resource): + self._id_pools.validate(self.resource_info.copy()) + mock_do_put.assert_called_once_with(self.example_uri + "/validate", self.resource_info.copy(), timeout=-1) - @mock.patch.object(ResourceClient, 'update') + @mock.patch.object(Resource, 'update') def test_enable_called_once(self, update): - self.client.enable(self.resource_info.copy(), self.example_uri) - update.assert_called_once_with(self.resource_info.copy(), self.example_uri, timeout=-1) + self._id_pools.enable(self.resource_info.copy()) + update.assert_called_once_with(self.resource_info.copy(), timeout=-1) - @mock.patch.object(ResourceClient, 'get') - def test_get_check_range_availability_called_once_with_defaults(self, mock_get): - self.client.get_check_range_availability(self.example_uri, ['VCGYOAA023', - 'VCGYOAA024']) - mock_get.assert_called_once_with( + @mock.patch.object(Resource, 'load_resource') + @mock.patch.object(Resource, 'do_get') + def test_get_check_range_availability_called_once_with_defaults(self, mock_do_get, load_resource): + self._id_pools.get_check_range_availability(['VCGYOAA023', 'VCGYOAA024']) + mock_do_get.assert_called_once_with( self.example_uri + "/checkrangeavailability?idList=VCGYOAA023&idList=VCGYOAA024") - @mock.patch.object(ResourceClient, 'update') - def test_allocate_called_once(self, mock_update): - self.client.allocate(self.resource_info.copy(), self.example_uri) - mock_update.assert_called_once_with(self.resource_info.copy(), self.example_uri + "/allocator", timeout=-1) + @mock.patch.object(Resource, 'load_resource') + @mock.patch.object(Resource, 'do_put') + def test_allocate_called_once(self, mock_do_put, load_resource): + self._id_pools.allocate(self.resource_info.copy()) + mock_do_put.assert_called_once_with(self.example_uri + "/allocator", self.resource_info.copy(), timeout=-1) - @mock.patch.object(ResourceClient, 'update') - def test_collect_called_once(self, update): - self.client.collect(self.resource_info.copy(), self.example_uri) - update.assert_called_once_with(self.resource_info.copy(), self.example_uri + "/collector", timeout=-1) + @mock.patch.object(Resource, 'load_resource') + @mock.patch.object(Resource, 'do_put') + def test_collect_called_once(self, mock_do_put, load_resource): + self._id_pools.collect(self.resource_info.copy()) + mock_do_put.assert_called_once_with(self.example_uri + "/collector", self.resource_info.copy(), timeout=-1) diff --git a/tests/unit/test_oneview_client.py b/tests/unit/test_oneview_client.py index 21dfb6c4..8fdb6ff0 100755 --- a/tests/unit/test_oneview_client.py +++ b/tests/unit/test_oneview_client.py @@ -538,10 +538,6 @@ def test_id_pools_ipv4_subnets_lazy_loading(self): def test_id_pools_has_right_type(self): self.assertIsInstance(self._oneview.id_pools, IdPools) - def test_id_pools_lazy_loading(self): - id_pools = self._oneview.id_pools - self.assertEqual(id_pools, self._oneview.id_pools) - def test_lazy_loading_logical_enclosures(self): logical_enclosures = self._oneview.logical_enclosures self.assertEqual(logical_enclosures, self._oneview.logical_enclosures) From e49efa9dca123a641e17ad48c0e1855a36ae41e7 Mon Sep 17 00:00:00 2001 From: Jyothis Date: Mon, 27 Aug 2018 14:50:52 +0530 Subject: [PATCH 11/11] Example file updated --- examples/id_pools.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/examples/id_pools.py b/examples/id_pools.py index 2daad88e..ee47299a 100644 --- a/examples/id_pools.py +++ b/examples/id_pools.py @@ -45,48 +45,48 @@ pool_type_ipv4 = 'ipv4' print("\n Gets the Pool: " + pool_type_vsn) -id_pool_1 = oneview_client.id_pools.get_by_name(pool_type_vsn) -pprint(id_pool_1.data) +id_pool_vsn = oneview_client.id_pools.get_by_name(pool_type_vsn) +pprint(id_pool_vsn.data) print("\n Gets the Pool: " + pool_type_vwwn) -id_pool_2 = oneview_client.id_pools.get_by_name(pool_type_vwwn) -pprint(id_pool_2.data) +id_pool_vwwn = oneview_client.id_pools.get_by_name(pool_type_vwwn) +pprint(id_pool_vwwn.data) print("\n Gets the Pool: " + pool_type_vmac) -id_pool_3 = oneview_client.id_pools.get_by_name(pool_type_vmac) -pprint(id_pool_3.data) +id_pool_vmac = oneview_client.id_pools.get_by_name(pool_type_vmac) +pprint(id_pool_vmac.data) print("\n Gets the Pool: " + pool_type_ipv4) -id_pool_4 = oneview_client.id_pools.get_by_name(pool_type_ipv4) -pprint(id_pool_4.data) +id_pool_ipv4 = oneview_client.id_pools.get_by_name(pool_type_ipv4) +pprint(id_pool_ipv4.data) print("\n Enable the Id Pool") -id_pool_1.enable({"type": "Pool", "enabled": True}) +id_pool_vsn.enable({"type": "Pool", "enabled": True}) print(" Id Pool enabled") print("\n Generates a random range") -rnd_range = id_pool_1.generate() +rnd_range = id_pool_vsn.generate() pprint(rnd_range) print("\n Allocates a set of IDs from a pool") -allocated_ids = id_pool_1.allocate({"count": 10}) +allocated_ids = id_pool_vsn.allocate({"count": 10}) pprint(allocated_ids) print("\n Checks the range availability in the Id pool") -range_availability = id_pool_1.get_check_range_availability(['VCGYOAF00P', 'VCGYOAF002']) +range_availability = id_pool_vsn.get_check_range_availability(['VCGYOAF00P', 'VCGYOAF002']) pprint(range_availability) print("\n Validates a set of user specified IDs to reserve in the pool") -validated = id_pool_1.validate({'idList': ['VCGYOAA023', 'VCGYOAA024']}) +validated = id_pool_vsn.validate({'idList': ['VCGYOAA023', 'VCGYOAA024']}) pprint(validated) print("\n Validates an Id Pool") -get_validate = id_pool_1.validate_id_pool(['172.18.9.11']) +get_validate = id_pool_vsn.validate_id_pool(['172.18.9.11']) pprint(get_validate) print("\n Collect a set of IDs back to Id Pool") try: - collected_ids = id_pool_1.collect({"idList": allocated_ids['idList']}) + collected_ids = id_pool_vsn.collect({"idList": allocated_ids['idList']}) pprint(collected_ids) except HPOneViewException as e: print(e.msg)