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
示例#3
0
    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
示例#5
0
    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)
示例#7
0
    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