Exemplo n.º 1
0
    def perform(self, include_result=False, passthrough_gone=False):
        """
        Perform the action that this Command represents.
        :return:
        """
        result = {}
        consumer_uuid = inj.require(inj.IDENTITY).uuid

        try:
            store = SyncedStore(
                uep=self.uep, consumer_uuid=consumer_uuid, on_changed=self.report.record_change
            )
            result = store.sync()
        except ConnectionException as e:
            # In case the error is GoneException (i.e. the consumer no more
            # exists), then reraise it only if GoneException is handled in
            # its own way rather than checking SyspurposeSyncActionReport.
            if isinstance(e, GoneException) and passthrough_gone:
                raise
            self.report._exceptions.append("Unable to sync syspurpose with server: %s" % str(e))
            self.report._status = "Failed to sync system purpose"
        self.report._updates = "\n\t\t ".join(self.report._updates)
        log.debug("Syspurpose updated: %s" % self.report)
        if not include_result:
            return self.report
        else:
            return self.report, result
Exemplo n.º 2
0
    def perform(self, include_result=False):
        """
        Perform the action that this Command represents.
        :return:
        """
        result = {}
        consumer_uuid = inj.require(inj.IDENTITY).uuid

        try:
            store = SyncedStore(
                uep=self.uep,
                consumer_uuid=consumer_uuid,
                report=self.report,
                on_changed=self.report.record_change
            )
            result = store.sync()
        except ConnectionException as e:
            self.report._exceptions.append('Unable to sync syspurpose with server: %s' % str(e))
            self.report._status = 'Failed to sync system purpose'
        self.report._updates = "\n\t\t ".join(self.report._updates)
        log.debug("Syspurpose updated: %s" % self.report)
        if not include_result:
            return self.report
        else:
            return self.report, result
Exemplo n.º 3
0
    def _do_command(self):
        """
        Own implementation of all actions
        :return: None
        """
        self._validate_options()

        content = {}
        if self.options.show is True:
            if self.is_registered():
                try:
                    self.cp = self.cp_provider.get_consumer_auth_cp()
                except connection.RestlibException as err:
                    log.exception(err)
                    log.debug(
                        "Error: Unable to retrieve system purpose from server")
                except Exception as err:
                    log.debug(
                        "Error: Unable to retrieve system purpose from server: {err}"
                        .format(err=err))
                else:
                    self.store = SyncedStore(uep=self.cp,
                                             consumer_uuid=self.identity.uuid)
                    sync_result = self.store.sync()
                    content = sync_result.result
            else:
                content = syspurposelib.read_syspurpose()
            print(
                json.dumps(content,
                           indent=2,
                           ensure_ascii=False,
                           sort_keys=True))
Exemplo n.º 4
0
    def _do_command(self):
        self._validate_options()

        self.cp = None
        try:
            # If we have a username/password, we're going to use that, otherwise
            # we'll use the identity certificate. We already know one or the other
            # exists:
            if self.options.token:
                try:
                    self.cp = self.cp_provider.get_keycloak_auth_cp(
                        self.options.token)
                except Exception as err:
                    log.error(
                        "unable to connect to candlepin server using token: \"{token}\", err: {err}"
                        .format(token=self.options.token, err=err))
                    print(_("Unable to connect to server using token"))
            elif self.options.username and self.options.password:
                self.cp_provider.set_user_pass(self.options.username,
                                               self.options.password)
                self.cp = self.cp_provider.get_basic_auth_cp()
            else:
                # get an UEP as consumer
                if self.is_registered():
                    self.cp = self.cp_provider.get_consumer_auth_cp()
        except connection.RestlibException as err:
            log.exception(err)
            if getattr(self.options, 'list', None):
                log.error(
                    "Error: Unable to retrieve {attr} from server: {err}".
                    format(attr=self.attr, err=err))
                system_exit(os.EX_SOFTWARE, str(err))
            else:
                log.debug(
                    "Error: Unable to retrieve {attr} from server: {err}".
                    format(attr=self.attr, err=err))
        except Exception as err:
            log.debug(
                "Error: Unable to retrieve {attr} from server: {err}".format(
                    attr=self.attr, err=err))

        self.store = SyncedStore(uep=self.cp, consumer_uuid=self.identity.uuid)

        if getattr(self.options, 'unset', None):
            self.unset()
        elif getattr(self.options, 'set', None):
            self.set()
        elif hasattr(self.options, 'to_add') and len(self.options.to_add) > 0:
            self.add()
        elif hasattr(self.options,
                     'to_remove') and len(self.options.to_remove) > 0:
            self.remove()
        elif getattr(self.options, 'list', None):
            self.list()
        elif getattr(self.options, 'show', None):
            self.show()
        else:
            self.show()
Exemplo n.º 5
0
def main():
    """
    Run the cli (Do the syspurpose tool thing!!)
    :return: 0
    """
    log.debug("Running the syspurpose utility...")

    parser = setup_arg_parser()
    args = parser.parse_args()

    # Syspurpose is not intended to be used in containers for the time being (could change later).
    if in_container():
        print(
            _("WARNING: Setting syspurpose in containers has no effect."
              "Please run syspurpose on the host.\n"))

    try:
        from subscription_manager.identity import Identity
        from subscription_manager.cp_provider import CPProvider
        identity = Identity()
        uuid = identity.uuid
        uep = CPProvider().get_consumer_auth_cp()
    except ImportError:
        uuid = None
        uep = None
        print(
            _("Warning: Unable to sync system purpose with subscription management server:"
              " subscription_manager module is not available."))

    syspurposestore = SyncedStore(uep=uep, consumer_uuid=uuid)
    if args.func is not None:
        result = args.func(args, syspurposestore)
    else:
        parser.print_help()
        return 0
    if args.requires_sync:
        result = syspurposestore.sync()
    if result:
        if result.remote_changed:
            print(
                _("System purpose successfully sent to subscription management server."
                  ))
        else:
            print(
                _("Unable to send system purpose to subscription management server"
                  ))

    return 0
Exemplo n.º 6
0
def main():
    """
    Run the cli (Do the syspurpose tool thing!!)
    :return: 0
    """

    parser = setup_arg_parser()
    args = parser.parse_args()

    # Syspurpose is not intended to be used in containers for the time being (could change later).
    if in_container():
        print(_("WARNING: Setting syspurpose in containers has no effect."
              "Please run syspurpose on the host.\n"))

    try:
        from subscription_manager.identity import Identity
        from subscription_manager.cp_provider import CPProvider
        identity = Identity()
        uuid = identity.uuid
        uep = CPProvider().get_consumer_auth_cp()
    except ImportError:
        uuid = None
        uep = None
        print(_("Warning: Unable to sync system purpose with subscription management server:"
                " subscription_manager module is not available."))

    syspurposestore = SyncedStore(uep=uep, consumer_uuid=uuid, use_valid_fields=True)
    if getattr(args, 'func', None) is not None:
        args.func(args, syspurposestore)
    else:
        parser.print_help()
        return 0

    return 0
    def test_server_side_falsey_removes_value_locally(self):
        initial_syspurpose = {'role': 'something'}
        remote_contents = {'role': ''}
        self.uep.getConsumer.return_value = remote_contents

        # Write an out of order list to both the local and cache
        write_to_file_utf8(io.open(self.local_syspurpose_file, 'w'),
                           initial_syspurpose)
        write_to_file_utf8(io.open(self.cache_syspurpose_file, 'w'),
                           initial_syspurpose)

        synced_store = SyncedStore(self.uep, consumer_uuid="something")
        result = self.assertRaisesNothing(synced_store.sync)

        self.assertTrue(isinstance(result, SyncResult))

        local_result = json.load(io.open(self.local_syspurpose_file, 'r'))
        cache_result = json.load(io.open(self.cache_syspurpose_file, 'r'))

        self.assertTrue(
            'role' not in local_result,
            'The role was falsey and should not have been in the local file')
        self.assertTrue(
            'role' in cache_result
            and cache_result['role'] == remote_contents['role'],
            'Expected the cache file to contain the same value for role as the remote'
        )
Exemplo n.º 8
0
 def show(self):
     if self.cp.has_capability("syspurpose"):
         self.store = SyncedStore(uep=self.cp,
                                  consumer_uuid=self.identity.uuid)
         super(ServiceLevelCommand, self).show()
     else:
         self.show_service_level()
def write_syspurpose(values):
    """
    Write the syspurpose to the file system.
    :param values:
    :return:
    """
    if SyncedStore is not None:
        sp = SyncedStore(None)
        sp.update_local(values)
    else:
        # Simple backup in case the syspurpose tooling is not installed.
        try:
            json.dump(values, open(USER_SYSPURPOSE), ensure_ascii=True, indent=2)
        except OSError:
            log.warning('Could not write syspurpose to %s' % USER_SYSPURPOSE)
            return False
    return True
    def test_server_upgraded_to_support_syspurpose(self):
        # This one attempts to show that if a server does not support syspurpose and then does
        # after an upgrade perhaps, that we do the right thing (not unsetting all the user set
        # values

        self.uep.has_capability = mock.Mock(side_effect=lambda x: x in [])
        write_to_file_utf8(io.open(self.local_syspurpose_file, 'w'),
                           {u'role': u'initial'})
        write_to_file_utf8(io.open(self.cache_syspurpose_file, 'w'), {})

        synced_store = SyncedStore(self.uep, consumer_uuid="something")
        result = self.assertRaisesNothing(synced_store.sync)

        self.assertTrue(isinstance(result, SyncResult))

        local_result = json.load(io.open(self.local_syspurpose_file, 'r'))
        cache_result = json.load(io.open(self.cache_syspurpose_file, 'r'))

        self.assert_equal_dict({u'role': u'initial'}, local_result)
        self.assert_equal_dict({}, cache_result)

        # Now the "fake" upgrade

        self.uep.has_capability = mock.Mock(
            side_effect=lambda x: x in ["syspurpose"])
        self.uep.getConsumer.return_value = self.default_remote_values

        synced_store = SyncedStore(self.uep, consumer_uuid="something")
        result = self.assertRaisesNothing(synced_store.sync)

        self.assertTrue(isinstance(result, SyncResult))

        local_result = json.load(io.open(self.local_syspurpose_file, 'r'))
        cache_result = json.load(io.open(self.cache_syspurpose_file, 'r'))

        expected_cache = {
            u'role':
            u'initial',  # This was set locally before and should still be
            u'usage': self.default_remote_values['usage'],
            u'service_level_agreement':
            self.default_remote_values['serviceLevel'],
            u'addons': self.default_remote_values['addOns']
        }

        self.assert_equal_dict({u'role': u'initial'}, local_result)
        self.assert_equal_dict(expected_cache, cache_result)
Exemplo n.º 11
0
def write_syspurpose(values):
    """
    Write the syspurpose to the file system.
    :param values:
    :return:
    """
    if SyncedStore is not None:
        sp = SyncedStore(None)
        sp.update_local(values)
    else:
        # Simple backup in case the syspurpose tooling is not installed.
        try:
            json.dump(values, open(USER_SYSPURPOSE), ensure_ascii=True, indent=2)
        except OSError:
            log.warning('Could not write syspurpose to %s' % USER_SYSPURPOSE)
            return False
    return True
Exemplo n.º 12
0
 def unset(self):
     if self.cp.has_capability("syspurpose"):
         self.store = SyncedStore(uep=self.cp,
                                  consumer_uuid=self.identity.uuid)
         super(ServiceLevelCommand, self).unset()
     else:
         self.update_service_level("")
         print(_("Service level preference has been unset"))
Exemplo n.º 13
0
 def set(self):
     if self.cp.has_capability("syspurpose"):
         self.store = SyncedStore(uep=self.cp,
                                  consumer_uuid=self.identity.uuid)
         super(ServiceLevelCommand, self).set()
     else:
         self.update_service_level(self.options.set)
         print(
             _('Service level set to: "{val}".').format(
                 val=self.options.set))
Exemplo n.º 14
0
    def _do_command(self):
        """
        Own implementation of all actions
        :return: None
        """
        self._validate_options()

        # a subcommand was actually invoked, so dispatch it
        if self.options.subparser_name is not None:
            subcmd = self.cli_commands[self.options.subparser_name]
            # set a reference to ourselves in the subcommand being executed;
            # this way, everything we collected & created in main() is
            # "forwarded" to the subcommand class (see
            # AbstractSyspurposeCommand.__getattr__())
            subcmd.syspurpose_command = self
            subcmd._do_command()
            return

        content = {}
        if self.options.syspurpose_show is True:
            if self.is_registered():
                try:
                    self.cp = self.cp_provider.get_consumer_auth_cp()
                except connection.RestlibException as err:
                    log.exception(err)
                    log.debug(
                        "Error: Unable to retrieve system purpose from server")
                except Exception as err:
                    log.debug(
                        "Error: Unable to retrieve system purpose from server: {err}"
                        .format(err=err))
                else:
                    self.store = SyncedStore(uep=self.cp,
                                             consumer_uuid=self.identity.uuid)
                    sync_result = self.store.sync()
                    content = sync_result.result
            else:
                content = syspurposelib.read_syspurpose()
            print(
                json.dumps(content,
                           indent=2,
                           ensure_ascii=False,
                           sort_keys=True))
Exemplo n.º 15
0
def save_sla_to_syspurpose_metadata(service_level):
    """
    Saves the provided service-level value to the local Syspurpose Metadata (syspurpose.json) file.
    If the service level provided is null or empty, the sla value to the local syspurpose file is set to null.

    :param service_level: The service-level value to be saved in the syspurpose file.
    :type service_level: str
    """

    if 'SyncedStore' in globals() and SyncedStore is not None:
        store = SyncedStore(None)

        # if empty, set it to null
        if service_level is None or service_level == "":
            service_level = None

        store.set("service_level_agreement", service_level)
        store.finish()
        log.debug("Syspurpose SLA value successfully saved locally.")
    else:
        log.error("SyspurposeStore could not be imported. Syspurpose SLA value not saved locally.")
Exemplo n.º 16
0
def merge_syspurpose_values(local=None,
                            remote=None,
                            base=None,
                            uep=None,
                            consumer_uuid=None):
    """
    Try to do three-way merge of local, remote and base dictionaries.
    Note: when remote is None, then this method will call REST API.
    :param local: dictionary with local values
    :param remote: dictionary with remote values
    :param base: dictionary with cached values
    :param uep: object representing connection to canlepin server
    :param consumer_uuid: UUID of consumer
    :return: Dictionary with local result
    """

    if SyncedStore is None:
        return {}

    synced_store = SyncedStore(uep=uep, consumer_uuid=consumer_uuid)

    if local is None:
        local = synced_store.get_local_contents()
    if remote is None:
        remote = synced_store.get_remote_contents()
    if base is None:
        base = synced_store.get_cached_contents()

    result = synced_store.merge(local=local, remote=remote, base=base)
    local_result = {key: result[key] for key in result if result[key]}
    log.debug('local result: %s ' % local_result)
    return local_result
Exemplo n.º 17
0
def save_sla_to_syspurpose_metadata(uep, consumer_uuid, service_level):
    """
    Saves the provided service-level value to the local Syspurpose Metadata (syspurpose.json) file.
    If the service level provided is null or empty, the sla value to the local syspurpose file is set to null.
    when uep and consumer_uuid is not None, then service_level is also synced with candlepin server

    :param uep: The object with uep connection (connection to candlepin server)
    :param consumer_uuid: Consumer UUID
    :param service_level: The service-level value to be saved in the syspurpose file.
    :type service_level: str
    """

    if 'SyncedStore' in globals() and SyncedStore is not None:
        synced_store = SyncedStore(uep=uep, consumer_uuid=consumer_uuid)

        # if empty, set it to null
        if service_level is None or service_level == "":
            service_level = None

        synced_store.set("service_level_agreement", service_level)
        synced_store.finish()
        log.debug("Syspurpose SLA value successfully saved locally.")
    else:
        log.error(
            "SyspurposeStore could not be imported. Syspurpose SLA value not saved locally."
        )
Exemplo n.º 18
0
def get_sys_purpose_store():
    """
    :return: Returns a singleton instance of the syspurpose store if it was imported.
             Otherwise None.
    """
    global store
    if store is not None:
        return store
    elif SyncedStore is not None:
        uep = inj.require(inj.CP_PROVIDER).get_consumer_auth_cp()
        uuid = inj.require(inj.IDENTITY).uuid
        store = SyncedStore(uep, consumer_uuid=uuid)
    return store
Exemplo n.º 19
0
def read_syspurpose(synced_store=None, raise_on_error=False):
    """
    Reads the system purpose from the correct location on the file system.
    Makes an attempt to use a SyspurposeStore if available falls back to reading the json directly.
    :return: A dictionary containing the total syspurpose.
    """
    if SyncedStore is not None:
        if synced_store is None:
            synced_store = SyncedStore(None)
        try:
            content = synced_store.get_local_contents()
        except (OSError, IOError):
            content = {}
    else:
        try:
            content = json.load(open(USER_SYSPURPOSE))
        except (os.error, ValueError, IOError):
            # In the event this file could not be read treat it as empty
            if raise_on_error:
                raise
            content = {}
    return content
    def test_values_not_known_server_side_are_left_alone(self):
        cache_contents = {
            u'role': u'initial_role',
            u'usage': u'initial_usage',
            u'service_level_agreement': u'',
            u'addons': []
        }
        local_contents = {
            u'role': cache_contents[u'role'],
            u'usage': cache_contents[u'usage'],
            u'made_up_key':
            u'arbitrary_value'  # this key was added and is not known
        }
        remote_contents = {
            u'role': u'remote_role',
            u'usage':
            u'',  # Usage has been reset on the server side, should be removed locally
            u'serviceLevel': u'',
            u'addOns': []
        }

        self.uep.getConsumer.return_value = remote_contents

        # Write an out of order list to both the local and cache
        write_to_file_utf8(io.open(self.local_syspurpose_file, 'w'),
                           local_contents)
        write_to_file_utf8(io.open(self.cache_syspurpose_file, 'w'),
                           cache_contents)

        synced_store = SyncedStore(self.uep, consumer_uuid="something")
        result = self.assertRaisesNothing(synced_store.sync)

        self.assertTrue(isinstance(result, SyncResult))

        local_result = json.load(io.open(self.local_syspurpose_file, 'r'))
        cache_result = json.load(io.open(self.cache_syspurpose_file, 'r'))

        expected_local = {
            u'role': remote_contents['role'],
            u'made_up_key': local_contents['made_up_key']
        }
        expected_cache = {
            u'role': remote_contents['role'],
            u'usage': remote_contents['usage'],
            u'made_up_key': local_contents['made_up_key'],
            u'addons': [],
            u'service_level_agreement': u''
        }

        self.assert_equal_dict(expected_local, local_result)
        self.assert_equal_dict(expected_cache, cache_result)
Exemplo n.º 21
0
    def perform(self, include_result=False):
        """
        Perform the action that this Command represents.
        :return:
        """
        result = {}
        consumer_uuid = inj.require(inj.IDENTITY).uuid

        try:
            store = SyncedStore(uep=self.uep,
                                        consumer_uuid=consumer_uuid,
                                        report=self.report,
                                        on_changed=self.report.record_change)
            result = store.sync()
        except ConnectionException as e:
            self.report._exceptions.append('Unable to sync syspurpose with server: %s' % str(e))
            self.report._status = 'Failed to sync system purpose'
        self.report._updates = "\n\t\t ".join(self.report._updates)
        log.debug("Syspurpose updated: %s" % self.report)
        if not include_result:
            return self.report
        else:
            return self.report, result
Exemplo n.º 22
0
    def test_read_file_non_existent_cache_directory(self):
        """
        Test the SyspurposeStore.read_file can resurrect from situation, when directory /var/lib/rhsm/cache
        does not exist
        """
        # Delete the temporary directory
        os.rmdir(self.temp_cache_dir)

        consumer_uuid = "something"
        synced_store = SyncedStore(self.uep, consumer_uuid=consumer_uuid)
        local_content = self.assertRaisesNothing(synced_store.get_local_contents)
        self.assertEqual(local_content, {})

        # Make sure that the directory was created
        res = os.path.isdir(self.temp_dir)
        self.assertTrue(res)
Exemplo n.º 23
0
    def test_same_values_not_synced_with_server(self):
        cache_contents = {
            u'role': u'initial_role',
            u'usage': u'initial_usage',
            u'service_level_agreement': u'initial_sla'
        }
        local_contents = {
            u'role': u'initial_role',
            u'usage': u'initial_usage',
            u'service_level_agreement': u'initial_sla'
        }
        remote_contents = {
            u'role': u'initial_role',
            u'usage': u'initial_usage',
            u'serviceLevel': u'initial_sla'
        }

        self.uep.getConsumer.return_value = remote_contents
        write_to_file_utf8(io.open(self.cache_syspurpose_file, 'w'), cache_contents)
        write_to_file_utf8(io.open(self.local_syspurpose_file, 'w'), local_contents)

        synced_store = SyncedStore(self.uep, consumer_uuid="something")
        result = self.assertRaisesNothing(synced_store.sync)

        self.assertTrue(isinstance(result, SyncResult))

        # When values are still the same, then client should not try to sync
        # same values with server
        self.uep.updateConsumer.assert_not_called()

        local_result = json.load(io.open(self.local_syspurpose_file, 'r'))
        cache_result = json.load(io.open(self.cache_syspurpose_file, 'r'))

        expected_local = {
            u'role': u'initial_role',
            u'usage': u'initial_usage',
            u'service_level_agreement': u'initial_sla'
        }
        expected_cache = {
            u'role': u'initial_role',
            u'usage': u'initial_usage',
            u'service_level_agreement': u'initial_sla',
            u'addons': None
        }

        self.assert_equal_dict(expected_cache, cache_result)
        self.assert_equal_dict(expected_local, local_result)
Exemplo n.º 24
0
    def _assert_falsey_values_removed_from_local(self, remote_contents, local_contents, cache_contents):
        self.uep.getConsumer.return_value = remote_contents

        write_to_file_utf8(io.open(self.local_syspurpose_file, 'w'), local_contents)
        write_to_file_utf8(io.open(self.cache_syspurpose_file, 'w'), cache_contents)

        synced_store = SyncedStore(self.uep, consumer_uuid="something")
        result = self.assertRaisesNothing(synced_store.sync)

        self.assertTrue(isinstance(result, SyncResult))
        local_result = json.load(io.open(self.local_syspurpose_file, 'r'))
        # All the values from the local file should be truthy.
        self.assertTrue(all(local_result[key] for key in local_result))

        cache_result = json.load(io.open(self.cache_syspurpose_file, 'r'))
        # The cache should contain the entire set of values from the SyncResult
        self.assertDictEqual(cache_result, result.result)
Exemplo n.º 25
0
    def test_list_items_are_order_agnostic(self):
        addons = [1, 2]
        self.uep.getConsumer.return_value = {'addOns': addons}

        # Write an out of order list to both the local and cache
        write_to_file_utf8(io.open(self.local_syspurpose_file, 'w'), {'addons': addons[::-1]})
        write_to_file_utf8(io.open(self.cache_syspurpose_file, 'w'), {'addons': addons[::-1]})

        synced_store = SyncedStore(self.uep, consumer_uuid="something")
        result = self.assertRaisesNothing(synced_store.sync)

        self.assertTrue(isinstance(result, SyncResult))

        local_result = json.load(io.open(self.local_syspurpose_file, 'r'))
        cache_result = json.load(io.open(self.cache_syspurpose_file, 'r'))

        self.assertSetEqual(set(result.result['addons']), set(local_result['addons']),
                            'Expected local file to have the same set of addons as the result')
        self.assertSetEqual(set(result.result['addons']),  set(cache_result['addons']),
                            'Expected cache file to have the same set of addons as the result')
Exemplo n.º 26
0
    def test_server_does_not_support_syspurpose(self):
        # This is how we detect if we have syspurpose support
        self.uep.has_capability = mock.Mock(side_effect=lambda x: x in [])

        write_to_file_utf8(io.open(self.local_syspurpose_file, 'w'), {u'role': u'initial'})
        write_to_file_utf8(io.open(self.cache_syspurpose_file, 'w'), {})

        synced_store = SyncedStore(self.uep, consumer_uuid="something")

        self.assertRaisesNothing(synced_store.set, u'role', u'new_role')
        result = self.assertRaisesNothing(synced_store.sync)

        self.assertTrue(isinstance(result, SyncResult))

        local_result = json.load(io.open(self.local_syspurpose_file, 'r'))
        cache_result = json.load(io.open(self.cache_syspurpose_file, 'r'))

        # The cache should not be updated at all
        self.assert_equal_dict({}, cache_result)
        self.assert_equal_dict({u'role': u'new_role'}, local_result)

        self.uep.updateConsumer.assert_not_called()
    def test_user_deletes_syspurpose_file(self):
        cache_contents = {
            u'role': u'initial_role',
            u'usage': u'initial_usage',
            u'service_level_agreement': u'',
            u'addons': []
        }
        remote_contents = {
            u'role': u'initial_role',
            u'usage': u'initial_usage',
            u'serviceLevel': u'',
            u'addOns': []
        }
        consumer_uuid = "something"
        self.uep.getConsumer.return_value = remote_contents
        write_to_file_utf8(io.open(self.cache_syspurpose_file, 'w'),
                           cache_contents)
        # We don't write anything to the self.local_syspurpose_file anywhere else, so not making
        # one is equivalent to removing an existing one.

        synced_store = SyncedStore(self.uep, consumer_uuid=consumer_uuid)
        result = self.assertRaisesNothing(synced_store.sync)

        expected_cache = {u'service_level_agreement': u'', u'addons': []}
        expected_local = {}

        local_result = json.load(io.open(self.local_syspurpose_file, 'r'))
        cache_result = json.load(io.open(self.cache_syspurpose_file, 'r'))

        self.assert_equal_dict(expected_local, local_result)
        self.assert_equal_dict(expected_cache, cache_result)
        self.uep.updateConsumer.assert_called_once_with(consumer_uuid,
                                                        role="",
                                                        usage="",
                                                        service_level=u"",
                                                        addons=[])
def save_sla_to_syspurpose_metadata(service_level):
    """
    Saves the provided service-level value to the local Syspurpose Metadata (syspurpose.json) file.
    If the service level provided is null or empty, the sla value to the local syspurpose file is set to null.

    :param service_level: The service-level value to be saved in the syspurpose file.
    :type service_level: str
    """

    if 'SyncedStore' in globals() and SyncedStore is not None:
        store = SyncedStore(None)

        # if empty, set it to null
        if service_level is None or service_level == "":
            service_level = None

        store.set("service_level_agreement", service_level)
        store.finish()
        log.info("Syspurpose SLA value successfully saved locally.")
    else:
        log.error("SyspurposeStore could not be imported. Syspurpose SLA value not saved locally.")
Exemplo n.º 29
0
class SyspurposeCommand(CliCommand):
    """
    Syspurpose command for generic actions. This command will be used for all
    syspurpose actions in the future and it will replace addons, role,
    service-level and usage commands. It will be possible to set service-type
    using this command.

    Note: when the system is not registered, then it doesn't make any sense to
    synchronize syspurpose values with candlepin server, because consumer
    does not exist.
    """
    def __init__(self):
        """
        Initialize the syspurpose command
        """
        short_desc = _(
            "Convenient module for managing all system purpose settings")
        super(SyspurposeCommand, self).__init__("syspurpose",
                                                short_desc,
                                                primary=False)
        self.parser.add_argument("--show",
                                 action="store_true",
                                 help=_("show current system purpose"))

    def _validate_options(self):
        """
        Validate provided options
        :return: None
        """
        # When no CLI options are provided, then show current syspurpose values
        if self.options.show is not True:
            self.options.show = True

    def _do_command(self):
        """
        Own implementation of all actions
        :return: None
        """
        self._validate_options()

        content = {}
        if self.options.show is True:
            if self.is_registered():
                try:
                    self.cp = self.cp_provider.get_consumer_auth_cp()
                except connection.RestlibException as err:
                    log.exception(err)
                    log.debug(
                        "Error: Unable to retrieve system purpose from server")
                except Exception as err:
                    log.debug(
                        "Error: Unable to retrieve system purpose from server: {err}"
                        .format(err=err))
                else:
                    self.store = SyncedStore(uep=self.cp,
                                             consumer_uuid=self.identity.uuid)
                    sync_result = self.store.sync()
                    content = sync_result.result
            else:
                content = syspurposelib.read_syspurpose()
            print(
                json.dumps(content,
                           indent=2,
                           ensure_ascii=False,
                           sort_keys=True))
Exemplo n.º 30
0
class AbstractSyspurposeCommand(CliCommand):
    """
    Abstract command for manipulating an attribute of system purpose.
    """

    def __init__(
        self,
        name,
        subparser,
        shortdesc=None,
        primary=False,
        attr=None,
        commands=("set", "unset", "show", "list"),
    ):
        # set 'subparser' before calling the parent constructor, as it will
        # (indirectly) call _create_argparser(), which our reimplementation uses
        self.subparser = subparser
        if self.subparser is None:
            # this syspurpose command is a deprecated top-level subcommand,
            # so change its description to be a deprecated text
            shortdesc = DEPRECATED_COMMAND_MESSAGE
        super(AbstractSyspurposeCommand, self).__init__(name, shortdesc=shortdesc, primary=primary)
        self.commands = commands
        self.attr = attr

        self.store = None

        if "set" in commands:
            self.parser.add_argument(
                "--set",
                dest="set",
                help=_("set {attr} of system purpose").format(attr=attr),
            )
        if "unset" in commands:
            self.parser.add_argument(
                "--unset",
                dest="unset",
                action="store_true",
                help=_("unset {attr} of system purpose").format(attr=attr),
            )
        if "add" in commands:
            self.parser.add_argument(
                "--add",
                dest="to_add",
                action="append",
                default=[],
                help=_("add an item to the list ({attr}).").format(attr=attr),
            )
        if "remove" in commands:
            self.parser.add_argument(
                "--remove",
                dest="to_remove",
                action="append",
                default=[],
                help=_("remove an item from the list ({attr}).").format(attr=attr),
            )
        if "show" in commands:
            self.parser.add_argument(
                "--show",
                dest="show",
                action="store_true",
                help=_("show this system's current {attr}").format(attr=attr),
            )
        if "list" in commands:
            self.parser.add_argument(
                "--list",
                dest="list",
                action="store_true",
                help=_("list all {attr} available").format(attr=attr),
            )

    def __getattr__(self, name):
        """
        This custom __getattr__() reimplementation is used to lookup attributes
        in the parent syspurpose command, if set; this is done in case the
        current command is a subcommand of 'syspurpose', so
        SyspurposeCommand._do_command() will set a reference to itself as
        'syspurpose_command' attribute.
        """
        syspurpose_command = self.__dict__.get("syspurpose_command", None)
        if syspurpose_command is not None:
            return getattr(syspurpose_command, name)
        raise AttributeError

    def _validate_options(self):
        to_set = getattr(self.options, "set", None)
        to_unset = getattr(self.options, "unset", None)
        to_add = getattr(self.options, "to_add", None)
        to_remove = getattr(self.options, "to_remove", None)
        to_show = getattr(self.options, "show", None)

        if to_set:
            self.options.set = self.options.set.strip()
        if to_add:
            self.options.to_add = [x.strip() for x in self.options.to_add if isinstance(x, str)]
        if to_remove:
            self.options.to_remove = [x.strip() for x in self.options.to_remove if isinstance(x, str)]
        if (to_set or to_add or to_remove) and to_unset:
            system_exit(os.EX_USAGE, _("--unset cannot be used with --set, --add, or --remove"))
        if to_add and to_remove:
            system_exit(os.EX_USAGE, _("--add cannot be used with --remove"))

        if not self.is_registered():
            if self.options.list:
                if self.options.token and not self.options.username:
                    pass
                elif self.options.token and self.options.username:
                    system_exit(os.EX_USAGE, _("Error: you can specify --username or --token not both"))
                elif not self.options.username or not self.options.password:
                    system_exit(
                        os.EX_USAGE,
                        _(
                            "Error: you must register or specify --username and --password to list {attr}"
                        ).format(attr=self.attr),
                    )
            elif to_unset or to_set or to_add or to_remove or to_show:
                pass
            else:
                system_exit(ERR_NOT_REGISTERED_CODE, ERR_NOT_REGISTERED_MSG)

        if self.is_registered() and (
            getattr(self.options, "username", None)
            or getattr(self.options, "password", None)
            or getattr(self.options, "token", None)
            or getattr(self.options, "org", None)
        ):
            system_exit(
                os.EX_USAGE,
                _(
                    "Error: --username, --password, --token and --org "
                    "can be used only on unregistered systems"
                ),
            )

    def _get_valid_fields(self):
        """
        Try to get valid fields from server
        :return: Dictionary with valid fields
        """
        valid_fields = {}
        if self.is_registered():
            # When system is registered, then try to get valid fields from cache file
            try:
                valid_fields = get_syspurpose_valid_fields(uep=self.cp, identity=self.identity)
            except ProxyException:
                system_exit(os.EX_UNAVAILABLE, _("Proxy connection failed, please check your settings."))
        elif self.options.username and self.options.password and self.cp is not None:
            # Try to get current organization key. It is property of OrgCommand.
            # Every Syspurpose command has to be subclass of OrgCommand too
            # must have used credentials in command if not registered to proceed
            try:
                org_key = self.org
                server_response = self.cp.getOwnerSyspurposeValidFields(org_key)
            except connection.RestlibException as rest_err:
                log.warning(
                    "Unable to get list of valid fields using REST API: {rest_err}".format(rest_err=rest_err)
                )
                mapped_message: str = ExceptionMapper().get_message(rest_err)
                system_exit(os.EX_SOFTWARE, mapped_message)
            except ProxyException:
                system_exit(os.EX_UNAVAILABLE, _("Proxy connection failed, please check your settings."))
            else:
                if "systemPurposeAttributes" in server_response:
                    server_response = post_process_received_data(server_response)
                    valid_fields = server_response["systemPurposeAttributes"]
        return valid_fields

    def _is_provided_value_valid(self, value):
        """
        Try to validate provided value. Check if the value is included in valid fields.
        If the value is not provided in the valid fields and we can connect candlepin server,
        then print some warning.
        :param value: provided value on CLI
        :return: True if the value is valid; otherwise return False
        """
        invalid_values = self._are_provided_values_valid([value])
        return len(invalid_values) == 0

    def _are_provided_values_valid(self, values):
        """
        Try to validate the provided values. Check if all the values are included in valid fields.
        If any of the values is not provided in the valid fields and we can connect candlepin
        server, then print some warning.
        :param values: provided values on CLI
        :return: list of invalid values
        """

        # First check if the the value is in the valid_fields.  Comparison is case insensitive.
        invalid_values = []
        valid_fields = self._get_valid_fields()
        if self.attr in valid_fields:
            for value in values:
                if all([x.casefold() != value.casefold() for x in valid_fields[self.attr]]):
                    invalid_values.append(value)
        invalid_values_len = len(invalid_values)

        # When there are values not in the valid fields, then try to print some warning,
        # when the system is registered or username & password was provided as CLI option.
        # When the system is not registered and no username & password was provided, then
        # these values will be set silently.
        if invalid_values_len > 0:
            if (
                self.is_registered()
                or (self.options.username and self.options.password)
                or self.options.token
            ):
                if len(valid_fields.get(self.attr, [])) > 0:
                    # TRANSLATORS: this is used to quote a string
                    quoted_values = [_('"{value}"').format(value=value) for value in invalid_values]
                    printable_values = friendly_join(quoted_values)
                    print(
                        ungettext(
                            "Warning: Provided value {vals} is not included in the list of valid values",
                            "Warning: Provided values {vals} are not included in the list of valid values",
                            invalid_values_len,
                        ).format(vals=printable_values)
                    )
                    self._print_valid_values(valid_fields)
                else:
                    print(
                        _(
                            "Warning: This organization does not have any subscriptions that provide a "
                            'system purpose "{attr}".  This setting will not influence auto-attaching '
                            "subscriptions."
                        ).format(attr=self.attr)
                    )

        return invalid_values

    def set(self):
        """
        Try to set new syspurpose attribute
        """
        self._set(self.options.set)
        self._is_provided_value_valid(self.options.set)
        success_msg = _('{attr} set to "{val}".').format(attr=self.attr, val=self.options.set)
        self._check_result(
            expectation=lambda res: res.get(self.attr) == self.options.set,
            success_msg=success_msg,
            command='subscription-manager syspurpose {name} --set "{val}"'.format(
                name=self.name, val=self.options.set
            ),
            attr=self.attr,
        )

    def _set(self, to_set):
        if self.store:
            self.store.set(self.attr, to_set)
        else:
            log.debug("Not setting syspurpose attribute {attr} (store not set)".format(attr=self.attr))

    def unset(self):
        self._unset()
        success_msg = _("{attr} unset.").format(attr=self.attr)
        self._check_result(
            expectation=lambda res: res.get(self.attr) in ["", None, []],
            success_msg=success_msg,
            command="subscription-manager syspurpose {name} --unset".format(name=self.name),
            attr=self.attr,
        )

    def _unset(self):
        if self.store:
            self.store.unset(self.attr)
        else:
            log.debug("Not unsetting syspurpose attribute {attr} (store not set)".format(attr=self.attr))

    def add(self):
        self._add(self.options.to_add)
        self._are_provided_values_valid(self.options.to_add)
        success_msg = _("{attr} updated.").format(attr=self.name)
        # When there is several options to add, then format of command is following
        # subscription-manager command --add opt1 --add opt2
        options = ['"' + option + '"' for option in self.options.to_add]
        to_add = "--add " + " --add ".join(options)
        command = "subscription-manager syspurpose {name} ".format(name=self.name) + to_add
        self._check_result(
            expectation=lambda res: all(x in res.get("addons", []) for x in self.options.to_add),
            success_msg=success_msg,
            command=command,
            attr=self.attr,
        )

    def _add(self, to_add):
        if not isinstance(to_add, list):
            to_add = [to_add]

        if self.store:
            for item in to_add:
                self.store.add(self.attr, item)
        else:
            log.debug("Not adding syspurpose attribute {attr} (store not set)".format(attr=self.attr))

    def remove(self):
        self._remove(self.options.to_remove)
        success_msg = _("{attr} updated.").format(attr=self.name.capitalize())
        options = ['"' + option + '"' for option in self.options.to_remove]
        # When there is several options to remove, then format of command is following
        # subscription-manager syspurpose command --remove opt1 --remove opt2
        to_remove = "--remove " + " --remove ".join(options)
        command = "subscription-manager syspurpose {name} ".format(name=self.name) + to_remove
        self._check_result(
            expectation=lambda res: all(x not in res.get("addons", []) for x in self.options.to_remove),
            success_msg=success_msg,
            command=command,
            attr=self.attr,
        )

    def _remove(self, to_remove):
        if not isinstance(to_remove, list):
            to_remove = [to_remove]

        if self.store:
            for item in to_remove:
                self.store.remove(self.attr, item)
        else:
            log.debug("Not removing syspurpose attribute {attr} (store not set)".format(attr=self.attr))

    def show(self):
        if self.is_registered():
            syspurpose = self.sync().result
        else:
            syspurpose = syspurposelib.read_syspurpose()
        if syspurpose is not None and self.attr in syspurpose and syspurpose[self.attr]:
            val = syspurpose[self.attr]
            values = val if not isinstance(val, list) else ", ".join(val)
            print(_("Current {name}: {val}").format(name=self.name.capitalize(), val=values))
        else:
            print(_("{name} not set.").format(name=self.name.capitalize()))

    def _print_valid_values(self, valid_fields):
        """
        Print list of valid values for current syspurpose attribute
        :param valid_fields:
        :return: None
        """
        for valid_value in valid_fields[self.attr]:
            if len(valid_value) > 0:
                print(" - {value}".format(value=valid_value))

    def list(self):
        valid_fields = self._get_valid_fields()
        if self.attr in valid_fields:
            if len(valid_fields[self.attr]) > 0:
                line = "+-------------------------------------------+"
                print(line)
                translated_string = _("Available {syspurpose_attr}").format(syspurpose_attr=self.attr)
                # Print translated string (the length could be different) in the center of the line
                line_len = len(line)
                trans_str_len = len(translated_string)
                empty_space_len = int((line_len - trans_str_len) / 2)
                print(empty_space_len * " " + translated_string)
                print(line)
                # Print values
                self._print_valid_values(valid_fields)
            else:
                print(
                    _(
                        'There are no available values for the system purpose "{syspurpose_attr}" '
                        "from the available subscriptions in this "
                        "organization."
                    ).format(syspurpose_attr=self.attr)
                )
        else:
            print(
                _(
                    "Unable to get the list of valid values for the system purpose " '"{syspurpose_attr}".'
                ).format(syspurpose_attr=self.attr)
            )

    def sync(self):
        return syspurposelib.SyspurposeSyncActionCommand().perform(
            include_result=True, passthrough_gone=True
        )[1]

    def _do_command(self):
        self._validate_options()

        self.cp = None
        try:
            # If we have a username/password, we're going to use that, otherwise
            # we'll use the identity certificate. We already know one or the other
            # exists:
            if self.options.token:
                try:
                    self.cp = self.cp_provider.get_keycloak_auth_cp(self.options.token)
                except Exception as err:
                    log.error(
                        'unable to connect to candlepin server using token: "{token}", err: {err}'.format(
                            token=self.options.token, err=err
                        )
                    )
                    print(_("Unable to connect to server using token"))
            elif self.options.username and self.options.password:
                self.cp_provider.set_user_pass(self.options.username, self.options.password)
                self.cp = self.cp_provider.get_basic_auth_cp()
            else:
                # get an UEP as consumer
                if self.is_registered():
                    self.cp = self.cp_provider.get_consumer_auth_cp()
        except connection.RestlibException as err:
            log.exception(err)
            if getattr(self.options, "list", None):
                log.error(
                    "Error: Unable to retrieve {attr} from server: {err}".format(attr=self.attr, err=err)
                )
                mapped_message: str = ExceptionMapper().get_message(err)
                system_exit(os.EX_SOFTWARE, mapped_message)
            else:
                log.debug(
                    "Error: Unable to retrieve {attr} from server: {err}".format(attr=self.attr, err=err)
                )
        except Exception as err:
            log.debug("Error: Unable to retrieve {attr} from server: {err}".format(attr=self.attr, err=err))

        self.store = SyncedStore(uep=self.cp, consumer_uuid=self.identity.uuid)

        if getattr(self.options, "unset", None):
            self.unset()
        elif getattr(self.options, "set", None):
            self.set()
        elif hasattr(self.options, "to_add") and len(self.options.to_add) > 0:
            self.add()
        elif hasattr(self.options, "to_remove") and len(self.options.to_remove) > 0:
            self.remove()
        elif getattr(self.options, "list", None):
            self.list()
        elif getattr(self.options, "show", None):
            self.show()
        else:
            self.show()

    def _create_argparser(self):
        if self.subparser is None:
            # Without a subparser, which means it is a standalone command
            return super(AbstractSyspurposeCommand, self)._create_argparser()

        # This string is similar to what _get_usage() returns; we cannot use
        # _get_usage() as it prints the subcommand name as well, and the created
        # ArgumentParser is a subparser (so there is a parent parser already
        # printing the subcommand).
        usage = _("%(prog)s [OPTIONS]")
        return self.subparser.add_parser(
            self.name, description=self.shortdesc, usage=usage, help=self.shortdesc
        )

    def check_syspurpose_support(self, attr):
        if self.is_registered() and not self.cp.has_capability("syspurpose"):
            print(
                _(
                    "Note: The currently configured entitlement server does not support System Purpose {attr}.".format(
                        attr=attr
                    )
                )
            )

    def _check_result(self, expectation, success_msg, command, attr):
        if self.store:
            self.store.sync()
            result = self.store.get_cached_contents()
        else:
            result = {}
        if result and not expectation(result):
            advice = SP_ADVICE.format(command=command)
            value = result[attr]
            msg = _(SP_CONFLICT_MESSAGE.format(attr=attr, download_value=value, advice=advice))
            system_exit(os.EX_SOFTWARE, msgs=msg)
        else:
            print(success_msg)
Exemplo n.º 31
0
class SyspurposeCommand(CliCommand):
    """
    Syspurpose command for generic actions. This command will be used for all
    syspurpose actions in the future and it will replace addons, role,
    service-level and usage commands. It will be possible to set service-type
    using this command.

    Note: when the system is not registered, then it doesn't make any sense to
    synchronize syspurpose values with candlepin server, because consumer
    does not exist.
    """
    def __init__(self):
        """
        Initialize the syspurpose command
        """
        short_desc = _(
            "Convenient module for managing all system purpose settings")
        super(SyspurposeCommand, self).__init__("syspurpose",
                                                short_desc,
                                                primary=False)
        self.parser.add_argument(
            "--show",
            dest="syspurpose_show",
            action="store_true",
            help=_("show current system purpose"),
        )

        # all the subcommands of 'syspurpose'; add them to this list to be
        # registered as such
        syspurpose_command_classes = [
            AddonsCommand, RoleCommand, ServiceLevelCommand, UsageCommand
        ]
        # create a subparser for all the subcommands: it is passed to all
        # the subcommand classes, so they will create an ArgumentParser that
        # is a child of the 'syspurpose' one, rather than as standalone
        # parsers
        prog = os.path.basename(sys.argv[0])
        subparser = self.parser.add_subparsers(
            # create an own 'prog' string that contains the actual program name
            # followed by this command name; this way, the default 'prog'
            # definition is not used, as it is based on the 'usage' string of
            # the ArgumentParser (self.parser), which contains the '[OPTIONS]'
            # help text (see AbstractCLICommand._get_usage())
            prog=_("{prog} {name}").format(prog=prog, name=self.name),
            dest="subparser_name",
            title=_("Syspurpose submodules"),
            help="",  # trick argparse a bit to not show the {cmd1, cmd2, ..}
            metavar="",  # trick argparse a bit to not show the {cmd1, cmd2, ..}
        )
        self.cli_commands = {}
        for clazz in syspurpose_command_classes:
            cmd = clazz(subparser)
            self.cli_commands[cmd.name] = cmd

    def _get_usage(self):
        """
        Reimplemented from AbstractCLICommand to mention the optional submodule
        in the usage string.
        """
        return _("%(prog)s {name} [SUBMODULE] [OPTIONS]").format(
            name=self.name)

    def _validate_options(self):
        """
        Validate provided options
        :return: None
        """
        # When no CLI options are provided, then show current syspurpose values
        if self.options.syspurpose_show is not True:
            self.options.syspurpose_show = True

    def _do_command(self):
        """
        Own implementation of all actions
        :return: None
        """
        self._validate_options()

        # a subcommand was actually invoked, so dispatch it
        if self.options.subparser_name is not None:
            subcmd = self.cli_commands[self.options.subparser_name]
            # set a reference to ourselves in the subcommand being executed;
            # this way, everything we collected & created in main() is
            # "forwarded" to the subcommand class (see
            # AbstractSyspurposeCommand.__getattr__())
            subcmd.syspurpose_command = self
            subcmd._do_command()
            return

        content = {}
        if self.options.syspurpose_show is True:
            if self.is_registered():
                try:
                    self.cp = self.cp_provider.get_consumer_auth_cp()
                except connection.RestlibException as err:
                    log.exception(err)
                    log.debug(
                        "Error: Unable to retrieve system purpose from server")
                except Exception as err:
                    log.debug(
                        "Error: Unable to retrieve system purpose from server: {err}"
                        .format(err=err))
                else:
                    self.store = SyncedStore(uep=self.cp,
                                             consumer_uuid=self.identity.uuid)
                    sync_result = self.store.sync()
                    content = sync_result.result
            else:
                content = syspurposelib.read_syspurpose()
            print(
                json.dumps(content,
                           indent=2,
                           ensure_ascii=False,
                           sort_keys=True))