def fetch_resource(rest_end_point, href): """This is the method you should use to create a Resource instance if you have the href for it. It gets the current state of the resource with the given *href*. Once the current state is fetched from the Space server, this method creates a new instance of ``jnpr.space.resource.Resource`` with this state and returns it. :param rest_end_point: A *Space* object encapsulating the Junos Space cluster which contains this resource. :type rest_end_point: jnpr.space.rest.Space :param str href: The href of the resource that needs to be fetched. :returns: A new instance of jnpr.space.resource.Resource :raises: ``jnpr.space.rest.RestException`` if the GET request results in an error response. The exception's ``response`` attribute will have the full response from Space. """ response = rest_end_point.get(href) if response.status_code != 200: raise rest.RestException("GET failed on %s" % href, response) media_type = response.headers['content-type'] end = media_type.index('+') parts = media_type[:end].split('.') app_ = parts[len(parts)-3] service_ = parts[len(parts)-2] type_ = parts[len(parts)-1] if app_ == 'space': type_name = xmlutil.unmake_xml_name('.'.join([service_, type_])) else: type_name = xmlutil.unmake_xml_name('.'.join([app_, service_, type_])) xml_data = xmlutil.get_xml_obj_from_response(response) return make_resource(type_name, rest_end_point, xml_data)
def post(self, new_obj=None, accept=None, content_type=None, request_body=None, xml_name=None, task_monitor=None): """ Sends a POST request to the Space server to create a new Resource in this collection. :param new_obj: The new Resource that needs to be created as a member of this collection. This can be omitted in which case the caller must supply a request body for the POST request as the ``request_body`` argument. :type new_obj: A single ``jnpr.space.resource.Resource`` instance or a list of them. :param str accept: This can be used to supply a media-type that must be used as the Accept header in the request. This defaults to ``None`` and in this case SpaceEZ will use the media-type modeled in the description file. :param str content_type: This can be used to supply a media-type that must be used as the Content-Type header in the request. This defaults to ``None`` and in this case SpaceEZ will use the media-type modeled in the description file. :param str request_body: This can be used to supply a string that must be used as the request body in the request. This defaults to ``None`` and in this case the caller must supply the ``new_obj`` argument from which the request body will be formed. :param str xml_name: Can be used to override the name of the top-level XML element in the generated request body. This is useful in some cases such as creating a quick config template. This parameter defaults to ``None``. :param task_monitor: A TaskMonitor object that can be used to monitor the progress of the POST request, in case of asynchronous invocations. You need to check Junos Space API documentation to see if the POST invocation on this resource has asynchronous semantics and supply the task_monitor parameter only if it is asynchronous. Otherwise, this will default to ``None`` and the method will behave with synchronous semantics. :type task_monitor: jnpr.space.async.TaskMonitor :returns: If the new_obj parameter is a list, then the same list is returned. Otherwise, this method creates a new Resource object based on the state of the newly created resource, as extracted from the POST response body. In the case of asynchronous invocation, this will represent a Task object and will contain the unique id of the Task executing the POST request in Space. :raises: ``jnpr.space.rest.RestException`` if the POST method results in an error response. The exception's ``response`` attribute will have the full response from Space. """ if content_type is not None: media_type = content_type elif isinstance(new_obj, list): if self._meta_object.content_type is not None: media_type = self._meta_object.content_type else: media_type = self._meta_object.get_media_type(None) else: if new_obj is None: raise ValueError( 'Must provide content_type when providing request_body') media_type = new_obj.get_meta_object().get_media_type(None) headers = {'content-type': media_type} if accept is not None: headers['accept'] = accept if request_body is not None: body = request_body saved_root_tag = None if new_obj is not None: raise ValueError('Cannot use both request_body and new_obj!') else: if new_obj is None: raise ValueError('Cannot omit both request_body and new_obj!') xml_obj = None if isinstance(new_obj, list): xml_obj = etree.Element(self._meta_object.xml_name) for obj in new_obj: xml_obj.append(obj.form_xml()) else: xml_obj = new_obj.form_xml() saved_root_tag = xml_obj.tag if xml_name is not None: xml_obj.tag = xml_name body = xmlutil.cleanup(etree.tostring(xml_obj, encoding='unicode')) url = self.get_href() if task_monitor is not None: url = '?queue='.join([url, task_monitor.get_queue_url()]) response = self._rest_end_point.post(url, headers, body) if response.status_code == 204: # Special case of post with null response return new_obj if response.status_code not in [200, 202]: raise rest.RestException("POST failed on %s" % self.get_href(), response) if task_monitor is not None: resp_str = xmlutil.get_text_from_response(response) return xmlutil.xml2obj(resp_str) if not isinstance(new_obj, list): # Fixing issue #17 #r = response.text # Skip the <?xml> line to avoid encoding errors in lxml #start = r.index('?><') + 2 #root = etree.fromstring(r[start:]) root = xmlutil.get_xml_obj_from_response(response) #new_obj._xml_data = root #new_obj._rest_end_point = self._rest_end_point if saved_root_tag is not None: root.tag = saved_root_tag new_obj = self._create_resource(root) return new_obj
def post(self, new_obj=None, accept=None, content_type=None, request_body=None, xml_name=None, task_monitor=None): """ Sends a POST request to the Space server to create a new Resource in this collection. :param new_obj: The new Resource that needs to be created as a member of this collection. This can be omitted in which case the caller must supply a request body for the POST request as the ``request_body`` argument. :type new_obj: A single ``jnpr.space.resource.Resource`` instance or a list of them. :param str accept: This can be used to supply a media-type that must be used as the Accept header in the request. This defaults to ``None`` and in this case SpaceEZ will use the media-type modeled in the description file. :param str content_type: This can be used to supply a media-type that must be used as the Content-Type header in the request. This defaults to ``None`` and in this case SpaceEZ will use the media-type modeled in the description file. :param str request_body: This can be used to supply a string that must be used as the request body in the request. This defaults to ``None`` and in this case the caller must supply the ``new_obj`` argument from which the request body will be formed. :param str xml_name: Can be used to override the name of the top-level XML element in the generated request body. This is useful in some cases such as creating a quick config template. This parameter defaults to ``None``. :param task_monitor: A TaskMonitor object that can be used to monitor the progress of the POST request, in case of asynchronous invocations. You need to check Junos Space API documentation to see if the POST invocation on this resource has asynchronous semantics and supply the task_monitor parameter only if it is asynchronous. Otherwise, this will default to ``None`` and the method will behave with synchronous semantics. :type task_monitor: jnpr.space.async.TaskMonitor :returns: If the new_obj parameter is a list, then the same list is returned. Otherwise, this method creates a new Resource object based on the state of the newly created resource, as extracted from the POST response body. In the case of asynchronous invocation, this will represent a Task object and will contain the unique id of the Task executing the POST request in Space. :raises: ``jnpr.space.rest.RestException`` if the POST method results in an error response. The exception's ``response`` attribute will have the full response from Space. """ if content_type is not None: media_type = content_type elif isinstance(new_obj, list): if self._meta_object.content_type is not None: media_type = self._meta_object.content_type else: media_type = self._meta_object.get_media_type(None) else: if new_obj is None: raise ValueError('Must provide content_type when providing request_body') media_type = new_obj.get_meta_object().get_media_type(None) headers = {'content-type': media_type} if accept is not None: headers['accept'] = accept if request_body is not None: body = request_body saved_root_tag = None if new_obj is not None: raise ValueError('Cannot use both request_body and new_obj!') else: if new_obj is None: raise ValueError('Cannot omit both request_body and new_obj!') xml_obj = None if isinstance(new_obj, list): xml_obj = etree.Element(self._meta_object.xml_name) for obj in new_obj: xml_obj.append(obj.form_xml()) else: xml_obj = new_obj.form_xml() saved_root_tag = xml_obj.tag if xml_name is not None: xml_obj.tag = xml_name body = xmlutil.cleanup(etree.tostring(xml_obj, encoding='unicode')) url = self.get_href() if task_monitor is not None: url = '?queue='.join([url, task_monitor.get_queue_url()]) response = self._rest_end_point.post(url, headers, body) if response.status_code == 204: # Special case of post with null response return new_obj if response.status_code not in [200, 202]: raise rest.RestException("POST failed on %s" % self.get_href(), response) if task_monitor is not None: resp_str = xmlutil.get_text_from_response(response) return xmlutil.xml2obj(resp_str) if not isinstance(new_obj, list): # Fixing issue #17 #r = response.text # Skip the <?xml> line to avoid encoding errors in lxml #start = r.index('?><') + 2 #root = etree.fromstring(r[start:]) root = xmlutil.get_xml_obj_from_response(response) #new_obj._xml_data = root #new_obj._rest_end_point = self._rest_end_point if saved_root_tag is not None: root.tag = saved_root_tag new_obj = self._create_resource(root) return new_obj
def get(self, accept=None, filter_=None, domain_id=None, paging=None, sortby=None): """Gets the contained resources of this collection from Space. :param str accept: This can be used to supply a media-type that must be used as the Accept header in the GET request. This defaults to ``None`` and in this case SpaceEZ will use the media-type modeled in the description file. :param filter_: A filter expression to apply on the collection. This can be given as a dict with name:value pairs to filter with. For example, ``{'name':'user1'}``. Or this can be given as a string which forms a valid filter expression for this collection as per Junos Space API documentation. This parameter defaults to ``None``. :type filter_: str or dict :param paging: A paging expression to apply on the collection. This must be given as a dict with entries giving values for ``start`` and ``limit`` paging parameters. For example, ``{'start':10, 'limit':100}``. This parameter defaults to ``None``. :type paging: dict :param sortby: A list of field names to sort the results by. This parameter defaults to ``None``. :type sortby: list of str :returns: A list of ``jnpr.space.resource.Resource`` objects. :raises: ``jnpr.space.rest.RestException`` if the GET method results in an error response. The exception's ``response`` attribute will have the full response from Space. """ url = self._form_get_url(filter_, domain_id, paging, sortby) if accept is not None: mtype = accept else: mtype = self._meta_object.get_media_type(None) if mtype is not None: if not self._meta_object.retain_charset_in_accept: end = mtype.find(';charset=') if end > 0: mtype = mtype[0:end] headers = {'accept': mtype} else: headers = {} resource_list = [] response = self._rest_end_point.get(url, headers) if response.status_code != 200: if response.status_code == 204: return [] raise rest.RestException("GET failed on %s" % url, response) # Fixing issue #17 #r = response.text # Skip the <?xml> line to avoid encoding errors in lxml #start = r.index('?><') + 2 #root = etree.fromstring(r[start:]) root = xmlutil.get_xml_obj_from_response(response) if self._meta_object.single_object_collection: resource_list.append(self._create_resource(root)) elif self._meta_object.named_members: for key, value in self._meta_object.named_members.items(): resrc = self._create_named_resource(key, value, root) resrc.id = key resource_list.append(resrc) else: for child in root: try: resrc = self._create_resource(child) resource_list.append(resrc) except Exception as ex: if ex.ignore: pass else: raise ex return ResourceList(resource_list)
def get(self, accept=None, filter_=None, domain_id=None, paging=None, sortby=None): """Gets the contained resources of this collection from Space. :param str accept: This can be used to supply a media-type that must be used as the Accept header in the GET request. This defaults to ``None`` and in this case SpaceEZ will use the media-type modeled in the description file. :param filter_: A filter expression to apply on the collection. This can be given as a dict with name:value pairs to filter with. For example, ``{'name':'user1'}``. Or this can be given as a string which forms a valid filter expression for this collection as per Junos Space API documentation. This parameter defaults to ``None``. :type filter_: str or dict :param paging: A paging expression to apply on the collection. This must be given as a dict with entries giving values for ``start`` and ``limit`` paging parameters. For example, ``{'start':10, 'limit':100}``. This parameter defaults to ``None``. :type paging: dict :param sortby: A list of field names to sort the results by. This parameter defaults to ``None``. :type sortby: list of str :returns: A list of ``jnpr.space.resource.Resource`` objects. :raises: ``jnpr.space.rest.RestException`` if the GET method results in an error response. The exception's ``response`` attribute will have the full response from Space. """ url = self._form_get_url(filter_, domain_id, paging, sortby) if accept is not None: mtype = accept else: mtype = self._meta_object.get_media_type(None) if mtype is not None: if not self._meta_object.retain_charset_in_accept: end = mtype.find(';charset=') if end > 0: mtype = mtype[0:end] headers = {'accept' : mtype} else: headers = {} resource_list = [] response = self._rest_end_point.get(url, headers) if response.status_code != 200: if response.status_code == 204: return [] raise rest.RestException("GET failed on %s" % url, response) # Fixing issue #17 #r = response.text # Skip the <?xml> line to avoid encoding errors in lxml #start = r.index('?><') + 2 #root = etree.fromstring(r[start:]) root = xmlutil.get_xml_obj_from_response(response) if self._meta_object.single_object_collection: resource_list.append(self._create_resource(root)) elif self._meta_object.named_members: for key, value in self._meta_object.named_members.items(): resrc = self._create_named_resource(key, value, root) resrc.id = key resource_list.append(resrc) else: for child in root: try: resrc = self._create_resource(child) resource_list.append(resrc) except Exception as ex: if ex.ignore: pass else: raise ex return ResourceList(resource_list)