def test_request_resources_error_on_network_disconnected( self, m_request_resources, FakeConfig): """Raise error get_available_resources can't contact backend""" cfg = FakeConfig() urlerror = exceptions.UrlError( socket.gaierror(-2, "Name or service not known")) m_request_resources.side_effect = urlerror with pytest.raises(exceptions.UrlError) as exc: get_available_resources(cfg) assert urlerror == exc.value
def _unattached_status(cfg: UAConfig) -> Dict[str, Any]: """Return unattached status as a dict.""" response = copy.deepcopy(DEFAULT_STATUS) response["version"] = version.get_version(features=cfg.features) resources = get_available_resources(cfg) for resource in resources: if resource.get("available"): available = UserFacingAvailability.AVAILABLE.value else: available = UserFacingAvailability.UNAVAILABLE.value try: ent_cls = entitlement_factory( cfg=cfg, name=resource.get("name", "") ) except exceptions.EntitlementNotFoundError: LOG.debug( messages.AVAILABILITY_FROM_UNKNOWN_SERVICE.format( service=resource.get("name", "without a 'name' key") ) ) continue response["services"].append( { "name": resource.get("presentedAs", resource["name"]), "description": ent_cls.description, "available": available, } ) response["services"].sort(key=lambda x: x.get("name", "")) return response
def _unattached_status(self) -> "Dict[str, Any]": """Return unattached status as a dict.""" from uaclient.contract import get_available_resources from uaclient.entitlements import ENTITLEMENT_CLASS_BY_NAME response = copy.deepcopy(DEFAULT_STATUS) resources = get_available_resources(self) for resource in sorted(resources, key=lambda x: x["name"]): if resource["available"]: available = status.UserFacingAvailability.AVAILABLE.value else: available = status.UserFacingAvailability.UNAVAILABLE.value ent_cls = ENTITLEMENT_CLASS_BY_NAME.get(resource["name"]) if not ent_cls: LOG.debug( "Ignoring availability of unknown service %s" " from contract server", resource["name"], ) continue response["services"].append({ "name": resource["name"], "description": ent_cls.description, "available": available, }) return response
def help(cfg, name): """Return help information from an uaclient service as a dict :param name: Name of the service for which to return help data. :raises: UserFacingError when no help is available. """ resources = get_available_resources(cfg) help_resource = None # We are using an OrderedDict here to guarantee # that if we need to print the result of this # dict, the order of insertion will always be respected response_dict = OrderedDict() response_dict["name"] = name for resource in resources: if resource["name"] == name or resource.get("presentedAs") == name: try: help_ent_cls = entitlement_factory( cfg=cfg, name=resource["name"] ) except exceptions.EntitlementNotFoundError: continue help_resource = resource help_ent = help_ent_cls(cfg) break if help_resource is None: raise exceptions.UserFacingError( "No help available for '{}'".format(name) ) if cfg.is_attached: service_status = _attached_service_status(help_ent, {}) status_msg = service_status["status"] response_dict["entitled"] = service_status["entitled"] response_dict["status"] = status_msg if status_msg == "enabled" and help_ent_cls.is_beta: response_dict["beta"] = True else: if help_resource["available"]: available = UserFacingAvailability.AVAILABLE.value else: available = UserFacingAvailability.UNAVAILABLE.value response_dict["available"] = available response_dict["help"] = help_ent.help_info return response_dict
def help(self, name): """Return help information from an uaclient service as a dict :param name: Name of the service for which to return help data. :raises: UserFacingError when no help is available. """ from uaclient.contract import get_available_resources from uaclient.entitlements import ENTITLEMENT_CLASS_BY_NAME resources = get_available_resources(self) help_resource = None # We are using an OrderedDict here to guarantee # that if we need to print the result of this # dict, the order of insertion will always be respected response_dict = OrderedDict() response_dict["name"] = name for resource in resources: if resource["name"] == name and name in ENTITLEMENT_CLASS_BY_NAME: help_resource = resource help_ent_cls = ENTITLEMENT_CLASS_BY_NAME.get(name) help_ent = help_ent_cls(self) break if help_resource is None: raise exceptions.UserFacingError( "No help available for '{}'".format(name)) if self.is_attached: service_status = self._attached_service_status(help_ent, {}) status_msg = service_status["status"] response_dict["entitled"] = service_status["entitled"] response_dict["status"] = status_msg if status_msg == "enabled" and help_ent_cls.is_beta: response_dict["beta"] = True else: if help_resource["available"]: available = status.UserFacingAvailability.AVAILABLE.value else: available = status.UserFacingAvailability.UNAVAILABLE.value response_dict["available"] = available response_dict["help"] = help_ent.help_info return response_dict
def test_request_resources_from_contract_server(self, client, FakeConfig): """Call UAContractClient.request_resources to get updated resources.""" cfg = FakeConfig() url = API_V1_RESOURCES new_resources = [{"name": "new_resource", "available": False}] def fake_contract_client(cfg): fake_client = FakeContractClient(cfg) fake_client._responses = {url: {"resources": new_resources}} return fake_client client.side_effect = fake_contract_client assert new_resources == get_available_resources(cfg)
def test_request_resources_from_contract_server(self, client): """Call UAContractClient.request_resources to get updated resources.""" cfg = FakeConfig() platform = util.get_platform_info() resource_params = { "architecture": platform["arch"], "series": platform["series"], "kernel": platform["kernel"], } url = API_V1_RESOURCES + "?" + urllib.parse.urlencode(resource_params) new_resources = [{"name": "new_resource", "available": False}] def fake_contract_client(cfg): fake_client = FakeContractClient(cfg) fake_client._responses = {url: {"resources": new_resources}} return fake_client client.side_effect = fake_contract_client assert new_resources == get_available_resources(cfg)
def _attached_status(self) -> "Dict[str, Any]": """Return configuration of attached status as a dictionary.""" from uaclient.contract import get_available_resources from uaclient.entitlements import ENTITLEMENT_CLASSES response = copy.deepcopy(DEFAULT_STATUS) contractInfo = self.machine_token["machineTokenInfo"]["contractInfo"] response.update({ "attached": True, "account": self.accounts[0]["name"], "account-id": self.accounts[0]["id"], "origin": contractInfo.get("origin"), "subscription": contractInfo["name"], "subscription-id": contractInfo["id"], }) if contractInfo.get("effectiveTo"): response["expires"] = datetime.strptime( contractInfo["effectiveTo"], "%Y-%m-%dT%H:%M:%SZ") resources = self.machine_token.get("availableResources") if not resources: resources = get_available_resources(self) inapplicable_resources = { resource["name"]: resource.get("description") for resource in sorted(resources, key=lambda x: x["name"]) if not resource["available"] } for ent_cls in ENTITLEMENT_CLASSES: ent = ent_cls(self) response["services"].append( self._attached_service_status(ent, inapplicable_resources)) support = self.entitlements.get("support", {}).get("entitlement") if support: supportLevel = support.get("affordances", {}).get("supportLevel") if not supportLevel: supportLevel = DEFAULT_STATUS["techSupportLevel"] response["techSupportLevel"] = supportLevel return response
def simulate_status( cfg, token: str, show_beta: bool = False ) -> Tuple[Dict[str, Any], int]: """Get a status dictionary based on a token. Returns a tuple with the status dictionary and an integer value - 0 for success, 1 for failure """ ret = 0 response = copy.deepcopy(DEFAULT_STATUS) try: contract_information = get_contract_information(cfg, token) except exceptions.ContractAPIError as e: if hasattr(e, "code") and e.code == 401: raise exceptions.UserFacingError( msg=messages.ATTACH_INVALID_TOKEN.msg, msg_code=messages.ATTACH_INVALID_TOKEN.name, ) raise e contract_info = contract_information.get("contractInfo", {}) account_info = contract_information.get("accountInfo", {}) response.update( { "version": version.get_version(features=cfg.features), "contract": { "id": contract_info.get("id", ""), "name": contract_info.get("name", ""), "created_at": contract_info.get("createdAt", ""), "products": contract_info.get("products", []), }, "account": { "name": account_info.get("name", ""), "id": account_info.get("id"), "created_at": account_info.get("createdAt", ""), "external_account_ids": account_info.get( "externalAccountIDs", [] ), }, "simulated": True, } ) now = datetime.now(timezone.utc) if contract_info.get("effectiveTo"): response["expires"] = contract_info.get("effectiveTo") expiration_datetime = util.parse_rfc3339_date(response["expires"]) delta = expiration_datetime - now if delta.total_seconds() <= 0: message = messages.ATTACH_FORBIDDEN_EXPIRED.format( contract_id=response["contract"]["id"], date=expiration_datetime.strftime(ATTACH_FAIL_DATE_FORMAT), ) event.error(error_msg=message.msg, error_code=message.name) event.info("This token is not valid.\n" + message.msg + "\n") ret = 1 if contract_info.get("effectiveFrom"): response["effective"] = contract_info.get("effectiveFrom") effective_datetime = util.parse_rfc3339_date(response["effective"]) delta = now - effective_datetime if delta.total_seconds() <= 0: message = messages.ATTACH_FORBIDDEN_NOT_YET.format( contract_id=response["contract"]["id"], date=effective_datetime.strftime(ATTACH_FAIL_DATE_FORMAT), ) event.error(error_msg=message.msg, error_code=message.name) event.info("This token is not valid.\n" + message.msg + "\n") ret = 1 status_cache = cfg.read_cache("status-cache") if status_cache: resources = status_cache.get("services") else: resources = get_available_resources(cfg) entitlements = contract_info.get("resourceEntitlements", []) inapplicable_resources = [ resource["name"] for resource in sorted(resources, key=lambda x: x["name"]) if not resource["available"] ] for resource in resources: entitlement_name = resource.get("name", "") try: ent_cls = entitlement_factory(cfg=cfg, name=entitlement_name) except exceptions.EntitlementNotFoundError: continue ent = ent_cls(cfg=cfg) entitlement_information = _get_entitlement_information( entitlements, entitlement_name ) response["services"].append( { "name": resource.get("presentedAs", ent.name), "description": ent.description, "entitled": entitlement_information["entitled"], "auto_enabled": entitlement_information["auto_enabled"], "available": "yes" if ent.name not in inapplicable_resources else "no", } ) response["services"].sort(key=lambda x: x.get("name", "")) support = _get_entitlement_information(entitlements, "support") if support["entitled"]: supportLevel = support["affordances"].get("supportLevel") if supportLevel: response["contract"]["tech_support_level"] = supportLevel response.update(_get_config_status(cfg)) response = _handle_beta_resources(cfg, show_beta, response) return response, ret
def _attached_status(cfg) -> Dict[str, Any]: """Return configuration of attached status as a dictionary.""" cfg.remove_notice( "", messages.NOTICE_DAEMON_AUTO_ATTACH_LOCK_HELD.format(operation=".*"), ) cfg.remove_notice("", messages.NOTICE_DAEMON_AUTO_ATTACH_FAILED) response = copy.deepcopy(DEFAULT_STATUS) machineTokenInfo = cfg.machine_token["machineTokenInfo"] contractInfo = machineTokenInfo["contractInfo"] tech_support_level = UserFacingStatus.INAPPLICABLE.value response.update( { "version": version.get_version(features=cfg.features), "machine_id": machineTokenInfo["machineId"], "attached": True, "origin": contractInfo.get("origin"), "notices": cfg.read_cache("notices") or [], "contract": { "id": contractInfo["id"], "name": contractInfo["name"], "created_at": contractInfo.get("createdAt", ""), "products": contractInfo.get("products", []), "tech_support_level": tech_support_level, }, "account": { "name": cfg.accounts[0]["name"], "id": cfg.accounts[0]["id"], "created_at": cfg.accounts[0].get("createdAt", ""), "external_account_ids": cfg.accounts[0].get( "externalAccountIDs", [] ), }, } ) if contractInfo.get("effectiveTo"): response["expires"] = cfg.contract_expiry_datetime if contractInfo.get("effectiveFrom"): response["effective"] = contractInfo["effectiveFrom"] resources = cfg.machine_token.get("availableResources") if not resources: resources = get_available_resources(cfg) inapplicable_resources = { resource["name"]: resource.get("description") for resource in sorted(resources, key=lambda x: x.get("name", "")) if not resource.get("available") } for resource in resources: try: ent_cls = entitlement_factory( cfg=cfg, name=resource.get("name", "") ) except exceptions.EntitlementNotFoundError: continue ent = ent_cls(cfg) response["services"].append( _attached_service_status(ent, inapplicable_resources) ) response["services"].sort(key=lambda x: x.get("name", "")) support = cfg.entitlements.get("support", {}).get("entitlement") if support: supportLevel = support.get("affordances", {}).get("supportLevel") if supportLevel: response["contract"]["tech_support_level"] = supportLevel return response