def test_devices_configs(self): devices_list = self.space.device_management.devices.get( filter_={'managedStatus': 'In Sync'}) assert len(devices_list) > 1, "Not enough devices on Space" for d in devices_list: configs = d.configurations.get() assert len(configs) == 2 for c in configs: xml_config = c.get() xml_config = xmlutil.xml2obj(xml_config.configuration.text) assert xml_config.version[:7] == d.OSVersion[:7]
def get(self, attr=None, accept=None): """ This is an overloaded method that does two things: If the ``attr`` parameter is passed, it returns the corresponding XML attribute from the top level XML data element contained by this resource. If the ``attr`` parameter is not passed, it performs an HTTP GET for this resource and get its current state. :param str attr: The name of the XML attribute in the top-level element of the XML state of the resource. Defaults to ``None``. :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. :returns: - Value of the named XML attribute. OR - The current state of this resource fetched from Space and represented as a Python object. You can access the fields of the resource's state directly as attributes of this object. :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. """ if attr is not None: return self._get_xml_attr(attr) 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 = {} response = self._rest_end_point.get(self.get_href(), headers) if response.status_code != 200: raise rest.RestException("GET failed on %s" % self.get_href(), response) #r = response.text resp_txt = xmlutil.get_text_from_response(response) return xmlutil.xml2obj(resp_txt)
def get(self, attr=None, accept=None): """ This is an overloaded method that does two things: If the ``attr`` parameter is passed, it returns the corresponding XML attribute from the top level XML data element contained by this resource. If the ``attr`` parameter is not passed, it performs an HTTP GET for this resource and get its current state. :param str attr: The name of the XML attribute in the top-level element of the XML state of the resource. Defaults to ``None``. :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. :returns: - Value of the named XML attribute. OR - The current state of this resource fetched from Space and represented as a Python object. You can access the fields of the resource's state directly as attributes of this object. :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. """ if attr is not None: return self._get_xml_attr(attr) 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 = {} response = self._rest_end_point.get(self.get_href(), headers) if response.status_code != 200: raise rest.RestException("GET failed on %s" % self.get_href(), response) #r = response.text resp_txt = xmlutil.get_text_from_response(response) return xmlutil.xml2obj(resp_txt)
def _create_resource(self, xml_data): """ Helper method to create a resource under this collection. This is used to populate entries in the list returned by the get() method of this collection. """ if self._meta_object.resource_type: from jnpr.space import resource return resource.Resource(type_name=self._meta_object.resource_type, rest_end_point=self._rest_end_point, xml_data=xml_data, parent=self) else: xml_str = etree.tostring(xml_data, encoding='unicode') return xmlutil.xml2obj(xml_str)
def get_final_progress_update(self, pu_message): """ Gets the final progress-update message for a job, based on the href inside the ``pu_message`` argument supplied. The ``pu_message`` arg is the last progress-update message obtained from a hornet-q and it does not contain the full result of the job. The progress-update message fetched and returned by this method will contain the full result inside the ``data`` field. """ job_pu_href = '/'.join([pu_message.job.get('href'), 'progress-update']) response = self._rest_end_point.get(job_pu_href) if response.status_code != 200: raise Exception("Failed in GET on %s" % job_pu_href) response_txt = xmlutil.get_text_from_response(response) return xmlutil.xml2obj(response_txt)
def test_devices_raw_config(self): devices_list = self.space.device_management.devices.get( filter_={'managedStatus': 'In Sync'}) assert len(devices_list) > 1, "Not enough devices on Space" for d in devices_list: raw = d.configurations.raw.get() assert raw is not None raw_config = xmlutil.xml2obj(raw.configuration.text) assert raw_config.version[:7] == d.OSVersion[:7] if hasattr(raw_config, 'groups'): for g in raw_config.groups: print("Found config group %s on device %s" % (g.name, d.name))
def test_devices_expanded_config(self): devices_list = self.space.device_management.devices.get( filter_={'managedStatus': 'In Sync'}) assert len(devices_list) > 1, "Not enough devices on Space" for d in devices_list: exp = d.configurations.expanded.get() assert exp exp_config = xmlutil.xml2obj(exp.configuration.text) import pytest with pytest.raises(AttributeError): assert exp_config.groups is None assert exp_config.version[:7] == d.OSVersion[:7]
def __init__(self, type_name, rest_end_point, xml_data=None, attributes=None, parent=None): """Initializes a Resource object. :param str type_name: Fully qualified type name for the Resource to be created. It is of the format ``<service_name>.<resource_type>`` or ``<app-name>.<service_name>.<resource_type>`` Some examples are: * ``device_management.device`` * ``user_management.user`` * ``servicenow.device_management.device`` :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 lxml.etree.Element xml_data: The state of the resource as an XML object. This defaults to ``None``. :param dict attributes: The state of the resource as a dict where the keys are attribute names and values are attribute values. This defaults to ``None``. :param parent: The parent object of this resource. This defaults to ``None``. :type parent: jnpr.space.collection.Collection """ self._type_name = type_name self._rest_end_point = rest_end_point self._xml_data = xml_data self._attributes = attributes self._parent = parent self._collections = {} self._methods = {} self._init_meta_data(rest_end_point, type_name) if xml_data is not None: if self._meta_object.xml_name != xml_data.tag: exc = Exception('Invalid xml object for this resource!') exc.ignore = True raise exc self._xml_data = xmlutil.xml2obj( etree.tostring(xml_data, encoding='unicode'))
def get_final_progress_update(self, pu_message): """ Gets the final progress-update message for a job, based on the href inside the ``pu_message`` argument supplied. The ``pu_message`` arg is the last progress-update message obtained from a hornet-q and it does not contain the full result of the job. The progress-update message fetched and returned by this method will contain the full result inside the ``data`` field. """ job_pu_href = '/'.join([pu_message.job.get('href'), 'progress-update']) response = self._rest_end_point.get(job_pu_href) if response.status_code != 200: raise Exception("Failed in GET on %s" % job_pu_href) response_txt = xmlutil.get_text_from_response(response) response_txt = xmlutil.cleanup(response_txt) return xmlutil.xml2obj(response_txt)
def __init__(self, type_name, rest_end_point, xml_data=None, attributes=None, parent=None): """Initializes a Resource object. :param str type_name: Fully qualified type name for the Resource to be created. It is of the format ``<service_name>.<resource_type>`` or ``<app-name>.<service_name>.<resource_type>`` Some examples are: * ``device_management.device`` * ``user_management.user`` * ``servicenow.device_management.device`` :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 lxml.etree.Element xml_data: The state of the resource as an XML object. This defaults to ``None``. :param dict attributes: The state of the resource as a dict where the keys are attribute names and values are attribute values. This defaults to ``None``. :param parent: The parent object of this resource. This defaults to ``None``. :type parent: jnpr.space.collection.Collection """ self._type_name = type_name self._rest_end_point = rest_end_point self._xml_data = xml_data self._attributes = attributes self._parent = parent self._collections = {} self._methods = {} self._init_meta_data(rest_end_point, type_name) if xml_data is not None: if self._meta_object.xml_name != xml_data.tag: exc = Exception('Invalid xml object for this resource!') exc.ignore = True raise exc self._xml_data = xmlutil.xml2obj(etree.tostring(xml_data, encoding='unicode'))
def pull_message(self): """ Pull the next message from the hornet-q. It specifies an ``accept-wait`` header with the value that was given as the ``wait_time`` argument in the constructor so that the pull waits for this many seconds on the server side waiting for a message. :returns: If a message is pulled, it is parsed into a Python object and returned. If no message was pulled, returns ``None``. """ headers = {"accept-wait": self.wait_time} response = self._rest_end_point.post(self.next_msg_url, headers, body=None) next_msg = response.headers["msg-consume-next"] if len(next_msg) > 0: self.next_msg_url = self._strip_uri(next_msg) if response.status_code == 200: xml = xmlutil.get_text_from_response(response) return xmlutil.xml2obj(xml)
def get(self, accept=None): """Performs a GET corresponding to the Method object. :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. :returns: A Python object constructed from the response body that Space returned for the GET request. :raises: ``jnpr.space.rest.RestException`` if the GET results in an error response. The exception's ``response`` attribute will have the full response from Space. """ 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 = {} response = self._rest_end_point.get(self.get_href(), headers) if response.status_code != 200: raise rest.RestException("GET failed on %s " % self.get_href(), response) resp_text = xmlutil.get_text_from_response(response) return xmlutil.xml2obj(resp_text)
def get(self, accept=None): """Performs a GET corresponding to the Method object. :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. :returns: A Python object constructed from the response body that Space returned for the GET request. :raises: ``jnpr.space.rest.RestException`` if the GET results in an error response. The exception's ``response`` attribute will have the full response from Space. """ 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 = {} response = self._rest_end_point.get(self.get_href(), headers) if response.status_code != 200: raise rest.RestException("GET failed on %s " % self.get_href(), response) resp_text = xmlutil.get_text_from_response(response) return xmlutil.xml2obj(resp_text)
def _get_info(self): url = '/api/info?uri=' + self.get_href() response = self._rest_end_point.get(url) if response.status_code != 200: from . import rest raise rest.RestException("GET failed on %s" % url, response) obj = xmlutil.xml2obj(xmlutil.get_text_from_response(response)) """ Create a dict such as this: { methods: { DELETE: {} GET: { Accept: [header1, header2] } PUT: { Media-type Combinations: { 1: { Accept: header3 Content-Type: header4 } 2: { Accept: header5 Content-Type: header6 } } } POST: { Media-type Combinations: [ { Accept: header3 Content-Type: header4 } { Accept: header5 Content-Type: header6 } ] } } } """ info = {} methods = {} info['HTTP Methods'] = methods for m in obj['http-methods']['http-method']: method_name = m.get('type') if method_name in methods: method = methods[method_name] else: method = {} methods[method_name] = method if method_name in ['GET', 'DELETE']: for h in m.headers.header: header_name = h.get('type') if header_name in method: headers = method[header_name] else: headers = [] method[header_name] = headers for r in h.representations.representation: if '+xml' in r.text: headers.append(r.text) elif method_name in ['PUT', 'POST', 'PATCH']: if 'Media-type Combinations' not in method: combs = [] method['Media-type Combinations'] = combs else: combs = method['Media-type Combinations'] comb = {} combs.append(comb) for h in m.headers.header: header_name = h.get('type') header_val = '' for r in h.representations.representation: if '+xml' in r.text: header_val = r.text break comb[header_name] = header_val return info
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 put(self, new_val_obj=None, request_body=None, accept=None, content_type=None): """Modifies the state of this resource on Space by sending a PUT request with the new state. The attributes of *new_val_obj* are formatted to form the XML request body for the PUT request. If the parameter *new_val_obj* is ``None``, then the argument *request_body*, if present, is used as the request body. If this is also ``None``, then the attributes of this object itself are formatted to form the XML request body. Once the PUT request successfully completes, it re-initializes the state of this Resource object based on the XML response from Space. :param new_val_obj: A Resource object with the newly desired state for the resource. This defaults to ``None``. :type new_val_obj: jnpr.space.resource.Resource :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 SpaceEZ will create the request body using the supplied ``new_val_obj`` argument or from the current state of this object. :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. :returns: ``None`` :raises: ``jnpr.space.rest.RestException`` if the PUT method results in an error response. The exception's ``response`` attribute will have the full response from Space. """ if request_body is not None: body = request_body if new_val_obj is not None: raise ValueError('Cannot use both request_body and new_val_obj') elif new_val_obj is not None: body = etree.tostring(new_val_obj.form_xml(), encoding='unicode') else: body = etree.tostring(self.form_xml(), encoding='unicode') if content_type is not None: mtype = content_type else: mtype = self.get_meta_object().get_media_type(None) headers = {'content-type': mtype} if accept is not None: headers['accept'] = accept response = self._rest_end_point.put(self.get_href(), headers, body) if response.status_code != 200: raise rest.RestException("PUT failed on %s" % self.get_href(), 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 = etree.fromstring(response.content) # Fixing issue #19 self._xml_data = root resp_text = xmlutil.get_text_from_response(response) self._xml_data = xmlutil.xml2obj(resp_text)
def post(self, accept=None, content_type=None, request_body=None, task_monitor=None, schedule=None, *args, **kwargs): """ Some resources support the POST method. For example, the configuration of a device supports the POST method which can be used to fetch selected portions of the configuration based on xpath expressions. On such resources, this method can be used to send the POST request. :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 SpaceEZ will create the request body using the modeled template, replacing variables with kwargs. :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 :param str schedule: A string specifying a cron expression for scheduling the execution of the request on the Space server side. This is applicable only if the POST invocation on this resource has asynchronous semantics and you want to schedule the execution. Otherwise, this will default to ``None``. :param kwargs: Keyword args of the form name=value which will be used to substitute variables in a pre-defined template (if applicable) to form the request body. :type kwargs: A variable list of name=value arguments. :returns: A Python object constructed from the response body that Space returned for the POST method invocation. 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. """ url = self.get_href() if task_monitor is not None: url = '?queue='.join([url, task_monitor.get_queue_url()]) if schedule is not None: url = '&schedule='.join([url, schedule]) headers = {} if accept is not None: headers['accept'] = accept elif self._meta_object.response_type is not None: headers['accept'] = self._meta_object.response_type if content_type is not None: headers['content-type'] = content_type elif self._meta_object.request_type is not None: headers['content-type'] = self._meta_object.request_type if request_body is not None: body = request_body elif self._meta_object.request_template is not None: body = self._meta_object.request_template.render(**kwargs) else: body = None response = self._rest_end_point.post(url, headers, body) if (response.status_code != 202) and (response.status_code != 200): raise rest.RestException("POST failed on %s" % url, response) resp_text = xmlutil.get_text_from_response(response) resp_text = xmlutil.cleanup(resp_text) if self._meta_object.remove_junos_group: resp_text = xmlutil.remove_junos_group(resp_text) return xmlutil.xml2obj(resp_text)
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 put(self, new_val_obj=None, request_body=None, accept=None, content_type=None): """Modifies the state of this resource on Space by sending a PUT request with the new state. The attributes of *new_val_obj* are formatted to form the XML request body for the PUT request. If the parameter *new_val_obj* is ``None``, then the argument *request_body*, if present, is used as the request body. If this is also ``None``, then the attributes of this object itself are formatted to form the XML request body. Once the PUT request successfully completes, it re-initializes the state of this Resource object based on the XML response from Space. :param new_val_obj: A Resource object with the newly desired state for the resource. This defaults to ``None``. :type new_val_obj: jnpr.space.resource.Resource :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 SpaceEZ will create the request body using the supplied ``new_val_obj`` argument or from the current state of this object. :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. :returns: ``None`` :raises: ``jnpr.space.rest.RestException`` if the PUT method results in an error response. The exception's ``response`` attribute will have the full response from Space. """ if request_body is not None: body = request_body if new_val_obj is not None: raise ValueError( 'Cannot use both request_body and new_val_obj') elif new_val_obj is not None: body = etree.tostring(new_val_obj.form_xml(), encoding='unicode') else: body = etree.tostring(self.form_xml(), encoding='unicode') if content_type is not None: mtype = content_type else: mtype = self.get_meta_object().get_media_type(None) headers = {'content-type': mtype} if accept is not None: headers['accept'] = accept response = self._rest_end_point.put(self.get_href(), headers, body) if response.status_code != 200: raise rest.RestException("PUT failed on %s" % self.get_href(), 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 = etree.fromstring(response.content) # Fixing issue #19 self._xml_data = root resp_text = xmlutil.get_text_from_response(response) self._xml_data = xmlutil.xml2obj(resp_text)