def test_attach_subscription_task_success(self, environ_get): """Test the ParseAttachedSubscriptionsTask.""" # prepare mock proxies the task is expected to interact with rhsm_entitlement_proxy = Mock() rhsm_entitlement_proxy.GetPools.return_value = "foo" rhsm_syspurpose_proxy = Mock() rhsm_syspurpose_proxy.GetSyspurpose.return_value = "bar" task = ParseAttachedSubscriptionsTask( rhsm_entitlement_proxy=rhsm_entitlement_proxy, rhsm_syspurpose_proxy=rhsm_syspurpose_proxy) # mock the parsing methods subscription1 = AttachedSubscription() subscription2 = AttachedSubscription() task._parse_subscription_json = Mock() task._parse_subscription_json.return_value = [ subscription1, subscription2 ] system_purpose_data = SystemPurposeData() task._parse_system_purpose_json = Mock() task._parse_system_purpose_json.return_value = system_purpose_data # run the task result = task.run() # check DBus proxies were called as expected rhsm_entitlement_proxy.GetPools.assert_called_once_with( {'pool_subsets': get_variant(Str, "consumed")}, {}, "en_US.UTF-8") rhsm_syspurpose_proxy.GetSyspurpose.assert_called_once_with( "en_US.UTF-8") # check the parsing methods were called task._parse_subscription_json.assert_called_once_with("foo") task._parse_system_purpose_json.assert_called_once_with("bar") # check the result that has been returned is as expected self.assertEqual(result.attached_subscriptions, [subscription1, subscription2]) self.assertEqual(result.system_purpose_data, system_purpose_data)
def subscription_json_parsing_test(self): """Test the subscription JSON parsing method of ParseAttachedSubscriptionsTask.""" parse_method = ParseAttachedSubscriptionsTask._parse_subscription_json # the method should be able to survive the RHSM DBus API returning an empty string, # as empty list of subscriptions is a lesser issue than crashed installation self.assertEqual(parse_method(""), []) # try parsing a json file containing two subscriptions # - to make this look sane, we write it as a dict that we then convert to JSON subscription_dict = { "consumed": [{ "subscription_name": "Foo Bar Beta", "service_level": "very good", "sku": "ABC1234", "contract": "12345678", "starts": "05/12/20", "ends": "05/12/21", "quantity_used": "1" }, { "subscription_name": "Foo Bar Beta NG", "service_level": "even better", "sku": "ABC4321", "contract": "87654321", "starts": "now", "ends": "never", "quantity_used": "1000" }] } subscription_json = json.dumps(subscription_dict) expected_structs = [{ "name": "Foo Bar Beta", "service-level": "very good", "sku": "ABC1234", "contract": "12345678", "start-date": "May 12, 2020", "end-date": "May 12, 2021", "consumed-entitlement-count": 1 }, { "name": "Foo Bar Beta NG", "service-level": "even better", "sku": "ABC4321", "contract": "87654321", "start-date": "now", "end-date": "never", "consumed-entitlement-count": 1000 }] structs = get_native( AttachedSubscription.to_structure_list( parse_method(subscription_json))) # check the content of the AttachedSubscription corresponds to the input JSON, # including date formatting self.assertEqual(structs, expected_structs)
def _update_subscription_state(self): """Update state of the subscription related part of the spoke. Update state of the part of the spoke, that shows data about the currently attached subscriptions. """ # authentication method if self.authentication_method == AuthenticationMethod.USERNAME_PASSWORD: method_string = _("Registered with account {}").format( self.subscription_request.account_username) else: # org + key method_string = _("Registered with organization {}").format( self.subscription_request.organization) self._method_status_label.set_text(method_string) # final syspurpose data # role final_role_string = _("Role: {}").format(self.system_purpose_data.role) self._role_status_label.set_text(final_role_string) # SLA final_sla_string = _("SLA: {}").format(self.system_purpose_data.sla) self._sla_status_label.set_text(final_sla_string) # usage final_usage_string = _("Usage: {}").format( self.system_purpose_data.usage) self._usage_status_label.set_text(final_usage_string) # Insights # - this strings are referring to the desired target system state, # the installation environment itself is not expected to be # connected to Insights if self._subscription_module.InsightsEnabled: insights_string = _("Connected to Red Hat Insights") else: insights_string = _("Not connected to Red Hat Insights") self._insights_status_label.set_text(insights_string) # get attached subscriptions as a list of structs attached_subscriptions = self._subscription_module.AttachedSubscriptions # turn the structs to more useful AttachedSubscription instances attached_subscriptions = AttachedSubscription.from_structure_list( attached_subscriptions) # check how many we have & set the subscription status string accordingly subscription_count = len(attached_subscriptions) if subscription_count == 0: subscription_string = _( "No subscriptions are attached to the system") elif subscription_count == 1: subscription_string = _("1 subscription attached to the system") else: subscription_string = _("{} subscriptions attached to the system" ).format(subscription_count) self._attached_subscriptions_label.set_text(subscription_string) # populate the attached subscriptions listbox populate_attached_subscriptions_listbox(self._subscriptions_listbox, attached_subscriptions)
def _parse_subscription_json(cls, subscription_json): """Parse the JSON into list of AttachedSubscription instances. The expected JSON is at top level a list of rather complex dictionaries, with each dictionary describing a single subscription that has been attached to the system. :param str subscription_json: JSON describing what subscriptions have been attached :return: list of attached subscriptions :rtype: list of AttachedSubscription instances """ attached_subscriptions = [] try: subscriptions = json.loads(subscription_json) except json.decoder.JSONDecodeError: log.warning("subscription: failed to parse GetPools() JSON output") # empty attached subscription list is better than an installation # ending crash return [] # find the list of subscriptions consumed_subscriptions = subscriptions.get("consumed", []) log.debug("subscription: parsing %d attached subscriptions", len(consumed_subscriptions)) # split the list of subscriptions into separate subscription dictionaries for subscription_info in consumed_subscriptions: attached_subscription = AttachedSubscription() # user visible product name attached_subscription.name = subscription_info.get( "subscription_name", _("product name unknown")) # subscription support level # - this does *not* seem to directly correlate to system purpose SLA attribute attached_subscription.service_level = subscription_info.get( "service_level", _("unknown")) # SKU # - looks like productId == SKU in this JSON output attached_subscription.sku = subscription_info.get( "sku", _("unknown")) # contract number attached_subscription.contract = subscription_info.get( "contract", _("not available")) # subscription start date # - convert the raw date data from JSON to something more readable start_date = subscription_info.get("starts", _("unknown")) attached_subscription.start_date = cls._pretty_date(start_date) # subscription end date # - convert the raw date data from JSON to something more readable end_date = subscription_info.get("ends", _("unknown")) attached_subscription.end_date = cls._pretty_date(end_date) # consumed entitlements # - this seems to correspond to the toplevel "quantity" key, # not to the pool-level "consumed" key for some reason # *or* the pool-level "quantity" key quantity_string = int(subscription_info.get("quantity_used", 1)) attached_subscription.consumed_entitlement_count = quantity_string # add attached subscription to the list attached_subscriptions.append(attached_subscription) # return the list of attached subscriptions return attached_subscriptions
def AttachedSubscriptions(self) -> List[Structure]: """Return a list of DBus structures holding data about attached subscriptions.""" return AttachedSubscription.to_structure_list( self.implementation.attached_subscriptions)