def power_get_do(self, target): """Get the power status for the outlet The unit returns the power state when querying the ``/index.htm`` path...as a comment inside the HTML body of the respose. *Chuckle* So we look for:: <!-- state=XY lock=ANY --> *XY* is the hex bitmap of states against the outlet number. *ANY* is the hex lock bitmap (outlets that can't change). """ r = requests.get(self.url + "/index.htm") commonl.request_response_maybe_raise(r) m = self.state_regex.search(r.content) if not m: raise Exception("Cannot find '%s' in power switch response" % self.state_regex.pattern) state = int(m.group('state'), base = 16) # Note outlet numbers are base-1... if state & (1 << self.outlet - 1) == 0: return False else: return True
def get(self, target, component): """Get the power status for the outlet The unit returns the power state when querying the ``/index.htm`` path...as a comment inside the HTML body of the respose. *Chuckle* So we look for:: <!-- state=XY lock=ANY --> *XY* is the hex bitmap of states against the outlet number. *ANY* is the hex lock bitmap (outlets that can't change). """ r = requests.get(self.url + "/index.htm") commonl.request_response_maybe_raise(r) m = self.state_regex.search(r.content) if not m: raise Exception("Cannot find '%s' in power switch response" % self.state_regex.pattern) state = int(m.group('state'), base=16) # Note outlet numbers are base-1... if state & (1 << self.outlet - 1) == 0: return False else: return True
def power_cycle_do(self, target, wait=0): if self.power_get_do(target): r = requests.get(self.url + "/outlet?%d=OFF" % self.outlet) commonl.request_response_maybe_raise(r) if self.reboot_wait_s > 0 or wait > 0: time.sleep(max(self.reboot_wait_s, wait)) r = requests.get(self.url + "/outlet?%d=ON" % self.outlet) commonl.request_response_maybe_raise(r) # Give it time to settle time.sleep(self.reboot_wait_s) self.power_get_do(target)
def power_cycle_do(self, target, wait = 0): if self.power_get_do(target): r = requests.get(self.url + "/outlet?%d=OFF" % self.outlet) commonl.request_response_maybe_raise(r) if self.reboot_wait_s > 0 or wait > 0: time.sleep(max(self.reboot_wait_s, wait)) r = requests.get(self.url + "/outlet?%d=ON" % self.outlet) commonl.request_response_maybe_raise(r) # Give it time to settle time.sleep(self.reboot_wait_s) self.power_get_do(target)
def send_request(self, method, url, data=None, json=None, files=None, stream=False, raw=False, timeout=160): """ Send request to server using url and data, save the cookies generated from request, search for issues on connection and raise and exception or return the response object. :param str url: url to request :param dict data: args to send in the request. default None :param str method: method used to request GET, POST and PUT. Defaults to PUT. :param bool raise_error: if true, raise an error if something goes wrong in the request. default True :returns requests.Response: response object """ assert not (data and json), \ "can't specify data and json at the same time" # create the url to send request based on API version if not self._base_url: self._base_url = urlparse.urljoin(self._url, rest_target_broker.API_PREFIX) if url.startswith("/"): url = url[1:] url_request = urlparse.urljoin(self._base_url, url) logger.debug("send_request: %s %s", method, url_request) with self.lock: cookies = self.cookies session = tls_var("session", requests.Session) if method == 'GET': r = session.get(url_request, cookies=cookies, json=json, data=data, verify=self.verify_ssl, stream=stream, timeout=(timeout, timeout)) elif method == 'PATCH': r = session.patch(url_request, cookies=cookies, json=json, data=data, verify=self.verify_ssl, stream=stream, timeout=(timeout, timeout)) elif method == 'POST': r = session.post(url_request, cookies=cookies, json=json, data=data, files=files, verify=self.verify_ssl, stream=stream, timeout=(timeout, timeout)) elif method == 'PUT': r = session.put(url_request, cookies=cookies, json=json, data=data, verify=self.verify_ssl, stream=stream, timeout=(timeout, timeout)) elif method == 'DELETE': r = session.delete(url_request, cookies=cookies, json=json, data=data, verify=self.verify_ssl, stream=stream, timeout=(timeout, timeout)) else: raise Exception("Unknown method '%s'" % method) #update cookies if len(r.cookies) > 0: with self.lock: # Need to update like this because r.cookies is not # really a dict, but supports iteritems() -- overwrite # existing cookies (session cookie) and keep old, as # it will have the stuff we need to auth with the # server (like the remember_token) # FIXME: maybe filter to those two only? for cookie, value in r.cookies.iteritems(): self.cookies[cookie] = value commonl.request_response_maybe_raise(r) if raw: return r rdata = r.json(object_pairs_hook=collections.OrderedDict) if '_diagnostics' in rdata: diagnostics = rdata.pop('_diagnostics').encode("utf-8", 'replace') # this is not very...memory efficient for line in diagnostics.split("\n"): logger.warning("diagnostics: " + line) return rdata
def off(self, target, _component): r = requests.get(self.url + "/outlet?%d=OFF" % self.outlet) commonl.request_response_maybe_raise(r)
def power_off_do(self, target): r = requests.get(self.url + "/outlet?%d=OFF" % self.outlet) commonl.request_response_maybe_raise(r) self.power_get_do(target)
def power_on_do(self, target): r = requests.get(self.url + "/outlet?%d=ON" % self.outlet) commonl.request_response_maybe_raise(r) self.power_get_do(target) time.sleep(self.reboot_wait_s)
def send_request(self, method, url, data=None, json=None, files=None, stream=False, raw=False, timeout=160, retry_timeout=0, retry_backoff=0.5, skip_prefix=False): """ Send request to server using url and data, save the cookies generated from request, search for issues on connection and raise and exception or return the response object. :param str url: url to request :param dict data: args to send in the request. default None :param str method: method used to request GET, POST and PUT. Defaults to PUT. :param bool raise_error: if true, raise an error if something goes wrong in the request. default True :param float retry_timeout: (optional, default 0--disabled) how long (in seconds) to retry connections in case of failure (:class:`requests.exceptions.ConnectionError`, :class:`requests.exceptions.ReadTimeout`) Note a retry can have side effects if the request is not idempotent (eg: writing to a console); retries shall only be enabled for GET calls. Support for non-idempotent calls has to be added to the protocol. See also :meth:`tcfl.tc.target_c.ttbd_iface_call` :returns requests.Response: response object """ assert not (data and json), \ "can't specify data and json at the same time" assert isinstance(retry_timeout, (int, float)) and retry_timeout >= 0, \ f"retry_timeout: {retry_timeout} has to be an int/float >= 0" assert isinstance(retry_backoff, (int, float)) and retry_backoff > 0 if retry_timeout > 0: assert retry_backoff < retry_timeout, \ f"retry_backoff {retry_backoff} has to be" \ f" smaller than retry_timeout {retry_timeout}" # create the url to send request based on API version if url.startswith("/"): url = url[1:] if not self._base_url: self._base_url = urllib.parse.urljoin( self._url, rest_target_broker.API_PREFIX) if skip_prefix: url_request = urllib.parse.urljoin(self.parsed_url.geturl(), url) else: url_request = urllib.parse.urljoin(self._base_url, url) logger.debug("send_request: %s %s", method, url_request) with self.lock: cookies = self.cookies session = tls_var("session", requests.Session) retry_count = -1 retry_ts = None r = None while True: retry_count += 1 try: if method == 'GET': r = session.get(url_request, cookies=cookies, json=json, data=data, verify=self.verify_ssl, stream=stream, timeout=(timeout, timeout)) elif method == 'PATCH': r = session.patch(url_request, cookies=cookies, json=json, data=data, verify=self.verify_ssl, stream=stream, timeout=(timeout, timeout)) elif method == 'POST': r = session.post(url_request, cookies=cookies, json=json, data=data, files=files, verify=self.verify_ssl, stream=stream, timeout=(timeout, timeout)) elif method == 'PUT': r = session.put(url_request, cookies=cookies, json=json, data=data, verify=self.verify_ssl, stream=stream, timeout=(timeout, timeout)) elif method == 'DELETE': r = session.delete(url_request, cookies=cookies, json=json, data=data, verify=self.verify_ssl, stream=stream, timeout=(timeout, timeout)) else: raise AssertionError("{method}: unknown HTTP method") break except ( requests.exceptions.ConnectionError, requests.exceptions.ReadTimeout, ) as e: # Retry only these; note these cannot report # Report how many retries we have done, wait a backoff # and then loop it. if retry_timeout == 0: raise ts = time.time() if retry_ts == None: retry_ts = ts # first one else: if ts - retry_ts > retry_timeout: raise RuntimeError( f"{url_request}: giving up after {retry_timeout}s" f" retrying {retry_count} connection errors" ) from e time.sleep(retry_backoff) # increase the backoff to avoid pestering too much, # but make sure it doesn't get too big so we at least # get 10 tries in the timeout period if retry_backoff < retry_timeout / 10: retry_backoff *= 1.2 continue #update cookies if len(r.cookies) > 0: with self.lock: # Need to update like this because r.cookies is not # really a dict, but supports items() -- overwrite # existing cookies (session cookie) and keep old, as # it will have the stuff we need to auth with the # server (like the remember_token) # FIXME: maybe filter to those two only? for cookie, value in r.cookies.items(): self.cookies[cookie] = value commonl.request_response_maybe_raise(r) if raw: return r rdata = r.json(object_pairs_hook=collections.OrderedDict) if '_diagnostics' in rdata: diagnostics = rdata.pop('_diagnostics') # this is not very...memory efficient for line in diagnostics.split("\n"): logger.warning("diagnostics: " + line) return rdata
def send_request(self, method, url, data=None, files=None, stream=False, raw=False, timeout=480): """ Send request to server using url and data, save the cookies generated from request, search for issues on connection and raise and exception or return the response object. :param url: url to request :type url: str :param data: args to send in the request. default None :type data: dict :param method: method used to request GET, POST and PUT. default PUT :type method: str :param raise_error: if true, raise an error if something goes wrong in the request. default True :type raise_error: bool :returns: response object :rtype: requests.Response """ # create the url to send request based on API version if not self._base_url: self._base_url = urlparse.urljoin(self._url, rest_target_broker.API_PREFIX) if url.startswith("/"): url = url[1:] url_request = urlparse.urljoin(self._base_url, url) logger.debug("send_request: %s %s", method, url_request) with self.lock: cookies = self.cookies session = tls_var("session", requests.Session) if method == 'GET': r = session.get(url_request, cookies=cookies, data=data, verify=self.verify_ssl, stream=stream, timeout=timeout) elif method == 'POST': r = session.post(url_request, cookies=cookies, data=data, files=files, verify=self.verify_ssl, stream=stream, timeout=timeout) elif method == 'PUT': r = session.put(url_request, cookies=cookies, data=data, verify=self.verify_ssl, stream=stream, timeout=timeout) elif method == 'DELETE': r = session.delete(url_request, cookies=cookies, data=data, verify=self.verify_ssl, stream=stream, timeout=timeout) else: raise Exception("Unknown method '%s'" % method) #update cookies if len(r.cookies) > 0: with self.lock: # Need to update like this because r.cookies is not # really a dict, but supports iteritems() -- overwrite # existing cookies (session cookie) and keep old, as # it will have the stuff we need to auth with the # server (like the remember_token) # FIXME: maybe filter to those two only? for cookie, value in r.cookies.iteritems(): self.cookies[cookie] = value commonl.request_response_maybe_raise(r) if raw: return r rdata = r.json() diagnostics = rdata.get('diagnostics', "").encode("utf-8", 'replace') if diagnostics != "": for line in diagnostics.split("\n"): logger.warning("diagnostics: " + line) return rdata