diff --git a/.cockpit-ci/run b/.cockpit-ci/run index 2400c9fa6e..dc220f39bd 120000 --- a/.cockpit-ci/run +++ b/.cockpit-ci/run @@ -1 +1 @@ -../integration-tests/run \ No newline at end of file +../cockpit-tests/run \ No newline at end of file diff --git a/.fmf/version b/.fmf/version new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/.fmf/version @@ -0,0 +1 @@ +1 diff --git a/.github/workflows/auto-assign.yml b/.github/workflows/auto-assign.yml deleted file mode 100644 index 52918bb809..0000000000 --- a/.github/workflows/auto-assign.yml +++ /dev/null @@ -1,16 +0,0 @@ -name: AutoAssignReviewer - -on: - pull_request: - types: [opened, ready_for_review] - -jobs: - auto-assign-reviewer: - runs-on: ubuntu-latest - steps: - - name: Run assignment of reviewer team - uses: nikosmoum/auto-assign-reviewer-team@v0.5 - with: - githubToken: ${{ secrets.AUTOASSIGNREVIEWERSECRET }} - teamName: 'client-tools' - diff --git a/.github/workflows/libdnf.yml b/.github/workflows/libdnf.yml index 91425b0bec..91bc3989a8 100644 --- a/.github/workflows/libdnf.yml +++ b/.github/workflows/libdnf.yml @@ -13,7 +13,7 @@ jobs: matrix: include: - name: "CentOS Stream 10" - image: "quay.io/centos/centos:stream10-development" + image: "quay.io/centos/centos:stream10" - name: "Fedora latest" image: "registry.fedoraproject.org/fedora:latest" - name: "Fedora Rawhide" diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index a5bfd4e4c8..c0d23dc859 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -17,7 +17,7 @@ jobs: matrix: include: - name: "CentOS Stream 10" - image: "quay.io/centos/centos:stream10-development" + image: "quay.io/centos/centos:stream10" pytest_args: '' - name: "Fedora latest" image: "registry.fedoraproject.org/fedora:latest" diff --git a/.github/workflows/tito.yml b/.github/workflows/tito.yml index dd5663c720..273a8c6b3a 100644 --- a/.github/workflows/tito.yml +++ b/.github/workflows/tito.yml @@ -13,7 +13,7 @@ jobs: matrix: include: - name: "CentOS Stream 10" - image: "quay.io/centos/centos:stream10-development" + image: "quay.io/centos/centos:stream10" packager: "dnf4" - name: "Fedora latest" image: "registry.fedoraproject.org/fedora:latest" @@ -70,6 +70,11 @@ jobs: python3-pip python3-setuptools pip install https://github.com/rpm-software-management/tito/archive/refs/tags/tito-0.6.22-1.tar.gz + - name: Install which (RHEL-73048) + if: ${{ matrix.name == 'CentOS Stream 10' }} + run: | + dnf --setopt install_weak_deps=False install -y which + - name: Build the package run: | tito build --output=tito/ --test --rpm diff --git a/.packit.yml b/.packit.yml index 8bffe1f543..2f7be44275 100644 --- a/.packit.yml +++ b/.packit.yml @@ -18,3 +18,29 @@ jobs: targets: - centos-stream-10 - fedora-all + +# - job: tests +# trigger: pull_request +# identifier: "unit/centos-stream" +# targets: +# - centos-stream-10 +# labels: +# - unit +# tf_extra_params: +# environments: +# - artifacts: +# - type: repository-file +# id: https://copr.fedorainfracloud.org/coprs/g/yggdrasil/latest/repo/centos-stream-$releasever/group_yggdrasil-latest-centos-stream-$releasever.repo + + - job: tests + trigger: pull_request + identifier: "unit/fedora" + targets: + - fedora-all + labels: + - unit + tf_extra_params: + environments: + - artifacts: + - type: repository-file + id: https://copr.fedorainfracloud.org/coprs/g/yggdrasil/latest/repo/fedora-$releasever/group_yggdrasil-latest-fedora-$releasever.repo diff --git a/.tito/packages/subscription-manager b/.tito/packages/subscription-manager index e1d7289ea5..552dd25d20 100644 --- a/.tito/packages/subscription-manager +++ b/.tito/packages/subscription-manager @@ -1 +1 @@ -1.30.2-1 ./ +1.30.3-1 ./ diff --git a/Makefile b/Makefile index 2da843d702..8e2f8f415f 100644 --- a/Makefile +++ b/Makefile @@ -19,6 +19,7 @@ PREFIX ?= /usr/local SYSCONF ?= etc INSTALL_DIR = $(PREFIX)/share RUN_DIR ?= /run +SBIN_DIR ?= $(PREFIX)/sbin OS = $(shell test -f /etc/os-release && source /etc/os-release; echo $$ID) OS_DIST ?= $(shell rpm --eval='%dist') @@ -199,9 +200,9 @@ install-example-plugins: install-plugins install-via-setup: install-subpackages-via-setup EXCLUDE_PACKAGES="$(EXCLUDE_PACKAGES)" $(PYTHON) ./setup.py install --root $(DESTDIR) --pkg-version=$(VERSION) --prefix=$(PREFIX) \ $(SETUP_PY_INSTALL_PARAMS) - mkdir -p $(DESTDIR)/$(PREFIX)/sbin/ + mkdir -p $(DESTDIR)/$(SBIN_DIR)/ mkdir -p $(DESTDIR)/$(LIBEXEC_DIR)/ - mv $(DESTDIR)/$(PREFIX)/bin/subscription-manager $(DESTDIR)/$(PREFIX)/sbin/ + mv -n $(DESTDIR)/$(PREFIX)/bin/subscription-manager $(DESTDIR)/$(SBIN_DIR)/ mv $(DESTDIR)/$(PREFIX)/bin/rhsmcertd-worker $(DESTDIR)/$(LIBEXEC_DIR)/ mv $(DESTDIR)/$(PREFIX)/bin/rhsm-service $(DESTDIR)/$(LIBEXEC_DIR)/ mv $(DESTDIR)/$(PREFIX)/bin/rhsm-facts-service $(DESTDIR)/$(LIBEXEC_DIR)/ @@ -241,13 +242,6 @@ install-files: dbus-install install-conf install-plugins install -d $(DESTDIR)/$(PREFIX)/lib/sysusers.d install -m 644 etc-conf/rhsm-sysuser.conf $(DESTDIR)/$(PREFIX)/lib/sysusers.d/rhsm.conf - # SUSE Linux does not make use of consolehelper - if [ -f /etc/redhat-release ]; then \ - ln -sf /usr/bin/consolehelper $(DESTDIR)/$(PREFIX)/bin/subscription-manager; \ - install -m 644 etc-conf/subscription-manager.pam $(DESTDIR)/etc/pam.d/subscription-manager; \ - install -m 644 etc-conf/subscription-manager.console $(DESTDIR)/etc/security/console.apps/subscription-manager; \ - fi; \ - install -m 755 bin/rhsmcertd $(DESTDIR)/$(PREFIX)/bin/rhsmcertd .PHONY: check diff --git a/integration-tests/run b/cockpit-tests/run similarity index 86% rename from integration-tests/run rename to cockpit-tests/run index a414cc413f..a442cb3e0d 100755 --- a/integration-tests/run +++ b/cockpit-tests/run @@ -6,8 +6,8 @@ # like this: # # $ export TEST_OS=rhel-8-4 -# $ ./integration-tests/run prepare -# $ ./integration-tests/check-subscriptions -v -j1 -t +# $ ./cockpit-tests/run prepare +# $ ./cockpit-tests/check-subscriptions -v -j1 -t # # You need a couple of things installed for that. At least Python 3, # a Chromium browser, and libvirtd. See @@ -20,7 +20,7 @@ ifeq ($(TEST_OS),) TEST_OS = rhel-8-4 endif export TEST_OS -SUB_MAN_COCKPIT=$(CURDIR)/integration-tests/submancockpit +SUB_MAN_COCKPIT=$(CURDIR)/cockpit-tests/submancockpit VM_IMAGE=$(SUB_MAN_COCKPIT)/test/images/$(TEST_OS).qcow2 SUBMAN_TAR=$(CURDIR)/dist/subscription-manager.tar.gz SMBEXT_TAR=$(CURDIR)/dist/subscription-manager-build-extra.tar.gz @@ -45,7 +45,7 @@ $(SUBMAN_TAR): $(SMBEXT_TAR): tar czf $(SMBEXT_TAR) build_ext -$(VM_IMAGE): $(SUB_MAN_COCKPIT) $(SUBMAN_TAR) $(SMBEXT_TAR) integration-tests/vm.install +$(VM_IMAGE): $(SUB_MAN_COCKPIT) $(SUBMAN_TAR) $(SMBEXT_TAR) cockpit-tests/vm.install rm -f $(VM_IMAGE) # setup the test image from subscription-manager-cockpit without # a custom subscription-manager, which will be installed manually @@ -54,7 +54,7 @@ $(VM_IMAGE): $(SUB_MAN_COCKPIT) $(SUBMAN_TAR) $(SMBEXT_TAR) integration-tests/vm cd $(SUB_MAN_COCKPIT) && bots/image-customize -v $(TEST_OS) \ -u $(SUBMAN_TAR):/var/tmp/ \ -u $(SMBEXT_TAR):/var/tmp/ \ - -s ../../integration-tests/vm.install + -s ../../cockpit-tests/vm.install $(SUB_MAN_COCKPIT): git clone --quiet https://github.com/candlepin/subscription-manager-cockpit.git $(SUB_MAN_COCKPIT) diff --git a/integration-tests/vm.install b/cockpit-tests/vm.install similarity index 100% rename from integration-tests/vm.install rename to cockpit-tests/vm.install diff --git a/etc-conf/dbus/system.d/com.redhat.RHSM1.conf b/etc-conf/dbus/system.d/com.redhat.RHSM1.conf index 4c04437aa9..33f60e13a0 100644 --- a/etc-conf/dbus/system.d/com.redhat.RHSM1.conf +++ b/etc-conf/dbus/system.d/com.redhat.RHSM1.conf @@ -14,9 +14,6 @@ - - diff --git a/etc-conf/subscription-manager.completion.sh b/etc-conf/subscription-manager.completion.sh index a160fc7c4e..c9dca88edd 100644 --- a/etc-conf/subscription-manager.completion.sh +++ b/etc-conf/subscription-manager.completion.sh @@ -35,7 +35,7 @@ _subscription_manager_syspurpose() _subscription_manager_role() { local opts="--list --org --set --show - --unset --username --password --token + --unset --username --password ${_subscription_manager_common_opts}" COMPREPLY=($(compgen -W "${opts}" -- ${1})) } @@ -43,7 +43,7 @@ _subscription_manager_role() _subscription_manager_usage() { local opts="--list --org --set --show - --unset --username --password --token + --unset --username --password ${_subscription_manager_common_opts}" COMPREPLY=($(compgen -W "${opts}" -- ${1})) } @@ -72,7 +72,7 @@ _subscription_manager_config() _subscription_manager_environments() { - local opts="--org --password --username --token --set --list --list-enabled --list-disabled + local opts="--org --password --username --set --list --list-enabled --list-disabled ${_subscription_manager_common_url_opts} ${_subscription_manager_common_opts}" COMPREPLY=($(compgen -W "${opts}" -- ${1})) @@ -87,7 +87,7 @@ _subscription_manager_facts() _subscription_manager_identity() { - local opts="--force --password --regenerate --username --token + local opts="--force --password --regenerate --username ${_subscription_manager_common_opts}" COMPREPLY=($(compgen -W "${opts}" -- ${1})) } @@ -102,7 +102,7 @@ _subscription_manager_list() _subscription_manager_orgs() { - local opts="--password --username --token + local opts="--password --username ${_subscription_manager_common_url_opts} ${_subscription_manager_common_opts}" COMPREPLY=($(compgen -W "${opts}" -- ${1})) @@ -134,7 +134,7 @@ _subscription_manager_register() { local opts="--activationkey --baseurl --consumerid --environments --force --name --org --password --release - --username --token + --username ${_subscription_manager_common_url_opts} ${_subscription_manager_common_opts}" COMPREPLY=($(compgen -W "${opts}" -- ${1})) @@ -159,7 +159,7 @@ _subscription_manager_repos() _subscription_manager_service_level() { local opts="--list --org --set --show - --unset --username --password --token + --unset --username --password ${_subscription_manager_common_url_opts} ${_subscription_manager_common_opts}" COMPREPLY=($(compgen -W "${opts}" -- ${1})) diff --git a/etc-conf/subscription-manager.console b/etc-conf/subscription-manager.console deleted file mode 100644 index 7a9203b7bd..0000000000 --- a/etc-conf/subscription-manager.console +++ /dev/null @@ -1,3 +0,0 @@ -USER=root -PROGRAM=/usr/sbin/subscription-manager -SESSION=true diff --git a/etc-conf/subscription-manager.pam b/etc-conf/subscription-manager.pam deleted file mode 100644 index c7d67e3321..0000000000 --- a/etc-conf/subscription-manager.pam +++ /dev/null @@ -1,4 +0,0 @@ -#%PAM-1.0 -auth include config-util -account include config-util -session include config-util diff --git a/example-plugins/dbus_event.py b/example-plugins/dbus_event.py index 8d2d54530c..175b1d915a 100644 --- a/example-plugins/dbus_event.py +++ b/example-plugins/dbus_event.py @@ -89,9 +89,3 @@ def pre_subscribe_hook(self, conduit): def post_subscribe_hook(self, conduit): self._dbus_event("post_subscribe", conduit) - - def pre_auto_attach_hook(self, conduit): - self._dbus_event("pre_auto_attach", conduit) - - def post_auto_attach_hook(self, conduit): - self._dbus_event("post_auto_attach", conduit) diff --git a/example-plugins/subscribe.py b/example-plugins/subscribe.py index 7fe604993c..08ac839af8 100644 --- a/example-plugins/subscribe.py +++ b/example-plugins/subscribe.py @@ -37,9 +37,3 @@ def post_subscribe_hook(self, conduit): conduit: A PostSubscriptionConduit() """ conduit.log.debug("post subscribe called") - - def pre_auto_attach_hook(self, conduit): - conduit.log.debug("pre auto attach called") - - def post_auto_attach_hook(self, conduit): - conduit.log.debug("post auto attach called") diff --git a/integration-tests/README.md b/integration-tests/README.md new file mode 100644 index 0000000000..e206ade893 --- /dev/null +++ b/integration-tests/README.md @@ -0,0 +1,146 @@ +# Integration Test for subscription-manager + +There are integration tests for all parts of subscription-manager +in this directory. + +DBus tests are presented currently - they verify DBus api of *rhsm.service* +see [DBus objects](https://www.candlepinproject.org/docs/subscription-manager/dbus_objects.html) + +The tests use pytest ecosystem. + +## Installation + +1) Run local candlepin + +```shell +podman run -d --name canlepin -p 8080:8080 -p 8443:8443 --hostname candlepin.local ghcr.io/ptoscano/candlepin-unofficial:latest +``` + +2) Create additional testing data in candlepin + +Environments for *donaldduck* organization + +``` +curl --stderr /dev/null --insecure --user admin:admin --request POST \ +--data '{"id": "env-id-1", "name": "env-name-1", "description": "Testing environment num. 1"}' \ +--header 'accept: application/json' --header 'content-type: application/json' \ +https://localhost:8443/candlepin/owners/donaldduck/environments + +curl --stderr /dev/null --insecure --user admin:admin --request POST \ +--data '{"id": "env-id-2", "name": "env-name-2", "description": "Testing environment num. 2"}' \ +--header 'accept: application/json' --header 'content-type: application/json' \ +https://localhost:8443/candlepin/owners/donaldduck/environments +``` + +> citation from 'man subscription-manager' +> With on-premise subscription services, such as Subscription Asset +> Manager, the infrastructure is more complex. The local +> administrator can define independent groups called organizations +> which represent physical or organizational divisions (--org). +> Those organizations can be subdivided into environments. + +Activation keys for *donaldduck* organization + +> The tests use already installed test activation keys +> They are: +> - *default_key* +> - *awesome_os_pool" + +## Configuration + +Tests use [Dynaconf](https://www.dynaconf.com/) to load config +values. + +They are stored in a file in this directory *settings.toml* + +Config values for _testing_ environment + +```yaml +[testing] +candlepin.host = "localhost" +candlepin.port = 8443 +candlepin.insecure = true +candlepin.prefix = "/candlepin" +candlepin.username = "duey" +candlepin.password = "password" +candlepin.org = "donaldduck" +candlepin.activation_keys = ["default_key","awesome_os_pool"] +candlepin.environment.names = ["env-name-01","env-name-02"] +candlepin.environment.ids = ["env-id-01","env-id-02"] + +insights.legacy_upload = false +console.host = "cert.console.redhat.com" + +auth_proxy.host = +auth_proxy.port = 3127 +auth_proxy.username = "redhat" +auth_proxy.password = "redhat" + +noauth_proxy.host = +noauth_proxy.port = 3129 + +insights.hbi_host = "cert.console.redhat.com" +``` + +Configuration for pytest + +> There is a file *pytest.ini* in the main directory of this repo. +> It has nothing to do with integration-tests. It is a confiuration +> for unittests. + +*integration-tests/pytest.ini* + +```ini +[pytest] +addopts = "-srxv --capture=sys" +testpaths = "./" +log_cli = true +log_level = INFO +``` + +## Python virtual environment for testing + +It is good practice to use python virtual environment to run the +tests. All required packages for pytest are stored in +*requirements.txt*. + +> There is a file *requirements.txt* in the main directory of the +> repo. It is used by unittests. I has nothing to do with +> integration-tests at all. + +```shell +cd integration-tests +python3 -mvenv venv +source venv +pip install -r requirements.txt +deactivate +``` + +## Running the tests + +```shell +cd integration-tests +source venv +pytest +deativate +``` + +> There is a nice help for pytest in [Testing](../TESTING.md). It is +> full of interesting hits to run just a few tests, to increase output +> of a test run ... + +### Runnning integration tests using tmt + +You can use [Testing Farm](https://docs.testing-farm.io/Testing%20Farm/0.1/index.html) +to run the tests. + +It suposes that the package *subscription-manager* is installed at a local box. + +```shell +cd subscription-manager +sudo tmt --feeling-safe run -vvv --all provision --how local +``` + +> All details for tmt to run are stored at directory *systemtest* +> It is a starting point for deeper investigation to understand how +> the tests are run using tmt. diff --git a/integration-tests/__init__.py b/integration-tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/integration-tests/conftest.py b/integration-tests/conftest.py new file mode 100644 index 0000000000..ece0bd501b --- /dev/null +++ b/integration-tests/conftest.py @@ -0,0 +1,47 @@ +import logging +import os + +from dasbus.connection import MessageBus +from gi.repository import Gio + +import gi + +gi.require_version("Gio", "2.0") + +logger = logging.getLogger(__name__) + + +class RHSMPrivateBus(MessageBus): + """Representation of RHSM private bus connection that can be used as a context manager.""" + + def __init__(self, rhsm_register_server_proxy, *args, **kwargs): + """Representation of RHSM private bus connection that can be used as a context manager. + + :param rhsm_register_server_proxy: DBus proxy for the RHSM RegisterServer object + """ + super().__init__(*args, **kwargs) + self._rhsm_register_server_proxy = rhsm_register_server_proxy + self._private_bus_address = None + + def __enter__(self): + logger.debug("subscription: starting RHSM private DBus session") + locale = os.environ.get("LANG", "") + self._private_bus_address = self._rhsm_register_server_proxy.Start(locale) + logger.debug("subscription: RHSM private DBus session has been started") + return self + + def __exit__(self, _exc_type, _exc_value, _exc_traceback): + logger.debug("subscription: shutting down the RHSM private DBus session") + self.disconnect() + locale = os.environ.get("LANG", "") + self._rhsm_register_server_proxy.Stop(locale) + logger.debug("subscription: RHSM private DBus session has been shutdown") + + def _get_connection(self): + """Get a connection to RHSM private DBus session.""" + # the RHSM private bus address is potentially sensitive + # so we will not log it + logger.info("Connecting to the RHSM private DBus session.") + return self._provider.get_addressed_bus_connection( + bus_address=self._private_bus_address, flags=Gio.DBusConnectionFlags.AUTHENTICATION_CLIENT + ) diff --git a/integration-tests/constants.py b/integration-tests/constants.py new file mode 100644 index 0000000000..20de67dd35 --- /dev/null +++ b/integration-tests/constants.py @@ -0,0 +1,25 @@ +from dasbus.identifier import DBusObjectIdentifier, DBusServiceIdentifier +from dasbus.connection import SystemMessageBus + + +HOST_DETAILS: str = "/var/lib/insights/host-details.json" +MACHINE_ID_FILE: str = "/etc/insights-client/machine-id" +RHSM_CONFIG_FILE_PATH: str = "/etc/rhsm/rhsm.conf" + +RHSM_NAMESPACE = ("com", "redhat", "RHSM1") + +RHSM = DBusServiceIdentifier(namespace=RHSM_NAMESPACE, message_bus=SystemMessageBus()) + +RHSM_CONFIG = DBusObjectIdentifier(namespace=RHSM_NAMESPACE, basename="Config") + +RHSM_REGISTER_SERVER = DBusObjectIdentifier(namespace=RHSM_NAMESPACE, basename="RegisterServer") + +RHSM_REGISTER = DBusObjectIdentifier(namespace=RHSM_NAMESPACE, basename="Register") + +RHSM_UNREGISTER = DBusObjectIdentifier(namespace=RHSM_NAMESPACE, basename="Unregister") + +RHSM_ENTITLEMENT = DBusObjectIdentifier(namespace=RHSM_NAMESPACE, basename="Entitlement") + +RHSM_SYSPURPOSE = DBusObjectIdentifier(namespace=RHSM_NAMESPACE, basename="Syspurpose") + +RHSM_CONSUMER = DBusObjectIdentifier(namespace=RHSM_NAMESPACE, basename="Consumer") diff --git a/integration-tests/pytest.ini b/integration-tests/pytest.ini new file mode 100644 index 0000000000..cc5ed0d2da --- /dev/null +++ b/integration-tests/pytest.ini @@ -0,0 +1,5 @@ +[pytest] +addopts = "-srxv -vvv --capture=sys" +testpaths = "./" +log_cli = true +log_level = DEBUG diff --git a/integration-tests/requirements.txt b/integration-tests/requirements.txt new file mode 100644 index 0000000000..448eebeefc --- /dev/null +++ b/integration-tests/requirements.txt @@ -0,0 +1,14 @@ +# the version of black is specified also in the stylish.yml github workflow; +# please update the version there in case it is bumped here +black==24.3.0 +flake8 +git+https://github.com/ptoscano/pytest-client-tools@main +pytest +pyyaml +simplejson +dasbus +pycairo +PyGObject +sh +iniparse +funcy diff --git a/integration-tests/scripts/post-environments.sh b/integration-tests/scripts/post-environments.sh new file mode 100755 index 0000000000..830e473485 --- /dev/null +++ b/integration-tests/scripts/post-environments.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +USERNAME=admin +PASSWORD=admin +ORG=donaldduck + + +# The script will create two environments for an organization. +# +# citation from 'man subscription-manager': +# +# With on-premise subscription services, such as Subscription Asset Manager, +# the infrastructure is more complex. The local administrator can define +# independent groups called organizations which represent physical +# or organizational divisions (--org). Those organizations can be subdivided +# into environments (--environment). +# + +curl -k --request POST --user ${USERNAME}:${PASSWORD} \ + --data '{"id": "env-id-01", "name": "env-name-01", "description": "Testing environment num. 1"}' \ + --header 'accept: application/json' \ + --header 'content-type: application/json' \ + https://localhost:8443/candlepin/owners/${ORG}/environments + +curl -k --request POST --user ${USERNAME}:${PASSWORD} \ + --data '{"id": "env-id-02", "name": "env-name-02", "description": "Testing environment num. 2", "type": "content-template"}' \ + --header 'accept: application/json' \ + --header 'content-type: application/json' \ + https://localhost:8443/candlepin/owners/${ORG}/environments + diff --git a/integration-tests/scripts/run-local-candlepin.sh b/integration-tests/scripts/run-local-candlepin.sh new file mode 100755 index 0000000000..b8e19d533e --- /dev/null +++ b/integration-tests/scripts/run-local-candlepin.sh @@ -0,0 +1,9 @@ +#!/bin/bash +# +# A local candlepin is used for most of integration tests. +# It is fast to run the tests and the candlepin comes with testing data. +# So the environment is mostly prepared in the candlepin after we run a contianer. +# +# For most information see https://github.com/ptoscano/candlepin-container-unofficial +# +podman run -d --name candlepin -p 8080:8080 -p 8443:8443 --hostname candlepin.local ghcr.io/ptoscano/candlepin-unofficial:latest diff --git a/integration-tests/test_register.py b/integration-tests/test_register.py new file mode 100644 index 0000000000..4994a6d3e4 --- /dev/null +++ b/integration-tests/test_register.py @@ -0,0 +1,137 @@ +# Copyright (c) 2024 Red Hat, Inc. +# +# This software is licensed to you under the GNU General Public +# License as published by the Free Software Foundation; either version +# 2 of the License (GPLv2) or (at your option) any later version. +# There is NO WARRANTY for this software, express or implied, +# including the implied warranties of MERCHANTABILITY, +# NON-INFRINGEMENT, or FITNESS FOR A PARTICULAR PURPOSE. You should +# have received a copy of GPLv2 along with this software; if not, see +# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. +# + +import pytest +import json +from conftest import RHSMPrivateBus +from constants import RHSM, RHSM_REGISTER_SERVER, RHSM_REGISTER +from dasbus.error import DBusError +from dasbus.typing import get_variant, Str +from funcy import partial + +import logging + +logger = logging.getLogger(__name__) + +""" +Integration test for DBus RHSM Register Object. + +See https://www.candlepinproject.org/docs/subscription-manager/dbus_objects.html#register +for more details. + +Main usecases are presented in this file. + +Special usecases for registering (with proxy, activation keys, ...) are presented +in its own files. + +It is important to run tests as root. Since RegisterServer is a system dbus service. +And it provides a unix socket connection. +""" + +# each call uses standard english locale +locale = "en_US.UTF-8" + + +def test_register(any_candlepin, subman, test_config): + """ + https://www.candlepinproject.org/docs/subscription-manager/dbus_objects.html#methods-6 + """ + assert not subman.is_registered + proxy = RHSM.get_proxy(RHSM_REGISTER_SERVER) + with RHSMPrivateBus(proxy) as private_bus: + private_proxy = private_bus.get_proxy(RHSM.service_name, RHSM_REGISTER.object_path) + response = private_proxy.Register( + "", + test_config.get("candlepin", "username"), + test_config.get("candlepin", "password"), + {}, + {}, + locale, + ) + response_data = json.loads(response) + assert "idCert" in response_data, "A response contains of consumer certificate" + assert frozenset(["key", "cert", "updated", "created", "id", "serial"]).issubset( + frozenset(response_data["idCert"].keys()) + ) + + assert subman.is_registered + + +def test_register_with_org(external_candlepin, subman, test_config): + """ + https://www.candlepinproject.org/docs/subscription-manager/dbus_objects.html#methods-6 + """ + assert not subman.is_registered + proxy = RHSM.get_proxy(RHSM_REGISTER_SERVER) + with RHSMPrivateBus(proxy) as private_bus: + private_proxy = private_bus.get_proxy(RHSM.service_name, RHSM_REGISTER.object_path) + private_proxy.Register( + test_config.get("candlepin", "org"), + test_config.get("candlepin", "username"), + test_config.get("candlepin", "password"), + {}, + {}, + locale, + ) + assert subman.is_registered + + +@pytest.mark.parametrize("enable_content", ["true", "false"]) +def test_register_with_enable_content(external_candlepin, subman, test_config, enable_content): + """ + https://www.candlepinproject.org/docs/subscription-manager/dbus_objects.html#methods-6 + """ + assert not subman.is_registered + proxy = RHSM.get_proxy(RHSM_REGISTER_SERVER) + with RHSMPrivateBus(proxy) as private_bus: + private_proxy = private_bus.get_proxy(RHSM.service_name, RHSM_REGISTER.object_path) + private_proxy.Register( + test_config.get("candlepin", "org"), + test_config.get("candlepin", "username"), + test_config.get("candlepin", "password"), + {"enable_content": get_variant(Str, enable_content)}, + {}, + locale, + ) + assert subman.is_registered + + +@pytest.mark.parametrize( + "credentials", + [("wrong username", None, None), (None, "wrong password", None), (None, None, "wrong organization")], +) +def test_register_with_wrong_values(external_candlepin, subman, test_config, credentials): + """ + https://www.candlepinproject.org/docs/subscription-manager/dbus_objects.html#methods-6 + """ + assert not subman.is_registered + + candlepin_config = partial(test_config.get, "candlepin") + + proxy = RHSM.get_proxy(RHSM_REGISTER_SERVER) + with RHSMPrivateBus(proxy) as private_bus: + private_proxy = private_bus.get_proxy(RHSM.service_name, RHSM_REGISTER.object_path) + + wrong_org = "wrong-organization" + + username = credentials[0] or candlepin_config("username") + password = credentials[1] or candlepin_config("password") + organization = credentials[2] or "" + + with pytest.raises(DBusError) as excinfo: + private_proxy.Register(organization, username, password, {}, {}, locale) + logger.debug(f"raised exception: {excinfo}") + if credentials.organization == wrong_org: + assert f"Organization {wrong_org} does not exist." in str(excinfo.value) + else: + assert "Invalid Credentials" in str(excinfo.value) + assert not subman.is_registered diff --git a/integration-tests/utils.py b/integration-tests/utils.py new file mode 100644 index 0000000000..ef4b54c9bd --- /dev/null +++ b/integration-tests/utils.py @@ -0,0 +1,21 @@ +import time + + +def loop_until(predicate, poll_sec=5, timeout_sec=120): + """ + An helper function to handle a time period waiting for an external service + to update its state. + + an example: + + assert loop_until(lambda: insights_client.is_registered) + + The loop function will retry to run predicate every 5secs + until the total time exceeds timeout_sec. + """ + start = time.time() + ok = False + while (not ok) and (time.time() - start < timeout_sec): + time.sleep(poll_sec) + ok = predicate() + return ok diff --git a/man/rhsm.conf.5 b/man/rhsm.conf.5 index 925f4b5c73..3a8a65291f 100644 --- a/man/rhsm.conf.5 +++ b/man/rhsm.conf.5 @@ -242,7 +242,7 @@ daemon .PP splay .RS 4 -1 to enable splay. 0 to disable splay. If enabled, this feature delays the initial auto attach and cert check by an amount between 0 seconds and the interval given for the action being delayed. For example if the +1 to enable splay. 0 to disable splay. If enabled, this feature delays the initial cert check by an amount between 0 seconds and the interval given for the action being delayed. For example if the .B certCheckInterval were set to 3 minutes, the initial cert check would begin somewhere between 2 minutes after start up (minimum delay) and 5 minutes after start up. This is useful to reduce peak load on the Satellite or entitlement service used by a large number of machines. .RE diff --git a/man/subscription-manager.8 b/man/subscription-manager.8 index 203ac51023..abca3f08b2 100644 --- a/man/subscription-manager.8 +++ b/man/subscription-manager.8 @@ -172,10 +172,6 @@ Gives the username for the account which is registering the system; this user ac .B --password=PASSWORD Gives the user account password. -.TP -.B --token=TOKEN -Token to use when authorizing against the server. - .TP .B --serverurl=SERVER_HOSTNAME Passes the name of the subscription service with which to register the system. The default value, if this is not given, is the Customer Portal Subscription Management service, @@ -340,10 +336,6 @@ Gives the username for the account to use to connect to the organization account .B --password=PASSWORD Gives the user account password [Usable with --list on unregistered systems]. -.TP -.B --token=TOKEN -Token to use when authorizing against the server [Usable with --list on unregistered systems]. - .TP .B --org=ORG Identifies the organization for which the role applies [Usable with --list on unregistered systems]. @@ -388,10 +380,6 @@ Gives the username for the account to use to connect to the organization account .B --password=PASSWORD Gives the user account password [Usable with --list on unregistered systems]. -.TP -.B --token=TOKEN -Token to use when authorizing against the server [Usable with --list on unregistered systems]. - .TP .B --set=SERVICE_LEVEL Service level to apply to this system @@ -424,10 +412,6 @@ Gives the username for the account to use to connect to the organization account .B --password=PASSWORD Gives the user account password [Usable with --list on unregistered systems]. -.TP -.B --token=TOKEN -Token to use when authorizing against the server [Usable with --list on unregistered systems]. - .TP .B --org=ORG Identifies the organization for which the usage applies [Usable with --list on unregistered systems]. @@ -483,10 +467,6 @@ Gives the username for the account to use to connect to the organization account .B --password=PASSWORD Gives the user account password. -.TP -.B --token=TOKEN -Token to use when authorizing against the server. - .TP .B --org=ORG Identifies the organization for which to list the configured environments. @@ -551,10 +531,6 @@ Gives the username for the account to use to connect to the organization account .B --password=PASSWORD Gives the user account password. -.TP -.B --token=TOKEN -Token to use when authorizing against the server. - .TP .B --serverurl=SERVER_HOSTNAME Passes the name of the subscription service to use to list all available organizations. The \fBorgs\fP command will list all organizations for the specified service for which the user account is granted access. The default value, if this is not given, is the Customer Portal Subscription Management service, @@ -640,19 +616,15 @@ Gives the username for the account which is registering the system; this user ac .B --password=PASSWORD Gives the user account password. Optional, for user-based authentication. -.TP -.B --token=TOKEN -Token to use when authorizing against the server. - .TP .B --force -Regenerates the identity certificate for the system using username/password or token authentication. This is used with the +Regenerates the identity certificate for the system using username/password authentication. This is used with the .B --regenerate option. .B --regenerate alone will use an existing identity certificate to authenticate to the subscription management service. If the certificate is missing or corrupted or in other circumstances, then it may be better to use user authentication rather than certificate-based authentication. In that case, the .B --force -option requires the username or password or token to be given either as an argument or in response to a prompt. +option requires the username or password to be given either as an argument or in response to a prompt. .SS FACTS OPTIONS @@ -837,16 +809,16 @@ If a system has never been registered (not even during first boot), then the .B register command will register the system with whatever subscription management service is configured in the .B /etc/rhsm/rhsm.conf -file. This command requires, at a minimum, the username and password or token for an account to connect to the subscription management service. If the credentials aren't passed with the command, then +file. This command requires, at a minimum, the username and password for an account to connect to the subscription management service. If the credentials aren't passed with the command, then .B subscription-manager prompts for the username and password interactively. .PP -When there is a single organization or when using the Customer Portal Subscription Management service, all that is required is the username/password set or the token is used. For example: +When there is a single organization or when using the Customer Portal Subscription Management service, all that is required is the username/password set. For example: .RS .nf -subscription-manager register --username=admin --password=secret or subscription-manager register --token=eyJhbGciOiJSUzI1NiIsI ... stGc_2bFDQC8CENEOo +subscription-manager register --username=admin --password=secret .fi .RE @@ -1011,17 +983,8 @@ subscription-manager repos --list subscription-manager environments --username=jsmith --password=secret --org=prod - or - - subscription-manager environments --token=eyJhbGciOiJSUzI1NiIsI ... stGc_2bFDQC8CENEOo --org=prod - - subscription-manager orgs --username=jsmith --password=secret - -or - -subscription-manager orgs --token=eyJhbGciOiJSUzI1NiIsI ... stGc_2bFDQC8CENEOo .fi .RE diff --git a/po/de.po b/po/de.po index 87441fd0a6..feaabb98ec 100644 --- a/po/de.po +++ b/po/de.po @@ -5,15 +5,15 @@ # ljanda , 2019. #zanata # CoconutNut , 2021. # Ludek Janda , 2021. -# Ettore Atalan , 2021, 2022. +# Ettore Atalan , 2021, 2022, 2024. # Pino Toscano , 2023. msgid "" msgstr "" "Project-Id-Version: rhsm\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-07-21 12:27+0200\n" -"PO-Revision-Date: 2023-01-20 19:20+0000\n" -"Last-Translator: Pino Toscano \n" +"PO-Revision-Date: 2024-11-09 18:38+0000\n" +"Last-Translator: Ettore Atalan \n" "Language-Team: German \n" "Language: de\n" @@ -21,7 +21,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.15.1\n" +"X-Generator: Weblate 5.8.2\n" #: /usr/lib64/python3.9/argparse.py:296 msgid "usage: " @@ -113,7 +113,7 @@ msgstr "Unerkannte Argumente: %s" #: /usr/lib64/python3.9/argparse.py:1928 #, python-format msgid "not allowed with argument %s" -msgstr "nicht erlaubt mit dem Aurgument: %s" +msgstr "nicht erlaubt mit Argument %s" #: /usr/lib64/python3.9/argparse.py:1974 /usr/lib64/python3.9/argparse.py:1988 #, python-format @@ -129,7 +129,7 @@ msgstr "die folgenden Argumente sind erforderlich: %s" #: /usr/lib64/python3.9/argparse.py:2110 #, python-format msgid "one of the arguments %s is required" -msgstr "eines der Argumente %s ist benötigt" +msgstr "eines der Argumente %s ist erforderlich" #: /usr/lib64/python3.9/argparse.py:2153 msgid "expected one argument" diff --git a/po/fr.po b/po/fr.po index 2641df4fd6..ba3278e8b4 100644 --- a/po/fr.po +++ b/po/fr.po @@ -12,13 +12,14 @@ # Transtats , 2022, 2023. # blutch112 , 2022. # Pino Toscano , 2023. +# Léane GRASSER , 2024. msgid "" msgstr "" "Project-Id-Version: rhsm\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-07-21 12:27+0200\n" -"PO-Revision-Date: 2023-09-11 09:17+0000\n" -"Last-Translator: Pino Toscano \n" +"PO-Revision-Date: 2024-12-13 12:38+0000\n" +"Last-Translator: Léane GRASSER \n" "Language-Team: French \n" "Language: fr\n" @@ -26,7 +27,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" -"X-Generator: Weblate 4.18.2\n" +"X-Generator: Weblate 5.8.4\n" #: /usr/lib64/python3.9/argparse.py:296 msgid "usage: " @@ -39,7 +40,7 @@ msgstr ".__call__() non défini" #: /usr/lib64/python3.9/argparse.py:1204 #, python-format msgid "unknown parser %(parser_name)r (choices: %(choices)s)" -msgstr "analyseur syntaxique inconnu %(parser_name)r (choix : %(choices)s)" +msgstr "analyseur syntaxique %(parser_name)r inconnu (choix : %(choices)s)" #: /usr/lib64/python3.9/argparse.py:1264 #, python-format @@ -54,7 +55,7 @@ msgstr "impossible d'ouvrir '%(filename)s' : %(error)s" #: /usr/lib64/python3.9/argparse.py:1482 #, python-format msgid "cannot merge actions - two groups are named %r" -msgstr "ne peut pas fusionner les actions - deux groupes sont nommés %r" +msgstr "impossible de fusionner les actions : deux groupes portent le nom %r" #: /usr/lib64/python3.9/argparse.py:1520 msgid "'required' is an invalid argument for positionals" @@ -109,7 +110,7 @@ msgstr "afficher ce message d'aide et quitter" # translation auto-copied from project subscription-manager, version 1.9.X, document keys, author samfreemanz #: /usr/lib64/python3.9/argparse.py:1775 msgid "cannot have multiple subparser arguments" -msgstr "ne peut pas avoir plusieurs arguments de sous-analyse" +msgstr "impossible d'avoir plusieurs arguments de sous-analyseur" #: /usr/lib64/python3.9/argparse.py:1827 /usr/lib64/python3.9/argparse.py:2338 #, python-format @@ -139,15 +140,15 @@ msgstr "l'un des arguments %s est requis" #: /usr/lib64/python3.9/argparse.py:2153 msgid "expected one argument" -msgstr "attendait un argument" +msgstr "un argument attendu" #: /usr/lib64/python3.9/argparse.py:2154 msgid "expected at most one argument" -msgstr "attend au plus un argument" +msgstr "au plus un argument attendu" #: /usr/lib64/python3.9/argparse.py:2155 msgid "expected at least one argument" -msgstr "attendait au moins un argument" +msgstr "au moins un argument attendu" #: /usr/lib64/python3.9/argparse.py:2159 #, python-format @@ -301,8 +302,8 @@ msgid "" "subscription-manager to register.\n" msgstr "" "\n" -"Le système n'est pas inscrit auprès un serveur de droits d'accès. Utilisez " -"subscription-manager pour l'inscrire.\n" +"Le système n'est pas enregistré auprès un serveur de droits. Utilisez " +"subscription-manager pour l'enregistrer.\n" #: src/plugins/dnf/subscription_manager.py:52 msgid "" @@ -311,9 +312,9 @@ msgid "" "updates. You can use subscription-manager to assign subscriptions.\n" msgstr "" "\n" -"Ce système est inscrit auprès d'un serveur de droits d'accès, mais ne reçoit " -"pas de mises à jour. Vous pouvez utiliser le subscription-manager pour " -"assigner des abonnements.\n" +"Ce système est enregistré auprès d'un serveur de droits, mais ne reçoit pas " +"de mises à jour. Vous pouvez utiliser subscription-manager pour assigner des " +"abonnements.\n" #: src/plugins/dnf/subscription_manager.py:87 msgid "Not root, Subscription Management repositories not updated" @@ -984,11 +985,11 @@ msgstr "Vérification du statut de conformité" #: src/rhsm/connection.py:1782 msgid "Checking system purpose compliance status" -msgstr "Vérification de l'état de conformité de l'objet du système" +msgstr "Vérification de l'état de conformité de la finalité du système" #: src/rhsm/connection.py:1789 msgid "Fetching available system purpose settings" -msgstr "Extraire les paramètres disponibles de l'objectif du système" +msgstr "Récupération des paramètres de finalité du système disponibles" # translation auto-copied from project subscription-manager, version 1.9.X, document keys, author samfreemanz #: src/rhsm/connection.py:1796 src/rhsm/connection.py:1804 @@ -1093,8 +1094,8 @@ msgid "" "This system is not yet registered. Try 'subscription-manager register --" "help' for more information." msgstr "" -"Ce système n'est pas encore inscrit. Essayez 'subscription-manager register " -"--help' pour obtenir plus d'informations." +"Ce système n'est pas encore enregistré. Essayez 'subscription-manager " +"register --help' pour obtenir plus d'informations." #: src/rhsm_debug/debug_commands.py:53 msgid "Assemble system information as a tar file or directory" @@ -1310,11 +1311,12 @@ msgstr "" #: src/rhsmlib/services/entitlement.py:519 msgid "Error: this system is not registered" -msgstr "Erreur : ce système n'est pas inscrit" +msgstr "Erreur : ce système n'est pas enregistré" #: src/rhsmlib/services/register.py:192 msgid "This system is already registered. Add force to options to override." -msgstr "Ce système est déjà inscrit. Ajouter force aux options à substituer." +msgstr "" +"Ce système est déjà enregistré. Ajoutez force aux options pour passer outre." # translation auto-copied from project subscription-manager, version 1.9.X, document keys #: src/rhsmlib/services/register.py:195 @@ -1329,10 +1331,10 @@ msgid "" "with consumerid. Please use --force without --consumerid to re-register or " "use the clean command and try again without --force." msgstr "" -"Erreur : impossible de forcer l'inscription tout en tentant de récupérer " -"l'inscription précédente avec consumerid. Veuillez utiliser --force without " -"--consumerid pour réinscrire, ou utilisez la commande clean et tentez à " -"nouveau avec --force." +"Erreur : Impossible de forcer l'enregistrement tout en essayant de récupérer " +"l'enregistrement précédent avec consumerid. Veuillez utiliser --force sans --" +"consumerid pour effectuer un nouvel enregistrement, ou utilisez la commande " +"clean et essayez à nouveau sans --force." # translation auto-copied from project subscription-manager, version 1.9.X, document keys #: src/rhsmlib/services/register.py:209 @@ -1353,8 +1355,8 @@ msgstr "" #: src/subscription_manager/cli_command/register.py:147 msgid "Error: Activation keys can not be used with previously registered IDs." msgstr "" -"Erreur : les clés d'activation ne peuvent pas être utilisées avec des ID " -"précédemment inscrits." +"Erreur : Les clés d'activation ne peuvent pas être utilisées avec des ID " +"précédemment enregistrés." # translation auto-copied from project subscription-manager, version 1.9.X, document keys #: src/rhsmlib/services/register.py:218 @@ -1394,17 +1396,17 @@ msgstr "Désactivé" # translation auto-copied from project subscription-manager, version 1.9.X, document keys #: src/subscription_manager/branding/__init__.py:77 msgid "Register the system to the server" -msgstr "Inscrire le système sur le serveur" +msgstr "Enregistrer le système auprès du serveur" # translation auto-copied from project subscription-manager, version 1.9.X, document keys #: src/subscription_manager/branding/__init__.py:78 msgid "Unregister the system from the server" -msgstr "Désinscrire le système du serveur" +msgstr "Désenregistrer le système auprès du serveur" # translation auto-copied from project subscription-manager, version 1.9.X, document keys, author samfreemanz #: src/subscription_manager/branding/__init__.py:79 msgid "This system is registered to spacewalk" -msgstr "Ce système est inscrit sur spacewalk" +msgstr "Ce système est enregistré auprès de spacewalk" # translation auto-copied from project subscription-manager, version 1.9.X, document keys #: src/subscription_manager/branding/__init__.py:81 @@ -1416,7 +1418,7 @@ msgstr "AVERTISSEMENT" # translation auto-copied from project subscription-manager, version 1.9.X, document keys #: src/subscription_manager/branding/__init__.py:81 msgid "You have already registered with spacewalk." -msgstr "Vous êtes déjà inscrit avec spacewalk." +msgstr "Vous êtes déjà enregistré auprès de spacewalk." # translation auto-copied from project subscription-manager, version 1.9.X, document keys, author samfreemanz #: src/subscription_manager/branding/__init__.py:84 @@ -1444,8 +1446,8 @@ msgid "" "Register this system to the Customer Portal or another subscription " "management service" msgstr "" -"Inscrire ce système sur le Portail Client ou sur un autre service de gestion " -"des abonnements" +"Enregistrer ce système auprès du Customer Portal ou d'un autre service de " +"gestion d'abonnements" # translation auto-copied from project subscription-manager, version 1.9.X, document keys #: src/subscription_manager/branding/redhat_branding.py:10 @@ -1453,18 +1455,19 @@ msgid "" "Unregister this system from the Customer Portal or another subscription " "management service" msgstr "" -"Désinscrire ce système du Portail Client ou d'un autre service de gestion " -"des abonnements" +"Désenregistrer ce système auprès du Customer Portal ou d'un autre service de " +"gestion d'abonnements" # translation auto-copied from project subscription-manager, version 1.9.X, document keys #: src/subscription_manager/branding/redhat_branding.py:12 msgid "This system is registered to RHN Classic." -msgstr "Ce système est inscrit sur RHN Classic." +msgstr "Ce système est enregistré auprès de RHN Classic." # translation auto-copied from project subscription-manager, version 1.9.X, document keys #: src/subscription_manager/branding/redhat_branding.py:16 msgid "This system has already been registered with Red Hat using RHN Classic." -msgstr "Ce système a déjà été inscrit sur Red Hat à l'aide de RHN Classic." +msgstr "" +"Ce système a déjà été enregistré auprès de Red Hat à l'aide de RHN Classic." # translation auto-copied from project subscription-manager, version 1.9.X, document keys, author samfreemanz #: src/subscription_manager/branding/redhat_branding.py:19 @@ -1472,9 +1475,9 @@ msgid "" "Your system is being registered again using Red Hat Subscription Management. " "Red Hat recommends that customers only register once." msgstr "" -"Votre système est en train d'être réinscrit à l'aide de Red Hat Subscription " -"Management. Red Hat recommande à ses clients de ne s'inscrire qu'une seule " -"fois." +"Votre système est en train d'être enregistré à nouveau à l'aide de Red Hat " +"Subscription Management. Red Hat recommande à ses clients de n'effectuer " +"l'enregistrement qu'une seule fois." # translation auto-copied from project subscription-manager, version 1.9.X, document keys #: src/subscription_manager/branding/redhat_branding.py:24 @@ -1482,8 +1485,8 @@ msgid "" "To learn how to unregister from either service please consult this Knowledge " "Base Article: https://access.redhat.com/kb/docs/DOC-45563" msgstr "" -"Pour apprendre comment se désinscrire de l'un des services, veuillez " -"consulter cet article de la base des connaissances : https://access.redhat." +"Pour savoir comment supprimer l'enregistrement auprès de l'un des services, " +"consultez cet article de la base de connaissances : https://access.redhat." "com/kb/docs/DOC-45563" # translation auto-copied from project subscription-manager, version 1.9.X, document keys, author samfreemanz @@ -1502,14 +1505,14 @@ msgid "" "This system is registered using both RHN Classic and Red Hat Subscription " "Management." msgstr "" -"Ce système est inscrit avec les technologies RHN Classic et Red Hat " +"Ce système est enregistré avec les technologies RHN Classic et Red Hat " "Subscription Management." # translation auto-copied from project subscription-manager, version 1.9.X, document keys, author samfreemanz #: src/subscription_manager/branding/redhat_branding.py:34 msgid "Red Hat recommends that customers only register with one service." msgstr "" -"Red Hat recommande à ses clients de n'inscrire le système qu'à un seul " +"Red Hat recommande à ses clients d'enregistrer le système auprès d'un seul " "service." # translation auto-copied from project subscription-manager, version 1.9.X, document keys @@ -1608,12 +1611,12 @@ msgstr "Obsolète, voir 'syspurpose'" #: src/subscription_manager/cli_command/abstract_syspurpose.py:75 #, python-brace-format msgid "set {attr} of system purpose" -msgstr "définir {attr} dans l'objet du système" +msgstr "définir {attr} dans la finalité du système" #: src/subscription_manager/cli_command/abstract_syspurpose.py:82 #, python-brace-format msgid "unset {attr} of system purpose" -msgstr "supprimer {attr} de l'objet du système" +msgstr "supprimer {attr} de la finalité du système" #: src/subscription_manager/cli_command/abstract_syspurpose.py:90 #, python-brace-format @@ -1652,16 +1655,16 @@ msgstr "Erreur : vous pouvez spécifier --username ou --token mais pas les deux" msgid "" "Error: you must register or specify --username and --password to list {attr}" msgstr "" -"Erreur : vous devez vous inscrire ou spécifier --username et --password pour " -"répertorier {attr}" +"Erreur : vous devez effectuer l'enregistrement ou spécifier --username et --" +"password pour lister {attr}" #: src/subscription_manager/cli_command/abstract_syspurpose.py:173 msgid "" "Error: --username, --password, --token and --org can be used only on " "unregistered systems" msgstr "" -"Erreur : --username, --password, --token et --org ne peuvent être utilisés " -"que sur des systèmes non inscrits" +"Erreur : --username, --password, --token et --org ne peuvent être utilisés " +"que sur des systèmes non enregistrés" #. TRANSLATORS: this is used to quote a string #: src/subscription_manager/cli_command/abstract_syspurpose.py:251 @@ -1690,9 +1693,9 @@ msgid "" "system purpose \"{attr}\". This setting will not influence auto-attaching " "subscriptions." msgstr "" -"Avertissement : Cette organisation n'a pas d'abonnements qui fournissent un " -"objet de système « {attr} ». Ce paramètre n'aura aucune incidence sur " -"l'auto-assignation des abonnements." +"Avertissement : Cette organisation n'a pas d'abonnements qui fournissent une " +"finalité du système « {attr} ». Ce paramètre n'aura aucune incidence sur l" +"'auto-assignation des abonnements." #: src/subscription_manager/cli_command/abstract_syspurpose.py:278 #, python-brace-format @@ -1732,8 +1735,8 @@ msgid "" "There are no available values for the system purpose \"{syspurpose_attr}\" " "from the available subscriptions in this organization." msgstr "" -"Il n'y a pas de valeurs disponibles pour l'objet du système " -"\"{syspurpose_attr}\" parmi les abonnements disponibles dans cette " +"Il n'y a pas de valeurs disponibles pour la finalité du système \"" +"{syspurpose_attr}\" parmi les abonnements disponibles dans cette " "organisation." #: src/subscription_manager/cli_command/abstract_syspurpose.py:409 @@ -1742,8 +1745,8 @@ msgid "" "Unable to get the list of valid values for the system purpose " "\"{syspurpose_attr}\"." msgstr "" -"Impossible d'obtenir la liste des valeurs valides pour l'objet du système " -"\"{syspurpose_attr}\"." +"Impossible d'obtenir la liste des valeurs valides pour la finalité du " +"système \"{syspurpose_attr}\"." #: src/subscription_manager/cli_command/abstract_syspurpose.py:435 msgid "Unable to connect to server using token" @@ -1763,11 +1766,11 @@ msgid "" "Purpose {attr}." msgstr "" "Remarque : le serveur de droits d'accès actuellement configuré ne prend pas " -"en charge l'objet du système {attr}." +"en charge la finalité du système {attr}." #: src/subscription_manager/cli_command/addons.py:24 msgid "Show or modify the system purpose addons setting" -msgstr "Afficher ou modifier les paramètres de l'extension objet du système" +msgstr "Afficher ou modifier les paramètres de l'extension finalité du système" #: src/subscription_manager/cli_command/attach.py:51 msgid "The ID of the pool to attach (can be specified more than once)" @@ -1784,7 +1787,7 @@ msgid "" "Automatically attach the best-matched compatible subscriptions to this " "system. This is the default action." msgstr "" -"Assigne automatiquement les abonnements compatibles les mieux adaptés à ce " +"Assigner automatiquement les abonnements compatibles les mieux adaptés à ce " "système. Il s'agit de l'action par défaut." #: src/subscription_manager/cli_command/attach.py:71 @@ -1820,8 +1823,8 @@ msgid "" "Attach a specified subscription to the registered system, when system does " "not use Simple Content Access mode" msgstr "" -"Attachez un abonnement spécifié au système enregistré, lorsque le système " -"n'utilise pas le mode d'accès simple au contenu" +"Assigner l'abonnement spécifié au système enregistré, lorsque le système " +"n'utilise pas le mode Simple Content Access" #: src/subscription_manager/cli_command/attach.py:111 msgid "Error: --auto may not be used when specifying pools." @@ -1864,9 +1867,9 @@ msgid "" "Ignoring the request to attach. Attaching subscriptions is disabled for " "organization \"{owner_id}\" because Simple Content Access (SCA) is enabled." msgstr "" -"Ignorer la demande de rattachement. Le rattachement des abonnements est " -"désactivé pour l'organisation \"{owner_id}\" car l'accès simple au contenu " -"(SCA) est activé." +"Demande d'assignation ignorée. L'assignation des abonnements est désactivée " +"pour l'organisation \"{owner_id}\" car Simple Content Access (SCA) est " +"activé." # translation auto-copied from project subscription-manager, version 1.9.X, document keys, author samfreemanz #: src/subscription_manager/cli_command/attach.py:201 @@ -1979,9 +1982,9 @@ msgid "" "Ignoring the request to auto-attach. Attaching subscriptions is disabled for " "organization \"{owner_id}\" because Simple Content Access (SCA) is enabled." msgstr "" -"Ignorer la demande de rattachement automatique. Le rattachement des " -"abonnements est désactivé pour l'organisation \"{owner_id}\" car l'accès " -"simple au contenu (SCA) est activé." +"Demande d'assignation automatique ignorée. L'assignation des abonnements est " +"désactivée pour l'organisation \"{owner_id}\" car Simple Content Access (SCA)" +" est activé." #: src/subscription_manager/cli_command/cli.py:152 msgid "server URL in the form of https://hostname:port/prefix" @@ -2022,8 +2025,8 @@ msgstr "Ne pas afficher les messages de progression" msgid "" "Consumer identity either does not exist or is corrupted. Try register --help" msgstr "" -"L'identité du consommateur n'existe pas ou est corrompue. Essayez la " -"commande register --help" +"L'identité du consommateur n'existe pas ou est corrompue. Essayez register " +"--help" #: src/subscription_manager/cli_command/cli.py:267 msgid "" @@ -2064,7 +2067,8 @@ msgstr "" # translation auto-copied from project subscription-manager, version 1.9.X, document keys #: src/subscription_manager/cli_command/cli.py:422 msgid "System certificates corrupted. Please reregister." -msgstr "Certificats système corrompus. Veuillez réinscrire le système." +msgstr "" +"Certificats système corrompus. Veuillez effectuer l'enregistrement à nouveau." #: src/subscription_manager/cli_command/cli.py:433 #, python-brace-format @@ -2182,8 +2186,8 @@ msgstr "liste des environnements non activés pour ce consommateur" #: src/subscription_manager/cli_command/environments.py:81 msgid "You may not specify an --org for environments when registered." msgstr "" -"Vous ne pouvez pas spécifier un --org pour les environnements lors de " -"l'enregistrement." +"Vous ne pouvez pas spécifier un --org pour les environnements une fois " +"l'enregistrement effectué." # translation auto-copied from project subscription-manager, version 1.9.X, document keys, author samfreemanz #: src/subscription_manager/cli_command/environments.py:90 @@ -2365,8 +2369,8 @@ msgid "" "Error: You may not import certificates into a system that is registered to a " "subscription management service." msgstr "" -"Erreur : vous ne pouvez pas importer de certificats dans un système qui est " -"déjà inscrit à un service de gestion des abonnements." +"Erreur : Vous ne pouvez pas importer de certificats dans un système qui est " +"déjà enregistré auprès d'un service de gestion des abonnements." # translation auto-copied from project subscription-manager, version 1.9.X, document keys, author samfreemanz #: src/subscription_manager/cli_command/import_cert.py:52 @@ -2996,8 +3000,7 @@ msgstr "Référentiel : {repo}" #: src/subscription_manager/cli_command/owners.py:34 msgid "Display the organizations against which a user can register a system" msgstr "" -"Afficher les organisations avec lesquelles un utilisateur peut inscrire un " -"système" +"Afficher les organisations disponibles pour l'enregistrement d'un système" # translation auto-copied from project subscription-manager, version 1.9.X, document keys, author samfreemanz #: src/subscription_manager/cli_command/owners.py:52 @@ -3036,7 +3039,7 @@ msgstr "énumérer les emplacements de greffons {SM}" #: src/subscription_manager/cli_command/plugins.py:37 #, python-brace-format msgid "list {SM} plugin hooks" -msgstr "énumérer les points d'ancrage des greffons {SM}" +msgstr "énumérer les hooks du plugin {SM}" # translation auto-copied from project subscription-manager, version 1.9.X, document keys, author samfreemanz #: src/subscription_manager/cli_command/plugins.py:40 @@ -3115,7 +3118,7 @@ msgstr "" # translation auto-copied from project subscription-manager, version 1.9.X, document keys #: src/subscription_manager/cli_command/register.py:81 msgid "name of the system to register, defaults to the hostname" -msgstr "nom du système à inscrire, par défaut un nom d'hôte" +msgstr "nom du système à enregistrer, par défaut : nom d'hôte" # translation auto-copied from project subscription-manager, version 1.9.X, document keys #: src/subscription_manager/cli_command/register.py:87 @@ -3128,8 +3131,8 @@ msgid "" "register with one of multiple organizations for the user, using organization " "key" msgstr "" -"inscrire sur une des multiples organisations pour l'utilisateur, à l'aide de " -"la clé de l'organisation" +"enregistrer auprès d'une des organisations de l'utilisateur à l'aide d'une " +"clé d'organisation" #: src/subscription_manager/cli_command/register.py:99 msgid "" @@ -3137,10 +3140,10 @@ msgid "" "(a comma-separated list) in the destination org. The ability to use multiple " "environments is controlled by the entitlement server" msgstr "" -"s'inscrire à un environnement spécifique (valeur unique) ou à plusieurs " -"environnements (liste séparée par des virgules) dans l'org de destination. " -"La possibilité d'utiliser plusieurs environnements est contrôlée par le " -"serveur d'autorisation" +"enregistrer auprès d'un environnement spécifique (valeur unique) ou de " +"plusieurs environnements (liste séparée par des virgules) dans " +"l'organisation de destination. La possibilité d'utiliser plusieurs " +"environnements est contrôlée par le serveur de droits" # translation auto-copied from project subscription-manager, version 1.9.X, document keys #: src/subscription_manager/cli_command/register.py:107 @@ -3183,7 +3186,7 @@ msgstr "" # translation auto-copied from project subscription-manager, version 1.9.X, document keys #: src/subscription_manager/cli_command/register.py:140 msgid "This system is already registered. Use --force to override" -msgstr "Ce système est déjà inscrit. Utilisez --force pour remplacer" +msgstr "Ce système est déjà enregistré. Utilisez --force pour passer outre" # translation auto-copied from project subscription-manager, version 1.9.X, document keys #: src/subscription_manager/cli_command/register.py:152 @@ -3225,7 +3228,7 @@ msgstr "Annulation de l’inscription de : {hostname}:{port}{prefix}" #: src/subscription_manager/cli_command/register.py:301 #, python-brace-format msgid "The system with UUID {old_uuid} has been unregistered" -msgstr "Le système avec l'UUID {old_uuid} a été désinscrit" +msgstr "Le système avec l'UUID {old_uuid} a été désenregistré" #: src/subscription_manager/cli_command/register.py:325 #, python-brace-format @@ -3236,17 +3239,17 @@ msgstr "Inscription sur : {hostname}:{port}{prefix}" #: src/subscription_manager/cli_command/register.py:365 #, python-brace-format msgid "Error during registration: {e}" -msgstr "Erreur lors de l'inscription : {e}" +msgstr "Erreur lors de l'enregistrement : {e}" #: src/subscription_manager/cli_command/register.py:368 #, python-brace-format msgid "The system has been registered with ID: {id}" -msgstr "Le système a été inscrit avec l'ID : {id}" +msgstr "Le système a été enregistré avec l'ID : {id}" #: src/subscription_manager/cli_command/register.py:369 #, python-brace-format msgid "The registered system name is: {name}" -msgstr "Le nom du système inscrit est : {name}" +msgstr "Le nom de système enregistré est : {name}" #: src/subscription_manager/cli_command/register.py:371 #, python-brace-format @@ -3386,9 +3389,8 @@ msgid "" "Error: The registered entitlement server does not support remove --pool.\n" "Instead, use the remove --serial option." msgstr "" -"Erreur : Le serveur de droits d'accès utilisé ne prend pas en charge remove " -"--pool.\n" -"Veuillez plutôt utiliser l'option remove --serial." +"Erreur : Le serveur de droits utilisé ne prend pas en charge remove --pool.\n" +"Utilisez plutôt l'option remove --serial." #: src/subscription_manager/cli_command/remove.py:87 msgid "" @@ -3531,12 +3533,12 @@ msgstr "Le référentiel « {repoid} » est désactivé pour ce système." #: src/subscription_manager/cli_command/role.py:24 msgid "Show or modify the system purpose role setting" -msgstr "Afficher ou modifier les paramètres de rôle de l'objet du système" +msgstr "Afficher ou modifier les paramètres de rôle de la finalité du système" #: src/subscription_manager/cli_command/service_level.py:42 msgid "Show or modify the system purpose service-level setting" msgstr "" -"Afficher ou modifier le paramètre niveau de service de l'objet du système" +"Afficher ou modifier le paramètre niveau de service de la finalité du système" #: src/subscription_manager/cli_command/service_level.py:70 msgid "Error: --org is only supported with the --list or --set option" @@ -3549,8 +3551,8 @@ msgid "" "Error: you must register or specify --username and --password to list " "service levels" msgstr "" -"Erreur : vous devez inscrire le système ou spécifier --username et --" -"password pour répertorier les niveaux de service" +"Erreur : Vous devez effectuer l'enregistrement ou spécifier --username et --" +"password pour lister les niveaux de service" #: src/subscription_manager/cli_command/service_level.py:99 msgid "" @@ -3624,7 +3626,7 @@ msgid "" "Content Access Mode is set to Simple Content Access. This host has access to " "content, regardless of subscription status.\n" msgstr "" -"Le mode d'accès au contenu est réglé sur Simple Content Access. Cet hôte a " +"Le mode d'accès au contenu est défini sur Simple Content Access. Cet hôte a " "accès au contenu, quel que soit le statut de l'abonnement.\n" #: src/subscription_manager/cli_command/status.py:111 @@ -3639,15 +3641,16 @@ msgstr "" #: src/subscription_manager/cli_command/status.py:145 #, python-brace-format msgid "System Purpose Status: {status}" -msgstr "État de l'objet du système : {status}" +msgstr "État de la finalité du système : {status}" #: src/subscription_manager/cli_command/syspurpose.py:53 msgid "Convenient module for managing all system purpose settings" -msgstr "Module pratique pour gérer tous les paramètres de l'objet du système" +msgstr "" +"Module pratique pour gérer tous les paramètres de la finalité du système" #: src/subscription_manager/cli_command/syspurpose.py:59 msgid "show current system purpose" -msgstr "montrer l'objet du système actuel" +msgstr "montrer la finalité du système actuel" # translation auto-copied from project subscription-manager, version 1.9.X, document keys #: src/subscription_manager/cli_command/syspurpose.py:76 @@ -3669,16 +3672,17 @@ msgstr "%(prog)s {name} [SOUS-MODULE] [OPTIONS]" #: src/subscription_manager/cli_command/unregister.py:38 #: src/subscription_manager/utils.py:286 msgid "This system is currently not registered." -msgstr "Ce système n'est pas actuellement inscrit." +msgstr "Ce système n'est actuellement pas enregistré." # translation auto-copied from project subscription-manager, version 1.9.X, document keys, author samfreemanz #: src/subscription_manager/cli_command/unregister.py:71 msgid "System has been unregistered." -msgstr "Le système a été désinscrit." +msgstr "Le système a été désenregistré." #: src/subscription_manager/cli_command/usage.py:24 msgid "Show or modify the system purpose usage setting" -msgstr "Afficher ou modifier le paramètre d'utilisation de l'objet du système" +msgstr "" +"Afficher ou modifier le paramètre d'utilisation de la finalité du système" # translation auto-copied from project subscription-manager, version 1.9.X, document keys, author samfreemanz #: src/subscription_manager/cli_command/user_pass.py:40 @@ -3876,7 +3880,7 @@ msgstr "" # translation auto-copied from project subscription-manager, version 1.9.X, document keys, author samfreemanz #: src/subscription_manager/exceptions.py:55 msgid "Server URL can not be None" -msgstr "L'URL du serveur ne peut pas être « Aucun »" +msgstr "L'URL du serveur ne peut pas être None" # translation auto-copied from project subscription-manager, version 1.9.X, document keys, author samfreemanz #: src/subscription_manager/exceptions.py:56 diff --git a/po/it.po b/po/it.po index f76498039b..2d886f3549 100644 --- a/po/it.po +++ b/po/it.po @@ -2,13 +2,14 @@ # ljanda , 2018. #zanata # John Sefler , 2019. #zanata # Pino Toscano , 2021, 2022, 2023. +# Salvatore Cocuzza , 2024. msgid "" msgstr "" "Project-Id-Version: rhsm\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-07-21 12:27+0200\n" -"PO-Revision-Date: 2023-05-12 10:21+0000\n" -"Last-Translator: Pino Toscano \n" +"PO-Revision-Date: 2024-12-23 23:38+0000\n" +"Last-Translator: Salvatore Cocuzza \n" "Language-Team: Italian \n" "Language: it\n" @@ -16,7 +17,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.15.2\n" +"X-Generator: Weblate 5.9.2\n" #: /usr/lib64/python3.9/argparse.py:296 msgid "usage: " @@ -3046,13 +3047,13 @@ msgstr "" # translation auto-copied from project subscription-manager, version 1.9.X, document keys #: src/subscription_manager/cli_command/redeem.py:43 -#, fuzzy msgid "" "optional language to use for email notification when subscription redemption " "is complete (Examples: en-us, de-de)" msgstr "" -"lingua facoltativa da usare per la notifica via posta elettronica quando " -"l'acquisizione della sottoscrizione è stata completata (esempi: en-us, de-de)" +"lingua facoltativa da usare per la notifica delle email quando " +"l'acquisizione della sottoscrizione è stata completata. (Esempi: en-us, de-" +"de)" # translation auto-copied from project subscription-manager, version 1.9.X, document keys, author fvalen #: src/subscription_manager/cli_command/redeem.py:52 diff --git a/po/keys.pot b/po/keys.pot index b62699cec4..ae71d336cf 100644 --- a/po/keys.pot +++ b/po/keys.pot @@ -8,226 +8,247 @@ msgid "" msgstr "" "Project-Id-Version: rhsm\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-07-21 12:27+0200\n" +"POT-Creation-Date: 2025-01-07 18:12+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=CHARSET\n" +"Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" -#: /usr/lib64/python3.9/argparse.py:296 +#: /usr/lib64/python3.12/argparse.py:228 +#, python-format +msgid "%(heading)s:" +msgstr "" + +#: /usr/lib64/python3.12/argparse.py:299 msgid "usage: " msgstr "" -#: /usr/lib64/python3.9/argparse.py:861 +#: /usr/lib64/python3.12/argparse.py:722 +#, python-format +msgid " (default: %(default)s)" +msgstr "" + +#: /usr/lib64/python3.12/argparse.py:784 +#, python-format +msgid "argument %(argument_name)s: %(message)s" +msgstr "" + +#: /usr/lib64/python3.12/argparse.py:890 msgid ".__call__() not defined" msgstr "" -#: /usr/lib64/python3.9/argparse.py:1204 +#: /usr/lib64/python3.12/argparse.py:1161 +msgid "show program's version number and exit" +msgstr "" + +#: /usr/lib64/python3.12/argparse.py:1223 +#, python-format +msgid "conflicting subparser: %s" +msgstr "" + +#: /usr/lib64/python3.12/argparse.py:1227 +#, python-format +msgid "conflicting subparser alias: %s" +msgstr "" + +#: /usr/lib64/python3.12/argparse.py:1262 #, python-format msgid "unknown parser %(parser_name)r (choices: %(choices)s)" msgstr "" -#: /usr/lib64/python3.9/argparse.py:1264 +#: /usr/lib64/python3.12/argparse.py:1323 #, python-format msgid "argument \"-\" with mode %r" msgstr "" -#: /usr/lib64/python3.9/argparse.py:1273 +#: /usr/lib64/python3.12/argparse.py:1332 #, python-format msgid "can't open '%(filename)s': %(error)s" msgstr "" -#: /usr/lib64/python3.9/argparse.py:1482 +#: /usr/lib64/python3.12/argparse.py:1541 #, python-format msgid "cannot merge actions - two groups are named %r" msgstr "" -#: /usr/lib64/python3.9/argparse.py:1520 +#: /usr/lib64/python3.12/argparse.py:1583 msgid "'required' is an invalid argument for positionals" msgstr "" -#: /usr/lib64/python3.9/argparse.py:1542 +#: /usr/lib64/python3.12/argparse.py:1604 #, python-format msgid "" "invalid option string %(option)r: must start with a character " "%(prefix_chars)r" msgstr "" -#: /usr/lib64/python3.9/argparse.py:1560 +#: /usr/lib64/python3.12/argparse.py:1622 #, python-format msgid "dest= is required for options like %r" msgstr "" -#: /usr/lib64/python3.9/argparse.py:1577 +#: /usr/lib64/python3.12/argparse.py:1639 #, python-format msgid "invalid conflict_resolution value: %r" msgstr "" -#: /usr/lib64/python3.9/argparse.py:1595 +#: /usr/lib64/python3.12/argparse.py:1657 #, python-format msgid "conflicting option string: %s" msgid_plural "conflicting option strings: %s" msgstr[0] "" msgstr[1] "" -#: /usr/lib64/python3.9/argparse.py:1661 +#: /usr/lib64/python3.12/argparse.py:1731 msgid "mutually exclusive arguments must be optional" msgstr "" -#: /usr/lib64/python3.9/argparse.py:1728 +#: /usr/lib64/python3.12/argparse.py:1807 msgid "positional arguments" msgstr "" -#: /usr/lib64/python3.9/argparse.py:1729 -msgid "optional arguments" +#: /usr/lib64/python3.12/argparse.py:1808 +msgid "options" msgstr "" -#: /usr/lib64/python3.9/argparse.py:1744 +#: /usr/lib64/python3.12/argparse.py:1823 msgid "show this help message and exit" msgstr "" -#: /usr/lib64/python3.9/argparse.py:1775 +#: /usr/lib64/python3.12/argparse.py:1854 msgid "cannot have multiple subparser arguments" msgstr "" -#: /usr/lib64/python3.9/argparse.py:1827 /usr/lib64/python3.9/argparse.py:2338 +#: /usr/lib64/python3.12/argparse.py:1860 +msgid "subcommands" +msgstr "" + +#: /usr/lib64/python3.12/argparse.py:1906 +#: /usr/lib64/python3.12/argparse.py:2459 #, python-format msgid "unrecognized arguments: %s" msgstr "" -#: /usr/lib64/python3.9/argparse.py:1928 +#: /usr/lib64/python3.12/argparse.py:2011 #, python-format msgid "not allowed with argument %s" msgstr "" -#: /usr/lib64/python3.9/argparse.py:1974 /usr/lib64/python3.9/argparse.py:1988 +#: /usr/lib64/python3.12/argparse.py:2030 +#, python-format +msgid "ambiguous option: %(option)s could match %(matches)s" +msgstr "" + +#: /usr/lib64/python3.12/argparse.py:2062 +#: /usr/lib64/python3.12/argparse.py:2094 #, python-format msgid "ignored explicit argument %r" msgstr "" -#: /usr/lib64/python3.9/argparse.py:2095 +#: /usr/lib64/python3.12/argparse.py:2230 #, python-format msgid "the following arguments are required: %s" msgstr "" -#: /usr/lib64/python3.9/argparse.py:2110 +#: /usr/lib64/python3.12/argparse.py:2245 #, python-format msgid "one of the arguments %s is required" msgstr "" -#: /usr/lib64/python3.9/argparse.py:2153 +#: /usr/lib64/python3.12/argparse.py:2289 msgid "expected one argument" msgstr "" -#: /usr/lib64/python3.9/argparse.py:2154 +#: /usr/lib64/python3.12/argparse.py:2290 msgid "expected at most one argument" msgstr "" -#: /usr/lib64/python3.9/argparse.py:2155 +#: /usr/lib64/python3.12/argparse.py:2291 msgid "expected at least one argument" msgstr "" -#: /usr/lib64/python3.9/argparse.py:2159 +#: /usr/lib64/python3.12/argparse.py:2295 #, python-format msgid "expected %s argument" msgid_plural "expected %s arguments" msgstr[0] "" msgstr[1] "" -#: /usr/lib64/python3.9/argparse.py:2217 -#, python-format -msgid "ambiguous option: %(option)s could match %(matches)s" -msgstr "" - -#: /usr/lib64/python3.9/argparse.py:2281 +#: /usr/lib64/python3.12/argparse.py:2405 #, python-format msgid "unexpected option string: %s" msgstr "" -#: /usr/lib64/python3.9/argparse.py:2478 +#: /usr/lib64/python3.12/argparse.py:2541 #, python-format msgid "%r is not callable" msgstr "" -#: /usr/lib64/python3.9/argparse.py:2495 +#: /usr/lib64/python3.12/argparse.py:2557 #, python-format msgid "invalid %(type)s value: %(value)r" msgstr "" -#: /usr/lib64/python3.9/argparse.py:2506 +#: /usr/lib64/python3.12/argparse.py:2572 #, python-format msgid "invalid choice: %(value)r (choose from %(choices)s)" msgstr "" -#: /usr/lib64/python3.9/argparse.py:2582 +#: /usr/lib64/python3.12/argparse.py:2650 #, python-format msgid "%(prog)s: error: %(message)s\n" msgstr "" -#: src/daemons/rhsmcertd.c:107 -msgid "deprecated, see --cert-check-interval" -msgstr "" - -#: src/daemons/rhsmcertd.c:110 +#: src/daemons/rhsmcertd.c:99 msgid "interval to run cert check (in minutes)" msgstr "" -#: src/daemons/rhsmcertd.c:114 -msgid "deprecated, see --auto-attach-interval" -msgstr "" - -#: src/daemons/rhsmcertd.c:117 -msgid "interval to run auto-attach (in minutes)" -msgstr "" - -#: src/daemons/rhsmcertd.c:120 +#: src/daemons/rhsmcertd.c:102 msgid "interval to run auto-registration (in minutes)" msgstr "" -#: src/daemons/rhsmcertd.c:123 +#: src/daemons/rhsmcertd.c:105 msgid "run the initial checks immediately, with no delay" msgstr "" -#: src/daemons/rhsmcertd.c:126 +#: src/daemons/rhsmcertd.c:108 msgid "show debug messages" msgstr "" -#: src/daemons/rhsmcertd.c:128 +#: src/daemons/rhsmcertd.c:110 msgid "do not add an offset to the initial checks." msgstr "" -#: src/daemons/rhsmcertd.c:131 +#: src/daemons/rhsmcertd.c:113 msgid "try to perform auto-registration." msgstr "" -#: src/daemons/rhsmcertd.c:612 -#, c-format +#: src/daemons/rhsmcertd.c:545 msgid "For more information run: rhsmcertd --help\n" msgstr "" -#: src/daemons/rhsmcertd.c:729 +#: src/daemons/rhsmcertd.c:651 msgid "Wrong number of arguments specified.\n" msgstr "" -#: src/daemons/rhsmcertd.c:805 +#: src/daemons/rhsmcertd.c:720 msgid "Invalid argument specified.\n" msgstr "" -#: src/daemons/rhsmcertd.c:811 -#, c-format +#: src/daemons/rhsmcertd.c:726 msgid "WARN: Deprecated CLI arguments are being used.\n" msgstr "" -#: src/daemons/rhsmcertd.c:826 +#: src/daemons/rhsmcertd.c:741 #, c-format msgid "Invalid option: %s\n" msgstr "" -#: src/daemons/rhsmcertd.c:840 +#: src/daemons/rhsmcertd.c:755 #, c-format msgid "Invalid argument specified: %s\n" msgstr "" @@ -236,7 +257,7 @@ msgstr "" msgid "Installed products updated." msgstr "" -#: src/plugins/dnf/subscription_manager.py:35 +#: src/plugins/dnf/subscription_manager.py:46 #, python-format msgid "" "\n" @@ -249,25 +270,40 @@ msgid "" "active subscriptions, you can renew the expired subscription. " msgstr "" -#: src/plugins/dnf/subscription_manager.py:46 +#: src/plugins/dnf/subscription_manager.py:57 msgid "" "\n" "This system is not registered with an entitlement server. You can use " "subscription-manager to register.\n" msgstr "" -#: src/plugins/dnf/subscription_manager.py:52 +#: src/plugins/dnf/subscription_manager.py:63 +msgid "" +"\n" +"This system is not registered with an entitlement server. You can use " +"\"rhc\" or \"subscription-manager\" to register.\n" +msgstr "" + +#: src/plugins/dnf/subscription_manager.py:70 msgid "" "\n" "This system is registered with an entitlement server, but is not receiving " "updates. You can use subscription-manager to assign subscriptions.\n" msgstr "" -#: src/plugins/dnf/subscription_manager.py:87 +#: src/plugins/dnf/subscription_manager.py:77 +#, python-brace-format +msgid "" +"\n" +"This system has release set to {release_version} and it receives updates " +"only for this release.\n" +msgstr "" + +#: src/plugins/dnf/subscription_manager.py:112 msgid "Not root, Subscription Management repositories not updated" msgstr "" -#: src/plugins/dnf/subscription_manager.py:115 +#: src/plugins/dnf/subscription_manager.py:140 #, python-format msgid "" "subscription-manager plugin disabled %d system repository with respect of " @@ -278,15 +314,15 @@ msgid_plural "" msgstr[0] "" msgstr[1] "" -#: src/plugins/dnf/subscription_manager.py:133 +#: src/plugins/dnf/subscription_manager.py:158 msgid "Updating Subscription Management repositories." msgstr "" -#: src/plugins/dnf/subscription_manager.py:138 +#: src/plugins/dnf/subscription_manager.py:167 msgid "Unable to read consumer identity" msgstr "" -#: src/plugins/dnf/subscription_manager.py:141 +#: src/plugins/dnf/subscription_manager.py:170 msgid "subscription-manager is operating in container mode." msgstr "" @@ -743,124 +779,115 @@ msgstr "" msgid "Unknown Certificate Type" msgstr "" -#: src/rhsm/config.py:244 +#: src/rhsm/config.py:243 #, python-brace-format msgid "Invalid Log Level: {lvl}, setting to INFO for this run." msgstr "" -#: src/rhsm/config.py:249 +#: src/rhsm/config.py:248 msgid "" "Please use: subscription-manager config --logging.default_log_level= to set the default_log_level to a valid value." msgstr "" -#: src/rhsm/config.py:255 +#: src/rhsm/config.py:254 #, python-brace-format msgid "Valid Values: {valid_str}" msgstr "" -#: src/rhsm/connection.py:1402 +#: src/rhsm/connection.py:1444 msgid "Fetching supported resources" msgstr "" -#: src/rhsm/connection.py:1461 +#: src/rhsm/connection.py:1504 msgid "Checking connection status" msgstr "" -#: src/rhsm/connection.py:1489 +#: src/rhsm/connection.py:1529 msgid "Fetching cloud token" msgstr "" -#: src/rhsm/connection.py:1562 +#: src/rhsm/connection.py:1608 msgid "Registering system" msgstr "" -#: src/rhsm/connection.py:1592 src/rhsm/connection.py:1604 +#: src/rhsm/connection.py:1638 src/rhsm/connection.py:1650 msgid "Updating detected virtual machines running on given host" msgstr "" -#: src/rhsm/connection.py:1627 +#: src/rhsm/connection.py:1673 msgid "Updating hypervisor information" msgstr "" -#: src/rhsm/connection.py:1702 +#: src/rhsm/connection.py:1745 msgid "Updating consumer information" msgstr "" -#: src/rhsm/connection.py:1707 src/rhsm/connection.py:1711 +#: src/rhsm/connection.py:1750 src/rhsm/connection.py:1754 msgid "Fetching guest information" msgstr "" -#: src/rhsm/connection.py:1715 +#: src/rhsm/connection.py:1758 msgid "Removing guests" msgstr "" -#: src/rhsm/connection.py:1745 src/rhsm/connection.py:1756 +#: src/rhsm/connection.py:1788 src/rhsm/connection.py:1799 msgid "Updating profile information" msgstr "" -#: src/rhsm/connection.py:1764 +#: src/rhsm/connection.py:1807 msgid "Fetching consumer keys" msgstr "" -#: src/rhsm/connection.py:1773 +#: src/rhsm/connection.py:1816 msgid "Checking compliance status" msgstr "" -#: src/rhsm/connection.py:1782 +#: src/rhsm/connection.py:1825 msgid "Checking system purpose compliance status" msgstr "" -#: src/rhsm/connection.py:1789 +#: src/rhsm/connection.py:1832 msgid "Fetching available system purpose settings" msgstr "" -#: src/rhsm/connection.py:1796 src/rhsm/connection.py:1804 +#: src/rhsm/connection.py:1839 src/rhsm/connection.py:1847 msgid "Fetching organizations" msgstr "" -#: src/rhsm/connection.py:1817 +#: src/rhsm/connection.py:1860 msgid "Unregistering system" msgstr "" -#: src/rhsm/connection.py:1830 +#: src/rhsm/connection.py:1884 msgid "Fetching certificates" msgstr "" -#: src/rhsm/connection.py:1839 +#: src/rhsm/connection.py:1893 msgid "Fetching certificate serial numbers" msgstr "" -#: src/rhsm/connection.py:1855 +#: src/rhsm/connection.py:1909 msgid "Fetching content for a certificate" msgstr "" -#: src/rhsm/connection.py:1868 src/rhsm/connection.py:1885 -msgid "Updating subscriptions" -msgstr "" - -#: src/rhsm/connection.py:1894 src/rhsm/connection.py:1904 -#: src/rhsm/connection.py:1913 -msgid "Unsubscribing" -msgstr "" - -#: src/rhsm/connection.py:1962 +#: src/rhsm/connection.py:1959 msgid "Fetching pools" msgstr "" -#: src/rhsm/connection.py:1973 +#: src/rhsm/connection.py:1970 msgid "Fetching release information" msgstr "" -#: src/rhsm/connection.py:1987 +#: src/rhsm/connection.py:1984 msgid "Fetching available releases" msgstr "" -#: src/rhsm/connection.py:2002 +#: src/rhsm/connection.py:1999 msgid "Fetching entitlements" msgstr "" -#: src/rhsm/connection.py:2011 +#: src/rhsm/connection.py:2008 msgid "Fetching service levels" msgstr "" @@ -892,16 +919,12 @@ msgstr "" msgid "Removing content overrides" msgstr "" -#: src/rhsm/connection.py:2118 -msgid "Activating" -msgstr "" - -#: src/rhsm/connection.py:2127 +#: src/rhsm/connection.py:2111 msgid "Fetching job" msgstr "" #: src/rhsm_debug/debug_commands.py:39 -#: src/subscription_manager/cli_command/cli.py:51 +#: src/subscription_manager/cli_command/cli.py:50 msgid "" "This system is not yet registered. Try 'subscription-manager register --" "help' for more information." @@ -954,13 +977,13 @@ msgstr "" msgid "Unable to create zip file of system information: {error}" msgstr "" -#: src/rhsmlib/dbus/objects/register.py:133 +#: src/rhsmlib/dbus/objects/register.py:156 #, python-format msgid "" "User %s is member of more organizations, but no organization was selected" msgstr "" -#: src/rhsmlib/dbus/objects/register.py:149 +#: src/rhsmlib/dbus/objects/register.py:172 #, python-format msgid "User %s is not member of any organization" msgstr "" @@ -995,41 +1018,36 @@ msgstr "" #: src/rhsmlib/services/entitlement.py:94 #: src/rhsmlib/services/syspurpose.py:107 #: src/subscription_manager/cert_sorter.py:296 -#: src/subscription_manager/cli_command/cli.py:233 -#: src/subscription_manager/cli_command/cli.py:237 -#: src/subscription_manager/cli_command/cli.py:238 -#: src/subscription_manager/cli_command/cli.py:239 +#: src/subscription_manager/cli_command/cli.py:213 +#: src/subscription_manager/cli_command/cli.py:217 +#: src/subscription_manager/cli_command/cli.py:218 +#: src/subscription_manager/cli_command/cli.py:219 #: src/subscription_manager/cli_command/facts.py:66 -#: src/subscription_manager/cli_command/list.py:54 #: src/subscription_manager/reasons.py:110 -#: src/subscription_manager/utils.py:269 src/subscription_manager/utils.py:285 -#: src/subscription_manager/utils.py:287 src/subscription_manager/utils.py:307 -#: src/subscription_manager/utils.py:309 src/subscription_manager/utils.py:328 +#: src/subscription_manager/utils.py:273 src/subscription_manager/utils.py:289 +#: src/subscription_manager/utils.py:291 src/subscription_manager/utils.py:311 +#: src/subscription_manager/utils.py:313 src/subscription_manager/utils.py:332 msgid "Unknown" msgstr "" -#: src/rhsmlib/services/entitlement.py:124 src/rhsmlib/services/register.py:61 +#: src/rhsmlib/services/entitlement.py:124 src/rhsmlib/services/register.py:69 #, python-format msgid "Unknown arguments: %s" msgstr "" #: src/rhsmlib/services/entitlement.py:289 -#: src/subscription_manager/cli_command/list.py:428 msgid "Virtual" msgstr "" #: src/rhsmlib/services/entitlement.py:291 -#: src/subscription_manager/cli_command/list.py:430 msgid "Physical" msgstr "" #: src/rhsmlib/services/entitlement.py:294 -#: src/subscription_manager/cli_command/list.py:433 msgid "Yes" msgstr "" #: src/rhsmlib/services/entitlement.py:296 -#: src/subscription_manager/cli_command/list.py:435 msgid "No" msgstr "" @@ -1056,32 +1074,26 @@ msgid "" msgstr "" #: src/rhsmlib/services/entitlement.py:497 -#: src/subscription_manager/cli_command/list.py:292 msgid "Error: --all is only applicable with --available" msgstr "" #: src/rhsmlib/services/entitlement.py:499 -#: src/subscription_manager/cli_command/list.py:294 msgid "Error: --ondate is only applicable with --available" msgstr "" #: src/rhsmlib/services/entitlement.py:504 -#: src/subscription_manager/cli_command/list.py:297 msgid "Error: --servicelevel is only applicable with --available or --consumed" msgstr "" #: src/rhsmlib/services/entitlement.py:508 -#: src/subscription_manager/cli_command/list.py:302 msgid "Error: --match-installed is only applicable with --available" msgstr "" #: src/rhsmlib/services/entitlement.py:511 -#: src/subscription_manager/cli_command/list.py:304 msgid "Error: --no-overlap is only applicable with --available" msgstr "" #: src/rhsmlib/services/entitlement.py:516 -#: src/subscription_manager/cli_command/list.py:307 msgid "" "Error: --pool-only is only applicable with --available and/or --consumed" msgstr "" @@ -1090,44 +1102,61 @@ msgstr "" msgid "Error: this system is not registered" msgstr "" -#: src/rhsmlib/services/register.py:192 +#: src/rhsmlib/services/register.py:73 +msgid "Environment IDs and environment names are mutually exclusive" +msgstr "" + +#: src/rhsmlib/services/register.py:174 +#, python-brace-format +msgid "" +"Environment: '{env_names}' does not have required type '{environment_type}'" +msgstr "" + +#: src/rhsmlib/services/register.py:182 +#, python-brace-format +msgid "" +"Environments: '{env_names}' do not have required type '{environment_type}'" +msgstr "" + +#: src/rhsmlib/services/register.py:202 +msgid "" +"Registration is only possible when the organization is in Simple Content " +"Access (SCA) mode." +msgstr "" + +#: src/rhsmlib/services/register.py:243 msgid "This system is already registered. Add force to options to override." msgstr "" -#: src/rhsmlib/services/register.py:195 -#: src/subscription_manager/cli_command/register.py:142 +#: src/rhsmlib/services/register.py:246 +#: src/subscription_manager/cli_command/register.py:121 msgid "Error: system name can not be empty." msgstr "" -#: src/rhsmlib/services/register.py:199 -#: src/subscription_manager/cli_command/register.py:164 +#: src/rhsmlib/services/register.py:250 +#: src/subscription_manager/cli_command/register.py:139 msgid "" "Error: Can not force registration while attempting to recover registration " "with consumerid. Please use --force without --consumerid to re-register or " "use the clean command and try again without --force." msgstr "" -#: src/rhsmlib/services/register.py:209 -#: src/subscription_manager/cli_command/register.py:155 +#: src/rhsmlib/services/register.py:260 +#: src/subscription_manager/cli_command/register.py:132 msgid "Error: Must specify an activation key" msgstr "" -#: src/rhsmlib/services/register.py:211 -#: src/subscription_manager/cli_command/register.py:144 +#: src/rhsmlib/services/register.py:262 +#: src/subscription_manager/cli_command/register.py:123 msgid "Error: Activation keys do not require user credentials." msgstr "" -#: src/rhsmlib/services/register.py:214 -#: src/subscription_manager/cli_command/register.py:147 +#: src/rhsmlib/services/register.py:265 +#: src/subscription_manager/cli_command/register.py:126 msgid "Error: Activation keys can not be used with previously registered IDs." msgstr "" -#: src/rhsmlib/services/register.py:218 -#: src/subscription_manager/cli_command/register.py:150 -msgid "Error: Activation keys do not allow environments to be specified." -msgstr "" - -#: src/rhsmlib/services/register.py:225 +#: src/rhsmlib/services/register.py:271 msgid "Error: Missing username or password." msgstr "" @@ -1154,39 +1183,39 @@ msgstr "" msgid "Disabled" msgstr "" -#: src/subscription_manager/branding/__init__.py:77 +#: src/subscription_manager/branding/__init__.py:76 msgid "Register the system to the server" msgstr "" -#: src/subscription_manager/branding/__init__.py:78 +#: src/subscription_manager/branding/__init__.py:77 msgid "Unregister the system from the server" msgstr "" -#: src/subscription_manager/branding/__init__.py:79 +#: src/subscription_manager/branding/__init__.py:78 msgid "This system is registered to spacewalk" msgstr "" -#: src/subscription_manager/branding/__init__.py:81 +#: src/subscription_manager/branding/__init__.py:80 #: src/subscription_manager/branding/redhat_branding.py:14 -#: src/subscription_manager/managercli.py:96 +#: src/subscription_manager/managercli.py:78 msgid "WARNING" msgstr "" -#: src/subscription_manager/branding/__init__.py:81 +#: src/subscription_manager/branding/__init__.py:80 msgid "You have already registered with spacewalk." msgstr "" -#: src/subscription_manager/branding/__init__.py:84 +#: src/subscription_manager/branding/__init__.py:83 msgid "Please enter your account information:" msgstr "" -#: src/subscription_manager/branding/__init__.py:86 +#: src/subscription_manager/branding/__init__.py:85 msgid "" "Contact your system administrator if you have forgotten your login or " "password" msgstr "" -#: src/subscription_manager/branding/__init__.py:88 +#: src/subscription_manager/branding/__init__.py:87 #: src/subscription_manager/branding/redhat_branding.py:29 msgid "Red Hat Subscription Management" msgstr "" @@ -1257,7 +1286,7 @@ msgid "" "forgot_password" msgstr "" -#: src/subscription_manager/cache.py:176 +#: src/subscription_manager/cache.py:178 #, python-brace-format msgid "" "consumer_uuid={consumer_uuid} is not a valid consumer_uuid. Not attempting " @@ -1350,29 +1379,25 @@ msgstr "" msgid "--add cannot be used with --remove" msgstr "" -#: src/subscription_manager/cli_command/abstract_syspurpose.py:151 -msgid "Error: you can specify --username or --token not both" -msgstr "" - -#: src/subscription_manager/cli_command/abstract_syspurpose.py:156 +#: src/subscription_manager/cli_command/abstract_syspurpose.py:152 #, python-brace-format msgid "" "Error: you must register or specify --username and --password to list {attr}" msgstr "" -#: src/subscription_manager/cli_command/abstract_syspurpose.py:173 +#: src/subscription_manager/cli_command/abstract_syspurpose.py:167 msgid "" -"Error: --username, --password, --token and --org can be used only on " -"unregistered systems" +"Error: --username, --password, and --org can be used only on unregistered " +"systems" msgstr "" #. TRANSLATORS: this is used to quote a string -#: src/subscription_manager/cli_command/abstract_syspurpose.py:251 +#: src/subscription_manager/cli_command/abstract_syspurpose.py:239 #, python-brace-format msgid "\"{value}\"" msgstr "" -#: src/subscription_manager/cli_command/abstract_syspurpose.py:255 +#: src/subscription_manager/cli_command/abstract_syspurpose.py:243 #, python-brace-format msgid "" "Warning: Provided value {vals} is not included in the list of valid values" @@ -1381,7 +1406,7 @@ msgid_plural "" msgstr[0] "" msgstr[1] "" -#: src/subscription_manager/cli_command/abstract_syspurpose.py:264 +#: src/subscription_manager/cli_command/abstract_syspurpose.py:252 #, python-brace-format msgid "" "Warning: This organization does not have any subscriptions that provide a " @@ -1389,297 +1414,143 @@ msgid "" "subscriptions." msgstr "" -#: src/subscription_manager/cli_command/abstract_syspurpose.py:278 +#: src/subscription_manager/cli_command/abstract_syspurpose.py:266 #, python-brace-format msgid "{attr} set to \"{val}\"." msgstr "" -#: src/subscription_manager/cli_command/abstract_syspurpose.py:296 +#: src/subscription_manager/cli_command/abstract_syspurpose.py:284 #, python-brace-format msgid "{attr} unset." msgstr "" -#: src/subscription_manager/cli_command/abstract_syspurpose.py:313 -#: src/subscription_manager/cli_command/abstract_syspurpose.py:338 +#: src/subscription_manager/cli_command/abstract_syspurpose.py:301 +#: src/subscription_manager/cli_command/abstract_syspurpose.py:326 #, python-brace-format msgid "{attr} updated." msgstr "" -#: src/subscription_manager/cli_command/abstract_syspurpose.py:369 +#: src/subscription_manager/cli_command/abstract_syspurpose.py:357 #, python-brace-format msgid "Current {name}: {val}" msgstr "" -#: src/subscription_manager/cli_command/abstract_syspurpose.py:371 +#: src/subscription_manager/cli_command/abstract_syspurpose.py:359 #, python-brace-format msgid "{name} not set." msgstr "" -#: src/subscription_manager/cli_command/abstract_syspurpose.py:389 +#: src/subscription_manager/cli_command/abstract_syspurpose.py:377 #, python-brace-format msgid "Available {syspurpose_attr}" msgstr "" -#: src/subscription_manager/cli_command/abstract_syspurpose.py:401 -#: src/subscription_manager/cli_command/service_level.py:213 +#: src/subscription_manager/cli_command/abstract_syspurpose.py:389 +#: src/subscription_manager/cli_command/service_level.py:210 #, python-brace-format msgid "" "There are no available values for the system purpose \"{syspurpose_attr}\" " "from the available subscriptions in this organization." msgstr "" -#: src/subscription_manager/cli_command/abstract_syspurpose.py:409 +#: src/subscription_manager/cli_command/abstract_syspurpose.py:397 #, python-brace-format msgid "" "Unable to get the list of valid values for the system purpose " "\"{syspurpose_attr}\"." msgstr "" -#: src/subscription_manager/cli_command/abstract_syspurpose.py:435 -msgid "Unable to connect to server using token" -msgstr "" - -#: src/subscription_manager/cli_command/abstract_syspurpose.py:483 +#: src/subscription_manager/cli_command/abstract_syspurpose.py:461 #: src/subscription_manager/i18n_argparse.py:35 #, python-format msgid "%(prog)s [OPTIONS]" msgstr "" -#: src/subscription_manager/cli_command/abstract_syspurpose.py:492 +#: src/subscription_manager/cli_command/abstract_syspurpose.py:470 #, python-brace-format msgid "" "Note: The currently configured entitlement server does not support System " "Purpose {attr}." msgstr "" -#: src/subscription_manager/cli_command/addons.py:24 -msgid "Show or modify the system purpose addons setting" -msgstr "" - -#: src/subscription_manager/cli_command/attach.py:51 -msgid "The ID of the pool to attach (can be specified more than once)" -msgstr "" - -#: src/subscription_manager/cli_command/attach.py:57 -msgid "Number of subscriptions to attach. May not be used with an auto-attach." -msgstr "" - -#: src/subscription_manager/cli_command/attach.py:63 -msgid "" -"Automatically attach the best-matched compatible subscriptions to this " -"system. This is the default action." -msgstr "" - -#: src/subscription_manager/cli_command/attach.py:71 -msgid "" -"Automatically attach only subscriptions matching the specified service " -"level; only used with --auto" -msgstr "" - -#: src/subscription_manager/cli_command/attach.py:79 -msgid "" -"A file from which to read pool IDs. If a hyphen is provided, pool IDs will " -"be read from stdin." -msgstr "" - -#: src/subscription_manager/cli_command/attach.py:85 -msgid "All installed products are covered by valid entitlements." -msgstr "" - -#: src/subscription_manager/cli_command/attach.py:86 -msgid "No need to update subscriptions at this time." -msgstr "" - -#: src/subscription_manager/cli_command/attach.py:98 -msgid "" -"Attach a specified subscription to the registered system, when system does " -"not use Simple Content Access mode" -msgstr "" - -#: src/subscription_manager/cli_command/attach.py:111 -msgid "Error: --auto may not be used when specifying pools." -msgstr "" - -#: src/subscription_manager/cli_command/attach.py:114 -msgid "Error: The --servicelevel option cannot be used when specifying pools." -msgstr "" - -#: src/subscription_manager/cli_command/attach.py:120 -msgid "Error: Quantity must be a positive integer." -msgstr "" - -#: src/subscription_manager/cli_command/attach.py:122 -msgid "Error: --quantity may not be used with an auto-attach" -msgstr "" - -#: src/subscription_manager/cli_command/attach.py:132 -msgid "Error: Received data does not contain any pool IDs." -msgstr "" - -#: src/subscription_manager/cli_command/attach.py:136 -#, python-brace-format -msgid "Error: The file \"{file}\" does not contain any pool IDs." -msgstr "" - -#: src/subscription_manager/cli_command/attach.py:143 -#, python-brace-format -msgid "Error: The file \"{file}\" does not exist or cannot be read." -msgstr "" - -#: src/subscription_manager/cli_command/attach.py:157 -#, python-brace-format -msgid "" -"Ignoring the request to attach. Attaching subscriptions is disabled for " -"organization \"{owner_id}\" because Simple Content Access (SCA) is enabled." -msgstr "" - -#: src/subscription_manager/cli_command/attach.py:201 -msgid "Please enter a valid numeric pool ID." -msgstr "" - -#: src/subscription_manager/cli_command/attach.py:209 -#, python-brace-format -msgid "Successfully attached a subscription for: {name}" -msgstr "" - -#: src/subscription_manager/cli_command/attach.py:239 -msgid "No Installed products on system. No need to attach subscriptions." -msgstr "" - -#: src/subscription_manager/cli_command/attach.py:243 -msgid "" -"All installed products are covered by valid entitlements. No need to update " -"subscriptions at this time." -msgstr "" - -#: src/subscription_manager/cli_command/attach.py:257 -#: src/subscription_manager/cli_command/register.py:201 -msgid "" -"Error: The --servicelevel option is not supported by the server. Did not " -"complete your request." -msgstr "" - -#: src/subscription_manager/cli_command/attach.py:271 -msgid "Service level set to: {}" -msgstr "" - -#: src/subscription_manager/cli_command/attach.py:280 -msgid "Entitlement Certificate(s) update failed due to the following reasons:" -msgstr "" - -#: src/subscription_manager/cli_command/autoheal.py:27 -msgid "Set if subscriptions are attached on a schedule (default of daily)" -msgstr "" - -#: src/subscription_manager/cli_command/autoheal.py:28 -msgid "specify whether to enable or disable auto-attaching of subscriptions" -msgstr "" - -#: src/subscription_manager/cli_command/autoheal.py:35 -msgid "try to attach subscriptions for uncovered products each check-in" -msgstr "" - -#: src/subscription_manager/cli_command/autoheal.py:41 -msgid "do not try to automatically attach subscriptions each check-in" -msgstr "" - -#: src/subscription_manager/cli_command/autoheal.py:47 -msgid "show the current auto-attach preference" -msgstr "" - -#: src/subscription_manager/cli_command/autoheal.py:60 -msgid "Auto-attach preference: enabled" -msgstr "" - -#: src/subscription_manager/cli_command/autoheal.py:62 -msgid "Auto-attach preference: disabled" -msgstr "" - #: src/subscription_manager/cli_command/clean.py:25 msgid "" "Remove all local system and subscription data without affecting the server" msgstr "" #: src/subscription_manager/cli_command/clean.py:31 -#: src/subscription_manager/cli_command/register.py:312 +#: src/subscription_manager/cli_command/register.py:256 msgid "All local data removed" msgstr "" -#: src/subscription_manager/cli_command/cli.py:131 -#, python-brace-format -msgid "" -"Ignoring the request to auto-attach. Attaching subscriptions is disabled for " -"organization \"{owner_id}\" because Simple Content Access (SCA) is enabled." -msgstr "" - -#: src/subscription_manager/cli_command/cli.py:152 +#: src/subscription_manager/cli_command/cli.py:132 msgid "server URL in the form of https://hostname:port/prefix" msgstr "" -#: src/subscription_manager/cli_command/cli.py:159 +#: src/subscription_manager/cli_command/cli.py:139 msgid "" "do not check the entitlement server SSL certificate against available " "certificate authorities" msgstr "" -#: src/subscription_manager/cli_command/cli.py:170 +#: src/subscription_manager/cli_command/cli.py:150 msgid "proxy URL in the form of hostname:port" msgstr "" -#: src/subscription_manager/cli_command/cli.py:176 +#: src/subscription_manager/cli_command/cli.py:156 msgid "user for HTTP proxy with basic authentication" msgstr "" -#: src/subscription_manager/cli_command/cli.py:182 +#: src/subscription_manager/cli_command/cli.py:162 msgid "password for HTTP proxy with basic authentication" msgstr "" -#: src/subscription_manager/cli_command/cli.py:188 +#: src/subscription_manager/cli_command/cli.py:168 msgid "host suffixes that should bypass HTTP proxy" msgstr "" -#: src/subscription_manager/cli_command/cli.py:197 +#: src/subscription_manager/cli_command/cli.py:177 msgid "Do not display progress messages" msgstr "" -#: src/subscription_manager/cli_command/cli.py:209 +#: src/subscription_manager/cli_command/cli.py:189 msgid "" "Consumer identity either does not exist or is corrupted. Try register --help" msgstr "" -#: src/subscription_manager/cli_command/cli.py:267 +#: src/subscription_manager/cli_command/cli.py:247 msgid "" "subscription-manager is operating in container mode. Use your host system to " "manage subscriptions.\n" msgstr "" -#: src/subscription_manager/cli_command/cli.py:282 +#: src/subscription_manager/cli_command/cli.py:262 #, python-brace-format msgid "{prog}: error: no such option: {args}" msgstr "" -#: src/subscription_manager/cli_command/cli.py:301 +#: src/subscription_manager/cli_command/cli.py:281 msgid "Error parsing serverurl:" msgstr "" -#: src/subscription_manager/cli_command/cli.py:317 +#: src/subscription_manager/cli_command/cli.py:297 msgid "Error parsing baseurl:" msgstr "" -#: src/subscription_manager/cli_command/cli.py:390 +#: src/subscription_manager/cli_command/cli.py:369 #, python-brace-format msgid "Unable to reach the server at {host}:{port}{handler}" msgstr "" -#: src/subscription_manager/cli_command/cli.py:400 +#: src/subscription_manager/cli_command/cli.py:379 msgid "Error: CA certificate for subscription service has not been installed." msgstr "" -#: src/subscription_manager/cli_command/cli.py:422 +#: src/subscription_manager/cli_command/cli.py:401 msgid "System certificates corrupted. Please reregister." msgstr "" -#: src/subscription_manager/cli_command/cli.py:433 +#: src/subscription_manager/cli_command/cli.py:412 #, python-brace-format msgid "" "Consumer profile \"{uuid}\" has been deleted from the server. You can use " @@ -1740,72 +1611,81 @@ msgstr "" msgid "Section {section} and name {name} cannot be removed." msgstr "" -#: src/subscription_manager/cli_command/environments.py:44 +#: src/subscription_manager/cli_command/environments.py:42 +#: src/subscription_manager/cli_command/owners.py:31 +msgid "Name:" +msgstr "" + +#: src/subscription_manager/cli_command/environments.py:43 +msgid "Description:" +msgstr "" + +#: src/subscription_manager/cli_command/environments.py:49 msgid "Display the environments available for a user" msgstr "" -#: src/subscription_manager/cli_command/environments.py:45 +#: src/subscription_manager/cli_command/environments.py:50 msgid "specify organization for environment list, using organization key" msgstr "" -#: src/subscription_manager/cli_command/environments.py:52 +#: src/subscription_manager/cli_command/environments.py:57 msgid "set an ordered comma-separated list of environments for this consumer" msgstr "" -#: src/subscription_manager/cli_command/environments.py:58 +#: src/subscription_manager/cli_command/environments.py:63 msgid "list all environments for the organization" msgstr "" -#: src/subscription_manager/cli_command/environments.py:65 +#: src/subscription_manager/cli_command/environments.py:70 msgid "list the environments enabled for this consumer in order" msgstr "" -#: src/subscription_manager/cli_command/environments.py:72 +#: src/subscription_manager/cli_command/environments.py:77 msgid "list the environments not enabled for this consumer" msgstr "" -#: src/subscription_manager/cli_command/environments.py:81 +#: src/subscription_manager/cli_command/environments.py:86 msgid "You may not specify an --org for environments when registered." msgstr "" -#: src/subscription_manager/cli_command/environments.py:90 -#: src/subscription_manager/cli_command/register.py:460 +#: src/subscription_manager/cli_command/environments.py:95 +#: src/subscription_manager/cli_command/register.py:387 msgid "Error: Server does not support environments." msgstr "" -#: src/subscription_manager/cli_command/environments.py:97 +#: src/subscription_manager/cli_command/environments.py:99 msgid "This operation requires user credentials" msgstr "" -#: src/subscription_manager/cli_command/environments.py:111 +#: src/subscription_manager/cli_command/environments.py:113 msgid "Error: Unable to retrieve environment list from server" msgstr "" -#: src/subscription_manager/cli_command/environments.py:126 +#: src/subscription_manager/cli_command/environments.py:128 msgid "Environments updated." msgstr "" -#: src/subscription_manager/cli_command/environments.py:128 +#: src/subscription_manager/cli_command/environments.py:130 msgid "Error: Server does not support environment updates." msgstr "" -#: src/subscription_manager/cli_command/environments.py:136 +#: src/subscription_manager/cli_command/environments.py:138 msgid "Error: Server does not support multi-environment operations." msgstr "" -#: src/subscription_manager/cli_command/environments.py:154 +#: src/subscription_manager/cli_command/environments.py:156 msgid "Environments" msgstr "" -#: src/subscription_manager/cli_command/environments.py:167 +#: src/subscription_manager/cli_command/environments.py:169 msgid "This list operation does not have any environments to report." msgstr "" -#: src/subscription_manager/cli_command/environments.py:203 +#: src/subscription_manager/cli_command/environments.py:205 msgid "Error: The same environment may not be listed more than once. " msgstr "" -#: src/subscription_manager/cli_command/environments.py:209 +#: src/subscription_manager/cli_command/environments.py:211 #, python-brace-format msgid "No such environment: {names}" msgid_plural "No such environments: {names}" @@ -1850,479 +1730,108 @@ msgstr "" msgid "--username and --password can only be used with --force" msgstr "" -#: src/subscription_manager/cli_command/identity.py:65 -msgid "--token can only be used with --force" -msgstr "" - -#: src/subscription_manager/cli_command/identity.py:74 -#: src/subscription_manager/cli_command/identity.py:77 +#: src/subscription_manager/cli_command/identity.py:72 +#: src/subscription_manager/cli_command/identity.py:75 #: src/subscription_manager/cli_command/version.py:33 #, python-brace-format msgid "server type: {type}" msgstr "" -#: src/subscription_manager/cli_command/identity.py:89 +#: src/subscription_manager/cli_command/identity.py:87 #, python-brace-format msgid "system identity: {consumerid}" msgstr "" -#: src/subscription_manager/cli_command/identity.py:90 +#: src/subscription_manager/cli_command/identity.py:88 #, python-brace-format msgid "name: {consumer_name}" msgstr "" -#: src/subscription_manager/cli_command/identity.py:91 +#: src/subscription_manager/cli_command/identity.py:89 #, python-brace-format msgid "org name: {ownername}" msgstr "" -#: src/subscription_manager/cli_command/identity.py:92 +#: src/subscription_manager/cli_command/identity.py:90 #, python-brace-format msgid "org ID: {ownerid}" msgstr "" -#: src/subscription_manager/cli_command/identity.py:107 +#: src/subscription_manager/cli_command/identity.py:105 #: src/subscription_manager/printing_utils.py:165 #: src/subscription_manager/printing_utils.py:184 msgid "None" msgstr "" -#: src/subscription_manager/cli_command/identity.py:110 +#: src/subscription_manager/cli_command/identity.py:108 #, python-brace-format msgid "environment name: {environment_name}" msgid_plural "environment names: {environment_name}" msgstr[0] "" msgstr[1] "" -#: src/subscription_manager/cli_command/identity.py:130 +#: src/subscription_manager/cli_command/identity.py:125 msgid "Identity certificate has been regenerated." msgstr "" -#: src/subscription_manager/cli_command/identity.py:143 +#: src/subscription_manager/cli_command/identity.py:138 msgid "Error: Unable to generate a new identity for the system" msgstr "" -#: src/subscription_manager/cli_command/import_cert.py:30 -msgid "Import certificates which were provided outside of the tool" -msgstr "" - -#: src/subscription_manager/cli_command/import_cert.py:37 -msgid "certificate file to import (can be specified more than once)" -msgstr "" - -#: src/subscription_manager/cli_command/import_cert.py:45 -msgid "" -"Error: You may not import certificates into a system that is registered to a " -"subscription management service." -msgstr "" - -#: src/subscription_manager/cli_command/import_cert.py:52 -msgid "" -"Error: This command requires that you specify a certificate with --" -"certificate." -msgstr "" - -#: src/subscription_manager/cli_command/import_cert.py:69 -#, python-brace-format -msgid "Successfully imported certificate {file}" -msgstr "" - -#: src/subscription_manager/cli_command/import_cert.py:81 -#, python-brace-format -msgid "{file} is not a valid certificate file. Please use a valid certificate." -msgstr "" - -#: src/subscription_manager/cli_command/import_cert.py:90 -msgid "" -"An error occurred while importing the certificate. Please check log file for " -"more information." -msgstr "" - -#: src/subscription_manager/cli_command/import_cert.py:96 -#, python-brace-format -msgid "{file}: file not found." -msgstr "" - -#: src/subscription_manager/cli_command/list.py:49 -msgid "Future Subscription" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:50 -msgid "Subscribed" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:51 -msgid "Not Subscribed" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:52 -msgid "Expired" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:53 -msgid "Partially Subscribed" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:58 -#: src/subscription_manager/cli_command/list.py:69 -#: src/subscription_manager/cli_command/list.py:111 +#: src/subscription_manager/cli_command/list.py:29 msgid "Product Name:" msgstr "" -#: src/subscription_manager/cli_command/list.py:59 -#: src/subscription_manager/cli_command/list.py:70 +#: src/subscription_manager/cli_command/list.py:30 msgid "Product ID:" msgstr "" -#: src/subscription_manager/cli_command/list.py:60 -#: src/subscription_manager/cli_command/list.py:71 +#: src/subscription_manager/cli_command/list.py:31 msgid "Version:" msgstr "" -#: src/subscription_manager/cli_command/list.py:61 -#: src/subscription_manager/cli_command/list.py:72 +#: src/subscription_manager/cli_command/list.py:32 msgid "Arch:" msgstr "" -#: src/subscription_manager/cli_command/list.py:62 -#: src/subscription_manager/cli_command/list.py:112 -msgid "Status:" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:63 -#: src/subscription_manager/cli_command/list.py:138 -#: src/subscription_manager/cli_command/list.py:161 -msgid "Status Details:" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:64 -#: src/subscription_manager/cli_command/list.py:90 -#: src/subscription_manager/cli_command/list.py:140 -#: src/subscription_manager/cli_command/list.py:163 -msgid "Starts:" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:65 -#: src/subscription_manager/cli_command/list.py:91 -#: src/subscription_manager/cli_command/list.py:141 -#: src/subscription_manager/cli_command/list.py:164 -msgid "Ends:" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:76 -#: src/subscription_manager/cli_command/list.py:96 -#: src/subscription_manager/cli_command/list.py:126 -#: src/subscription_manager/cli_command/list.py:146 -msgid "Subscription Name:" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:77 -#: src/subscription_manager/cli_command/list.py:97 -#: src/subscription_manager/cli_command/list.py:127 -#: src/subscription_manager/cli_command/list.py:147 -msgid "Provides:" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:78 -#: src/subscription_manager/cli_command/list.py:98 -#: src/subscription_manager/cli_command/list.py:128 -#: src/subscription_manager/cli_command/list.py:148 -msgid "SKU:" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:79 -#: src/subscription_manager/cli_command/list.py:99 -#: src/subscription_manager/cli_command/list.py:129 -#: src/subscription_manager/cli_command/list.py:149 -msgid "Contract:" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:80 -#: src/subscription_manager/cli_command/list.py:132 -#: src/subscription_manager/cli_command/list.py:152 -msgid "Pool ID:" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:81 -#: src/subscription_manager/cli_command/list.py:133 -#: src/subscription_manager/cli_command/list.py:153 -msgid "Provides Management:" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:82 -msgid "Available:" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:83 -msgid "Suggested:" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:84 -#: src/subscription_manager/cli_command/list.py:136 -#: src/subscription_manager/cli_command/list.py:156 -msgid "Service Type:" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:85 -#: src/subscription_manager/cli_command/list.py:157 -msgid "Roles:" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:86 -#: src/subscription_manager/cli_command/list.py:100 -#: src/subscription_manager/cli_command/list.py:137 -#: src/subscription_manager/cli_command/list.py:158 -msgid "Service Level:" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:87 -#: src/subscription_manager/cli_command/list.py:159 -msgid "Usage:" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:88 -#: src/subscription_manager/cli_command/list.py:160 -msgid "Add-ons:" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:89 -#: src/subscription_manager/cli_command/list.py:139 -#: src/subscription_manager/cli_command/list.py:162 -msgid "Subscription Type:" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:92 -#: src/subscription_manager/cli_command/list.py:165 -msgid "Entitlement Type:" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:104 -msgid "Repo ID:" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:105 -msgid "Repo Name:" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:106 -msgid "Repo URL:" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:107 -msgid "Enabled:" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:116 -#: src/subscription_manager/cli_command/list.py:121 -msgid "Name:" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:117 -msgid "Description:" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:122 -msgid "Key:" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:130 -#: src/subscription_manager/cli_command/list.py:150 -msgid "Account:" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:131 -#: src/subscription_manager/cli_command/list.py:151 -msgid "Serial:" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:134 -#: src/subscription_manager/cli_command/list.py:154 -msgid "Active:" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:135 -#: src/subscription_manager/cli_command/list.py:155 -msgid "Quantity Used:" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:142 -msgid "System Type:" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:187 -msgid "No products installed." -msgstr "" - -#: src/subscription_manager/cli_command/list.py:191 -msgid "Installed Product Current Status:" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:202 -msgid "Unable to find available subscriptions for all your installed products." -msgstr "" - -#: src/subscription_manager/cli_command/list.py:208 +#: src/subscription_manager/cli_command/list.py:41 msgid "List subscription and product information for this system" msgstr "" -#: src/subscription_manager/cli_command/list.py:215 +#: src/subscription_manager/cli_command/list.py:46 msgid "list shows those products which are installed (default)" msgstr "" -#: src/subscription_manager/cli_command/list.py:220 -msgid "show those subscriptions which are available" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:225 -msgid "used with --available to ensure all subscriptions are returned" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:231 -#, python-brace-format -msgid "" -"date to search on, defaults to today's date, only used with --available " -"(example: {example})" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:237 -msgid "show the subscriptions being consumed by this system" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:243 -msgid "" -"shows only subscriptions matching the specified service level; only used " -"with --available and --consumed" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:251 -msgid "" -"shows pools which provide products that are not already covered; only used " -"with --available" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:259 -msgid "" -"shows only subscriptions matching products that are currently installed; " -"only used with --available" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:267 -msgid "" -"lists only subscriptions or products containing the specified expression in " -"the subscription or product information, varying with the list requested and " -"the server version (case-insensitive)." -msgstr "" - -#: src/subscription_manager/cli_command/list.py:277 -msgid "" -"lists only the pool IDs for applicable available or consumed subscriptions; " -"only used with --available and --consumed" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:285 -#, python-brace-format -msgid "" -"show pools that are active on or after the given date; only used with --" -"available (example: {example})" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:310 -msgid "Error: --afterdate is only applicable with --available" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:312 -msgid "Error: --afterdate cannot be used with --ondate" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:327 -#, python-brace-format +#: src/subscription_manager/cli_command/list.py:51 msgid "" -"Date entered is invalid. Date should be in YYYY-MM-DD format (example: " -"{dateexample})" +"lists only products containing the specified expression in the product " +"information." msgstr "" -#: src/subscription_manager/cli_command/list.py:355 +#: src/subscription_manager/cli_command/list.py:64 msgid " Installed Product Status" msgstr "" -#: src/subscription_manager/cli_command/list.py:391 +#: src/subscription_manager/cli_command/list.py:82 #, python-brace-format msgid "No installed products were found matching the expression \"{filter}\"." msgstr "" -#: src/subscription_manager/cli_command/list.py:396 +#: src/subscription_manager/cli_command/list.py:87 msgid "No installed products to list" msgstr "" -#: src/subscription_manager/cli_command/list.py:423 -msgid "Available Subscriptions" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:471 -#, python-brace-format -msgid "" -"No available subscription pools were found matching the expression " -"\"{filter}\" and the service level \"{level}\"." -msgstr "" - -#: src/subscription_manager/cli_command/list.py:478 -#, python-brace-format -msgid "" -"No available subscription pools were found matching the expression " -"\"{filter}\"." -msgstr "" - -#: src/subscription_manager/cli_command/list.py:484 -#, python-brace-format -msgid "" -"No available subscription pools were found matching the service level " -"\"{level}\"." -msgstr "" - -#: src/subscription_manager/cli_command/list.py:488 -msgid "No available subscription pools to list" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:510 -msgid "Consumed Subscriptions" -msgstr "" - -#: src/subscription_manager/cli_command/list.py:535 -#, python-brace-format -msgid "" -"No consumed subscription pools were found matching the expression " -"\"{filter}\" and the service level \"{level}\"." -msgstr "" - -#: src/subscription_manager/cli_command/list.py:541 -#, python-brace-format -msgid "" -"No consumed subscription pools were found matching the expression " -"\"{filter}\"." -msgstr "" - -#: src/subscription_manager/cli_command/list.py:548 -#, python-brace-format -msgid "" -"No consumed subscription pools were found matching the service level " -"\"{level}\"." -msgstr "" - -#: src/subscription_manager/cli_command/list.py:552 -msgid "No consumed subscription pools were found." -msgstr "" - #: src/subscription_manager/cli_command/org.py:35 msgid "specify an organization" msgstr "" #: src/subscription_manager/cli_command/org.py:46 -#: src/subscription_manager/cli_command/register.py:522 +#: src/subscription_manager/cli_command/register.py:449 msgid "Error: --org is a required parameter in non-interactive mode." msgstr "" #: src/subscription_manager/cli_command/org.py:48 -#: src/subscription_manager/cli_command/register.py:527 +#: src/subscription_manager/cli_command/register.py:454 msgid "Organization: " msgstr "" @@ -2332,7 +1841,7 @@ msgid "Error: User {username} is not member of any organization." msgstr "" #: src/subscription_manager/cli_command/org.py:74 -#: src/subscription_manager/cli_command/register.py:513 +#: src/subscription_manager/cli_command/register.py:440 #, python-brace-format msgid "Hint: User \"{name}\" is member of following organizations: {orgs}" msgstr "" @@ -2399,7 +1908,7 @@ msgid "Error: The 'repo-override' command is not supported by the server." msgstr "" #: src/subscription_manager/cli_command/override.py:138 -#: src/subscription_manager/cli_command/repos.py:124 +#: src/subscription_manager/cli_command/repos.py:130 msgid "Repositories disabled by configuration." msgstr "" @@ -2424,20 +1933,24 @@ msgstr "" msgid "Repository: {repo}" msgstr "" -#: src/subscription_manager/cli_command/owners.py:34 +#: src/subscription_manager/cli_command/owners.py:32 +msgid "Key:" +msgstr "" + +#: src/subscription_manager/cli_command/owners.py:38 msgid "Display the organizations against which a user can register a system" msgstr "" -#: src/subscription_manager/cli_command/owners.py:52 +#: src/subscription_manager/cli_command/owners.py:53 msgid "Organizations" msgstr "" -#: src/subscription_manager/cli_command/owners.py:61 +#: src/subscription_manager/cli_command/owners.py:62 #, python-brace-format msgid "{username} cannot register with any organizations." msgstr "" -#: src/subscription_manager/cli_command/owners.py:69 +#: src/subscription_manager/cli_command/owners.py:70 msgid "Error: Unable to retrieve org list from server" msgstr "" @@ -2472,29 +1985,6 @@ msgstr "" msgid "enabled" msgstr "" -#: src/subscription_manager/cli_command/redeem.py:29 -msgid "Attempt to redeem a subscription for a preconfigured system" -msgstr "" - -#: src/subscription_manager/cli_command/redeem.py:36 -msgid "email address to notify when subscription redemption is complete" -msgstr "" - -#: src/subscription_manager/cli_command/redeem.py:43 -msgid "" -"optional language to use for email notification when subscription redemption " -"is complete (Examples: en-us, de-de)" -msgstr "" - -#: src/subscription_manager/cli_command/redeem.py:52 -msgid "" -"Error: This command requires that you specify an email address with --email." -msgstr "" - -#: src/subscription_manager/cli_command/redeem.py:74 -msgid "Error: Unable to redeem subscription for this system." -msgstr "" - #: src/subscription_manager/cli_command/refresh.py:32 msgid "Pull the latest subscription data from the server" msgstr "" @@ -2512,142 +2002,120 @@ msgstr "" msgid "All local data refreshed" msgstr "" -#: src/subscription_manager/cli_command/register.py:68 +#: src/subscription_manager/cli_command/register.py:64 msgid "base URL for content in form of https://hostname:port/prefix" msgstr "" -#: src/subscription_manager/cli_command/register.py:81 +#: src/subscription_manager/cli_command/register.py:77 msgid "name of the system to register, defaults to the hostname" msgstr "" -#: src/subscription_manager/cli_command/register.py:87 +#: src/subscription_manager/cli_command/register.py:83 msgid "the existing system data is pulled from the server" msgstr "" -#: src/subscription_manager/cli_command/register.py:93 +#: src/subscription_manager/cli_command/register.py:89 msgid "" "register with one of multiple organizations for the user, using organization " "key" msgstr "" -#: src/subscription_manager/cli_command/register.py:99 +#: src/subscription_manager/cli_command/register.py:95 msgid "" "register with a specific environment (single value) or multiple environments " "(a comma-separated list) in the destination org. The ability to use multiple " "environments is controlled by the entitlement server" msgstr "" -#: src/subscription_manager/cli_command/register.py:107 +#: src/subscription_manager/cli_command/register.py:103 msgid "set a release version" msgstr "" -#: src/subscription_manager/cli_command/register.py:112 -msgid "Deprecated, see --auto-attach" -msgstr "" - -#: src/subscription_manager/cli_command/register.py:118 -msgid "automatically attach compatible subscriptions to this system" -msgstr "" - -#: src/subscription_manager/cli_command/register.py:123 +#: src/subscription_manager/cli_command/register.py:108 msgid "" "include an implicit attempt to unregister before registering a new system " "identity" msgstr "" -#: src/subscription_manager/cli_command/register.py:129 +#: src/subscription_manager/cli_command/register.py:114 msgid "" "activation key to use for registration (can be specified more than once)" msgstr "" -#: src/subscription_manager/cli_command/register.py:134 -msgid "" -"system preference used when subscribing automatically, requires --auto-attach" -msgstr "" - -#: src/subscription_manager/cli_command/register.py:140 +#: src/subscription_manager/cli_command/register.py:119 msgid "This system is already registered. Use --force to override" msgstr "" -#: src/subscription_manager/cli_command/register.py:152 -msgid "Error: Activation keys cannot be used with --auto-attach." -msgstr "" - -#: src/subscription_manager/cli_command/register.py:157 -msgid "Error: Must use --auto-attach with --servicelevel." +#: src/subscription_manager/cli_command/register.py:129 +msgid "Error: Activation keys do not allow environments to be specified." msgstr "" -#: src/subscription_manager/cli_command/register.py:159 +#: src/subscription_manager/cli_command/register.py:134 msgid "Error: Must provide --org with activation keys." msgstr "" -#: src/subscription_manager/cli_command/register.py:173 +#: src/subscription_manager/cli_command/register.py:148 msgid "Error: The --type option has been deprecated and may not be used." msgstr "" -#: src/subscription_manager/cli_command/register.py:176 -#: src/subscription_manager/cli_command/register.py:491 +#: src/subscription_manager/cli_command/register.py:151 +#: src/subscription_manager/cli_command/register.py:418 msgid "The entitlement server does not allow multiple environments" msgstr "" -#: src/subscription_manager/cli_command/register.py:220 +#: src/subscription_manager/cli_command/register.py:164 msgid "Uploading DNF profile" msgstr "" -#: src/subscription_manager/cli_command/register.py:290 +#: src/subscription_manager/cli_command/register.py:234 #: src/subscription_manager/cli_command/unregister.py:41 #, python-brace-format msgid "Unregistering from: {hostname}:{port}{prefix}" msgstr "" -#: src/subscription_manager/cli_command/register.py:301 +#: src/subscription_manager/cli_command/register.py:245 #, python-brace-format msgid "The system with UUID {old_uuid} has been unregistered" msgstr "" -#: src/subscription_manager/cli_command/register.py:325 +#: src/subscription_manager/cli_command/register.py:267 #, python-brace-format msgid "Registering to: {hostname}:{port}{prefix}" msgstr "" -#: src/subscription_manager/cli_command/register.py:365 +#: src/subscription_manager/cli_command/register.py:306 #, python-brace-format msgid "Error during registration: {e}" msgstr "" -#: src/subscription_manager/cli_command/register.py:368 +#: src/subscription_manager/cli_command/register.py:309 #, python-brace-format msgid "The system has been registered with ID: {id}" msgstr "" -#: src/subscription_manager/cli_command/register.py:369 +#: src/subscription_manager/cli_command/register.py:310 #, python-brace-format msgid "The registered system name is: {name}" msgstr "" -#: src/subscription_manager/cli_command/register.py:371 -#, python-brace-format -msgid "Service level set to: {level}" -msgstr "" - -#: src/subscription_manager/cli_command/register.py:438 +#: src/subscription_manager/cli_command/register.py:365 msgid "Error: --environments is a required parameter in non-interactive mode." msgstr "" -#: src/subscription_manager/cli_command/register.py:442 +#: src/subscription_manager/cli_command/register.py:369 msgid "Environments: " msgstr "" -#: src/subscription_manager/cli_command/register.py:444 +#: src/subscription_manager/cli_command/register.py:371 msgid "Environment: " msgstr "" -#: src/subscription_manager/cli_command/register.py:485 +#: src/subscription_manager/cli_command/register.py:412 #, python-brace-format msgid "Hint: Organization \"{key}\" contains following environments: {list}" msgstr "" -#: src/subscription_manager/cli_command/register.py:501 +#: src/subscription_manager/cli_command/register.py:428 #, python-brace-format msgid "{name} cannot register with any organizations." msgstr "" @@ -2708,147 +2176,77 @@ msgstr "" msgid "Available Releases" msgstr "" -#: src/subscription_manager/cli_command/remove.py:41 -msgid "certificate serial number to remove (can be specified more than once)" -msgstr "" - -#: src/subscription_manager/cli_command/remove.py:48 -msgid "the ID of the pool to remove (can be specified more than once)" -msgstr "" - -#: src/subscription_manager/cli_command/remove.py:54 -msgid "remove all subscriptions from this system" -msgstr "" - -#: src/subscription_manager/cli_command/remove.py:58 -msgid "Remove all or specific subscriptions from this system" -msgstr "" - -#: src/subscription_manager/cli_command/remove.py:71 -#, python-brace-format -msgid "Error: '{serial}' is not a valid serial number" -msgstr "" - -#: src/subscription_manager/cli_command/remove.py:80 -msgid "" -"Error: The registered entitlement server does not support remove --pool.\n" -"Instead, use the remove --serial option." -msgstr "" - -#: src/subscription_manager/cli_command/remove.py:87 -msgid "" -"Error: This command requires that you specify one of --serial, --pool or --" -"all." -msgstr "" - -#: src/subscription_manager/cli_command/remove.py:93 -msgid "The entitlement server successfully removed these pools:" -msgstr "" - -#: src/subscription_manager/cli_command/remove.py:95 -msgid "The entitlement server successfully removed these serial numbers:" -msgstr "" - -#: src/subscription_manager/cli_command/remove.py:97 -msgid "The entitlement server successfully removed these IDs:" -msgstr "" - -#: src/subscription_manager/cli_command/remove.py:102 -msgid "The entitlement server failed to remove these pools:" -msgstr "" - -#: src/subscription_manager/cli_command/remove.py:104 -msgid "The entitlement server failed to remove these serial numbers:" -msgstr "" - -#: src/subscription_manager/cli_command/remove.py:106 -msgid "The entitlement server failed to remove these IDs:" +#: src/subscription_manager/cli_command/repos.py:34 +msgid "Repo ID:" msgstr "" -#: src/subscription_manager/cli_command/remove.py:124 -msgid "All subscriptions have been removed at the server." +#: src/subscription_manager/cli_command/repos.py:35 +msgid "Repo Name:" msgstr "" -#: src/subscription_manager/cli_command/remove.py:129 -#, python-format -msgid "%s subscription removed at the server." -msgid_plural "%s subscriptions removed at the server." -msgstr[0] "" -msgstr[1] "" - -#: src/subscription_manager/cli_command/remove.py:170 -#: src/subscription_manager/cli_command/remove.py:206 -#, python-brace-format -msgid "Unable to perform remove due to the following exception: {e}" +#: src/subscription_manager/cli_command/repos.py:36 +msgid "Repo URL:" msgstr "" -#: src/subscription_manager/cli_command/remove.py:182 -#, python-brace-format -msgid "{total} subscription removed from this system." -msgid_plural "{total} subscriptions removed from this system." -msgstr[0] "" -msgstr[1] "" - -#: src/subscription_manager/cli_command/remove.py:198 -#, python-brace-format -msgid "Subscription with serial number {serial} removed from this system" +#: src/subscription_manager/cli_command/repos.py:37 +msgid "Enabled:" msgstr "" -#: src/subscription_manager/cli_command/repos.py:60 +#: src/subscription_manager/cli_command/repos.py:66 msgid "List the repositories which this system is entitled to use" msgstr "" -#: src/subscription_manager/cli_command/repos.py:67 +#: src/subscription_manager/cli_command/repos.py:73 msgid "list all known repositories for this system" msgstr "" -#: src/subscription_manager/cli_command/repos.py:73 +#: src/subscription_manager/cli_command/repos.py:79 msgid "list known, enabled repositories for this system" msgstr "" -#: src/subscription_manager/cli_command/repos.py:79 +#: src/subscription_manager/cli_command/repos.py:85 msgid "list known, disabled repositories for this system" msgstr "" -#: src/subscription_manager/cli_command/repos.py:87 +#: src/subscription_manager/cli_command/repos.py:93 msgid "" "repository to enable (can be specified more than once). Wildcards (* and ?) " "are supported." msgstr "" -#: src/subscription_manager/cli_command/repos.py:96 +#: src/subscription_manager/cli_command/repos.py:102 msgid "" "repository to disable (can be specified more than once). Wildcards (* and ?) " "are supported." msgstr "" -#: src/subscription_manager/cli_command/repos.py:165 +#: src/subscription_manager/cli_command/repos.py:171 #, python-brace-format msgid " Available Repositories in {file}" msgstr "" -#: src/subscription_manager/cli_command/repos.py:181 +#: src/subscription_manager/cli_command/repos.py:187 msgid "There were no available repositories matching the specified criteria." msgstr "" -#: src/subscription_manager/cli_command/repos.py:183 -#: src/subscription_manager/cli_command/repos.py:201 +#: src/subscription_manager/cli_command/repos.py:189 +#: src/subscription_manager/cli_command/repos.py:207 msgid "This system has no repositories available through subscriptions." msgstr "" -#: src/subscription_manager/cli_command/repos.py:210 +#: src/subscription_manager/cli_command/repos.py:216 #, python-brace-format msgid "" "Error: '{repoid}' does not match a valid repository ID. Use \"subscription-" "manager repos --list\" to see valid repositories." msgstr "" -#: src/subscription_manager/cli_command/repos.py:265 +#: src/subscription_manager/cli_command/repos.py:271 #, python-brace-format msgid "Repository '{repoid}' is enabled for this system." msgstr "" -#: src/subscription_manager/cli_command/repos.py:267 +#: src/subscription_manager/cli_command/repos.py:273 #, python-brace-format msgid "Repository '{repoid}' is disabled for this system." msgstr "" @@ -2871,42 +2269,42 @@ msgid "" "service levels" msgstr "" -#: src/subscription_manager/cli_command/service_level.py:99 +#: src/subscription_manager/cli_command/service_level.py:98 msgid "" -"Error: --username, --password, --token, --org and --serverurl can be used " -"only on unregistered systems" +"Error: --username, --password, --org and --serverurl can be used only on " +"unregistered systems" msgstr "" -#: src/subscription_manager/cli_command/service_level.py:126 +#: src/subscription_manager/cli_command/service_level.py:123 msgid "Error: Unable to retrieve service levels." msgstr "" -#: src/subscription_manager/cli_command/service_level.py:157 +#: src/subscription_manager/cli_command/service_level.py:154 #, python-brace-format msgid "Service level set to: \"{val}\"." msgstr "" -#: src/subscription_manager/cli_command/service_level.py:165 +#: src/subscription_manager/cli_command/service_level.py:162 msgid "Service level preference has been unset" msgstr "" -#: src/subscription_manager/cli_command/service_level.py:172 -#: src/subscription_manager/cli_command/service_level.py:188 -#: src/subscription_manager/cli_command/service_level.py:222 -#: src/subscription_manager/cli_command/service_level.py:229 +#: src/subscription_manager/cli_command/service_level.py:169 +#: src/subscription_manager/cli_command/service_level.py:185 +#: src/subscription_manager/cli_command/service_level.py:219 +#: src/subscription_manager/cli_command/service_level.py:226 msgid "Error: The service-level command is not supported by the server." msgstr "" -#: src/subscription_manager/cli_command/service_level.py:192 +#: src/subscription_manager/cli_command/service_level.py:189 #, python-brace-format msgid "Current service level: {level}" msgstr "" -#: src/subscription_manager/cli_command/service_level.py:194 +#: src/subscription_manager/cli_command/service_level.py:191 msgid "Service level preference not set" msgstr "" -#: src/subscription_manager/cli_command/service_level.py:206 +#: src/subscription_manager/cli_command/service_level.py:203 msgid "Available Service Levels" msgstr "" @@ -2942,30 +2340,30 @@ msgstr "" msgid "System Purpose Status: {status}" msgstr "" -#: src/subscription_manager/cli_command/syspurpose.py:53 +#: src/subscription_manager/cli_command/syspurpose.py:51 msgid "Convenient module for managing all system purpose settings" msgstr "" -#: src/subscription_manager/cli_command/syspurpose.py:59 +#: src/subscription_manager/cli_command/syspurpose.py:57 msgid "show current system purpose" msgstr "" -#: src/subscription_manager/cli_command/syspurpose.py:76 +#: src/subscription_manager/cli_command/syspurpose.py:74 #, python-brace-format msgid "{prog} {name}" msgstr "" -#: src/subscription_manager/cli_command/syspurpose.py:78 +#: src/subscription_manager/cli_command/syspurpose.py:76 msgid "Syspurpose submodules" msgstr "" -#: src/subscription_manager/cli_command/syspurpose.py:92 +#: src/subscription_manager/cli_command/syspurpose.py:90 #, python-format, python-brace-format msgid "%(prog)s {name} [SUBMODULE] [OPTIONS]" msgstr "" #: src/subscription_manager/cli_command/unregister.py:38 -#: src/subscription_manager/utils.py:286 +#: src/subscription_manager/utils.py:290 msgid "This system is currently not registered." msgstr "" @@ -2985,23 +2383,19 @@ msgstr "" msgid "password to use when authorizing against the server" msgstr "" -#: src/subscription_manager/cli_command/user_pass.py:50 -msgid "token to use when authorizing against the server" -msgstr "" - -#: src/subscription_manager/cli_command/user_pass.py:64 +#: src/subscription_manager/cli_command/user_pass.py:59 msgid "Error: --username is a required parameter in non-interactive mode." msgstr "" -#: src/subscription_manager/cli_command/user_pass.py:69 +#: src/subscription_manager/cli_command/user_pass.py:64 msgid "Error: --password is a required parameter in non-interactive mode." msgstr "" -#: src/subscription_manager/cli_command/user_pass.py:73 +#: src/subscription_manager/cli_command/user_pass.py:68 msgid "Username: " msgstr "" -#: src/subscription_manager/cli_command/user_pass.py:76 +#: src/subscription_manager/cli_command/user_pass.py:71 msgid "Password: " msgstr "" @@ -3019,34 +2413,34 @@ msgstr "" msgid "subscription management rules: {version}" msgstr "" -#: src/subscription_manager/entcertlib.py:295 +#: src/subscription_manager/entcertlib.py:294 #, python-format msgid "%s local certificate has been deleted." msgid_plural "%s local certificates have been deleted." msgstr[0] "" msgstr[1] "" -#: src/subscription_manager/entcertlib.py:452 +#: src/subscription_manager/entcertlib.py:451 #, python-format msgid "Total updates: %d" msgstr "" -#: src/subscription_manager/entcertlib.py:453 +#: src/subscription_manager/entcertlib.py:452 #, python-format msgid "Found (local) serial# %s" msgstr "" -#: src/subscription_manager/entcertlib.py:454 +#: src/subscription_manager/entcertlib.py:453 #, python-format msgid "Expected (UEP) serial# %s" msgstr "" -#: src/subscription_manager/entcertlib.py:455 +#: src/subscription_manager/entcertlib.py:454 #: src/subscription_manager/repolib.py:682 msgid "Added (new)" msgstr "" -#: src/subscription_manager/entcertlib.py:456 +#: src/subscription_manager/entcertlib.py:455 msgid "Deleted (rogue):" msgstr "" @@ -3118,7 +2512,6 @@ msgid "Bad CA certificate: {file}: {reason}" msgstr "" #: src/subscription_manager/exceptions.py:51 -#: src/subscription_manager/scripts/rhsmcertd_worker.py:218 msgid "Your identity certificate has expired" msgstr "" @@ -3278,15 +2671,25 @@ msgstr "" msgid "Error uploading package profile: %s\n" msgstr "" -#: src/subscription_manager/scripts/rhsmcertd_worker.py:198 -msgid "Updating entitlement certificates & repositories" +#: src/subscription_manager/scripts/rhsmcertd_worker.py:328 +msgid "" +"This system is already registered, ignoring request to automatically " +"register." +msgstr "" + +#: src/subscription_manager/scripts/rhsmcertd_worker.py:331 +msgid "Registering the system" +msgstr "" + +#: src/subscription_manager/scripts/rhsmcertd_worker.py:342 +msgid "Updating entitlement certificates & repositories." msgstr "" -#: src/subscription_manager/scripts/rhsmcertd_worker.py:293 +#: src/subscription_manager/scripts/rhsmcertd_worker.py:404 msgid "Unable to update entitlement certificates and repositories" msgstr "" -#: src/subscription_manager/utils.py:386 +#: src/subscription_manager/utils.py:390 msgid "and" msgstr "" diff --git a/po/ko.po b/po/ko.po index 46752023d8..2b5d43e0b7 100644 --- a/po/ko.po +++ b/po/ko.po @@ -14,7 +14,7 @@ msgstr "" "Project-Id-Version: rhsm\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-07-21 12:27+0200\n" -"PO-Revision-Date: 2024-02-24 19:36+0000\n" +"PO-Revision-Date: 2024-09-27 06:11+0000\n" "Last-Translator: 김인수 \n" "Language-Team: Korean \n" @@ -23,7 +23,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Weblate 5.4\n" +"X-Generator: Weblate 5.7.2\n" #: /usr/lib64/python3.9/argparse.py:296 msgid "usage: " diff --git a/setup.py b/setup.py index f0ef2e11ee..4875d9bdfa 100755 --- a/setup.py +++ b/setup.py @@ -263,7 +263,7 @@ def find_py(self): setup( name="subscription-manager", - version="1.30.2", + version="1.30.3", url="http://www.candlepinproject.org", description="Manage subscriptions for Red Hat products.", license="GPLv2", diff --git a/src/daemons/rhsmcertd.c b/src/daemons/rhsmcertd.c index 58cde21ca0..30f9c5b787 100644 --- a/src/daemons/rhsmcertd.c +++ b/src/daemons/rhsmcertd.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -122,19 +123,38 @@ typedef struct _Config { bool auto_registration; } Config; -const char * -timestamp () +/** + * Format an ISO 8601 timestamp with milliseconds precision to the inout + * [timestamp] argument. + */ +void +format_timestamp (char *timestamp, size_t timestamp_len) { - time_t tm = time (0); - char *ts = asctime (localtime (&tm)); - char *p = ts; - while (*p) { - p++; - if (*p == '\n') { - *p = 0; - } - } - return ts; + struct timeval tv; + struct tm tm; + + // Get the time with a subsecond precision, and convert the seconds + // to a 'tm' struct for usage with strftime() + gettimeofday (&tv, NULL); + localtime_r (&tv.tv_sec, &tm); + + // First pass at time formatting: date, 'T' separator, time (up to seconds) + // and '.' separator before milliseconds + strftime (timestamp, timestamp_len, "%Y-%m-%dT%H:%M:%S.", &tm); + + // Second pass: format the milliseconds + sprintf (timestamp + 20, "%03ld", tv.tv_usec / 1000); + + // Third pass: format the timezone offset from UTC as '+hhmm' or '-hhmm' + strftime (timestamp + 23, 10, "%z", &tm); + + // Fourth pass: since there is no ':' between hours and minutes in the + // timezone offset printed by the previous step, then shift the minutes + // by one character further to make room for the ':'; + // the 3 characters moved using memmove() include the trailing null + // character at the end (so it is preserved) + memmove (timestamp + 27, timestamp + 26, 3); + timestamp[26] = ':'; } /* @@ -153,6 +173,7 @@ r_log (const char *level, const char *message, ...) va_list argp; FILE *log_file = NULL; struct stat log_dir_stat; + char timestamp[64]; /* When log directory does not exist, then try to create this directory */ if (stat(LOGDIR, &log_dir_stat) != 0) { @@ -164,9 +185,10 @@ r_log (const char *level, const char *message, ...) log_file = stdout; use_stdout = true; } + format_timestamp (timestamp, sizeof (timestamp)); va_start (argp, message); - fprintf (log_file, "%s [%s] ", timestamp (), level); + fprintf (log_file, "%s [%s] ", timestamp, level); vfprintf (log_file, message, argp); putc ('\n', log_file); diff --git a/src/plugins/dnf/subscription_manager.py b/src/plugins/dnf/subscription_manager.py index d8883e172c..f26a6f430c 100644 --- a/src/plugins/dnf/subscription_manager.py +++ b/src/plugins/dnf/subscription_manager.py @@ -242,7 +242,10 @@ def transaction(self): Call Package Profile """ cfg = config.get_config_parser() - if "1" == cfg.get("rhsm", "package_profile_on_trans"): + if ( + cfg.get("rhsm", "report_package_profile") == "1" + and cfg.get("rhsm", "package_profile_on_trans") == "1" + ): log.debug("Uploading package profile") self._upload_profile() else: diff --git a/src/plugins/libdnf/README.md b/src/plugins/libdnf/README.md index a9ed69817e..a3ae69e11d 100644 --- a/src/plugins/libdnf/README.md +++ b/src/plugins/libdnf/README.md @@ -115,12 +115,6 @@ To use testing repository you have to do several steps: subscription-manager register --username admin --password admin --org admin ``` -* Attach some subscription: - - ``` - subscription-manager attach --pool - ``` - * Choose some repository from output of `subscription-manager repos --list` and enable the repository: diff --git a/src/rhsm/connection.py b/src/rhsm/connection.py index 2d7f5c1018..bf86297c56 100644 --- a/src/rhsm/connection.py +++ b/src/rhsm/connection.py @@ -1536,6 +1536,7 @@ def registerConsumer( facts: Optional[dict] = None, owner: str = None, environments: str = None, + environment_names: str = None, keys: str = None, installed_products: list = None, uuid: str = None, @@ -1581,6 +1582,11 @@ def registerConsumer( for environment in environments.split(","): env_list.append({"id": environment}) params["environments"] = env_list + elif environment_names is not None and self.has_capability(MULTI_ENV): + env_name_list = [] + for env_name in environment_names.split(","): + env_name_list.append({"name": env_name}) + params["environments"] = env_name_list headers = {} if jwt_token: @@ -1903,63 +1909,6 @@ def getAccessibleContent(self, consumerId: str, if_modified_since: datetime.date method, headers=headers, description=_("Fetching content for a certificate") ) - def bindByEntitlementPool(self, consumerId: str, poolId: str, quantity: int = None) -> List[dict]: - """ - Subscribe consumer to a subscription by pool ID - :param consumerId: consumer UUID - :param poolId: pool ID - :param quantity: the desired quantity of subscription to be consumed - """ - method = "/consumers/%s/entitlements?pool=%s" % (self.sanitize(consumerId), self.sanitize(poolId)) - if quantity: - method = "%s&quantity=%s" % (method, quantity) - return self.conn.request_post(method, description=_("Updating subscriptions")) - - def bind(self, consumerId: str, entitle_date: datetime.datetime = None) -> List[dict]: - """ - Same as bindByProduct, but assume the server has a list of the - system's products. This is useful for autosubscribe. Note that this is - done on a best-effort basis, and there are cases when the server will - not be able to fulfill the client's product certs with entitlements - :param consumerId: consumer UUID - :param entitle_date: The date, when subscription will be valid - """ - method = "/consumers/%s/entitlements" % (self.sanitize(consumerId)) - - # add the optional date to the url - if entitle_date: - method = "%s?entitle_date=%s" % (method, self.sanitize(entitle_date.isoformat(), plus=True)) - - return self.conn.request_post(method, description=_("Updating subscriptions")) - - def unbindBySerial(self, consumerId: str, serial: str) -> bool: - """ - Try to remove consumed pool by serial number - :param consumerId: consumer UUID - :param serial: serial number of consumed pool - """ - method = "/consumers/%s/certificates/%s" % (self.sanitize(consumerId), self.sanitize(str(serial))) - return self.conn.request_delete(method, description=_("Unsubscribing")) is None - - def unbindByPoolId(self, consumer_uuid: str, pool_id: str) -> bool: - """ - Try to remove consumed pool by pool ID - :param consumer_uuid: consumer UUID - :param pool_id: pool ID - :return: None - """ - method = "/consumers/%s/entitlements/pool/%s" % (self.sanitize(consumer_uuid), self.sanitize(pool_id)) - return self.conn.request_delete(method, description=_("Unsubscribing")) is None - - def unbindAll(self, consumerId: str) -> dict: - """ - Try to remove all consumed pools - :param consumerId: consumer UUID - :return: Dictionary containing statistics about removed pools - """ - method = "/consumers/%s/entitlements" % self.sanitize(consumerId) - return self.conn.request_delete(method, description=_("Unsubscribing")) - def getPoolsList( self, consumer: str = None, @@ -2059,14 +2008,17 @@ def getServiceLevelList(self, owner_key: str) -> List[str]: results = self.conn.request_get(method, description=_("Fetching service levels")) return results - def getEnvironmentList(self, owner_key: str) -> List[dict]: + def getEnvironmentList(self, owner_key: str, list_all: bool = False) -> List[dict]: """ List the environments for a particular owner. Some servers may not support this and will error out. The caller can always check with supports_resource("environments"). """ - method = "/owners/%s/environments" % self.sanitize(owner_key) + if list_all and self.has_capability("typed_environments"): + method = "/owners/%s/environments?list_all=%r" % (self.sanitize(owner_key), list_all) + else: + method = "/owners/%s/environments" % (self.sanitize(owner_key)) results = self.conn.request_get(method, description=_("Fetching environments")) return results @@ -2149,22 +2101,6 @@ def deleteContentOverrides(self, consumerId: str, params: List[dict] = None) -> params = [] return self.conn.request_delete(method, params, description=_("Removing content overrides")) - def activateMachine(self, consumerId: str, email: str, lang: str = None) -> Union[dict, None]: - """ - Activate a subscription by machine, information is located in the consumer facts - :param consumerId: consumer UUID - :param email: The email for sending notification. The notification will be sent by candlepin server - :param lang: The locale specifies the language of notification email - :return When activation was successful, then dictionary is returned. Otherwise, None is returned. - """ - method = "/subscriptions?consumer_uuid=%s" % consumerId - method += "&email=%s" % self.sanitize(email) - if (not lang) and (locale.getdefaultlocale()[0] is not None): - lang = locale.getdefaultlocale()[0].lower().replace("_", "-") - if lang: - method += "&email_locale=%s" % self.sanitize(lang) - return self.conn.request_post(method, description=_("Activating")) - # used by virt-who def getJob(self, job_id: str) -> str: """ diff --git a/src/rhsm/logutil.py b/src/rhsm/logutil.py index 486d0937f4..36e64fd1f3 100644 --- a/src/rhsm/logutil.py +++ b/src/rhsm/logutil.py @@ -10,6 +10,7 @@ from typing import Optional, Tuple, Union, List +import datetime import logging import logging.handlers import logging.config @@ -17,6 +18,7 @@ import sys import rhsm.config + LOGFILE_DIR = "/var/log/rhsm/" LOGFILE_PATH = os.path.join(LOGFILE_DIR, "rhsm.log") USER_LOGFILE_DIR = os.path.join( @@ -145,6 +147,16 @@ def __init__(self, name) -> None: self.addFilter(PyWarningsLoggingFilter(name="py.warnings")) +class RhsmISO8601Formatter(logging.Formatter): + """Ensure date & time to be in ISO8601 format and containing info about milliseconds and time zone""" + + def __init__(self): + super().__init__(fmt=LOG_FORMAT) + + def formatTime(self, record, datefmt=None): + return datetime.datetime.fromtimestamp(record.created).astimezone().isoformat(timespec="milliseconds") + + def _get_default_rhsm_log_handler() -> ( Tuple[Union[logging.handlers.RotatingFileHandler, logging.StreamHandler], Optional[str]] ): @@ -152,7 +164,7 @@ def _get_default_rhsm_log_handler() -> ( error: Optional[Exception] = None if not _rhsm_log_handler: _rhsm_log_handler, error = RHSMLogHandler(LOGFILE_PATH, USER_LOGFILE_PATH) - _rhsm_log_handler.setFormatter(logging.Formatter(LOG_FORMAT)) + _rhsm_log_handler.setFormatter(RhsmISO8601Formatter()) return _rhsm_log_handler, error @@ -160,7 +172,7 @@ def _get_default_subman_debug_handler() -> Union[None, "SubmanDebugHandler"]: global _subman_debug_handler if not _subman_debug_handler: _subman_debug_handler = SubmanDebugHandler() - _subman_debug_handler.setFormatter(logging.Formatter(LOG_FORMAT)) + _subman_debug_handler.setFormatter(RhsmISO8601Formatter()) return _subman_debug_handler diff --git a/src/rhsmlib/client_info.py b/src/rhsmlib/client_info.py index aed83ffbd5..7dbe113153 100644 --- a/src/rhsmlib/client_info.py +++ b/src/rhsmlib/client_info.py @@ -14,7 +14,7 @@ """ This module holds information about current state of client application like sender of D-bus method, current subscription-manager command (register, -attach, ...), dnf command, etc. +status...), dnf command, etc. """ import logging diff --git a/src/rhsmlib/dbus/constants.py b/src/rhsmlib/dbus/constants.py index 6215467dbc..98ddc33a7b 100644 --- a/src/rhsmlib/dbus/constants.py +++ b/src/rhsmlib/dbus/constants.py @@ -24,8 +24,6 @@ "UNREGISTER_DBUS_PATH", "CONFIG_INTERFACE", "CONFIG_DBUS_PATH", - "ATTACH_INTERFACE", - "ATTACH_DBUS_PATH", "PRODUCTS_INTERFACE", "PRODUCTS_DBUS_PATH", "ENTITLEMENT_INTERFACE", @@ -69,9 +67,6 @@ CONFIG_INTERFACE = "%s.%s" % (INTERFACE_BASE, "Config") CONFIG_DBUS_PATH = "%s/%s" % (ROOT_DBUS_PATH, "Config") -ATTACH_INTERFACE = "%s.%s" % (INTERFACE_BASE, "Attach") -ATTACH_DBUS_PATH = "%s/%s" % (ROOT_DBUS_PATH, "Attach") - PRODUCTS_INTERFACE = "%s.%s" % (INTERFACE_BASE, "Products") PRODUCTS_DBUS_PATH = "%s/%s" % (ROOT_DBUS_PATH, "Products") diff --git a/src/rhsmlib/dbus/objects/entitlement.py b/src/rhsmlib/dbus/objects/entitlement.py index 07cfa62620..889862e048 100644 --- a/src/rhsmlib/dbus/objects/entitlement.py +++ b/src/rhsmlib/dbus/objects/entitlement.py @@ -11,7 +11,7 @@ # granted to use or replicate Red Hat trademarks that are incorporated # in this software or its documentation. import datetime -from typing import List, Union +from typing import Union import dbus import json @@ -65,35 +65,6 @@ def get_pools(self, options: dict, proxy_options: dict) -> dict: return pools - def remove_all_entitlements(self, proxy_options: dict) -> dict: - uep: UEPConnection = self.build_uep(proxy_options, proxy_only=True) - service = EntitlementService(uep) - result: dict = service.remove_all_entitlements() - - return result - - def remove_entitlements_by_pool_ids(self, pool_ids: List[str], proxy_options: dict) -> List[str]: - """Remove entitlements by Pool IDs - - :return: List of removed serials. - """ - uep: UEPConnection = self.build_uep(proxy_options, proxy_only=True) - service = EntitlementService(uep) - _, _, removed_serials = service.remove_entitlements_by_pool_ids(pool_ids) - - return removed_serials - - def remove_entitlements_by_serials(self, serials: List[str], proxy_options: dict) -> List[str]: - """Remove entitlements by serials. - - :return: List of removed serials. - """ - uep: UEPConnection = self.build_uep(proxy_options, proxy_only=True) - service = EntitlementService(uep) - removed_serials, _ = service.remove_entitlements_by_serials(serials) - - return removed_serials - def _parse_date(self, date_string: str) -> datetime.datetime: """ Return new datetime parsed from date. @@ -187,79 +158,6 @@ def GetPools(self, options, proxy_options, locale, sender=None): pools: dict = self.impl.get_pools(options, proxy_options) return json.dumps(pools) - @util.dbus_service_method( - constants.ENTITLEMENT_INTERFACE, - in_signature="a{sv}s", - out_signature="s", - ) - @util.dbus_handle_sender - @util.dbus_handle_exceptions - def RemoveAllEntitlements(self, proxy_options, locale, sender=None): - """ - Try to remove all entitlements (subscriptions) from the system - :param proxy_options: Settings of proxy - :param locale: String with locale (e.g. de_DE.UTF-8) - :param sender: Not used argument - :return: Json string containing response - """ - proxy_options = dbus_utils.dbus_to_python(proxy_options, expected_type=dict) - locale = dbus_utils.dbus_to_python(locale, expected_type=str) - - Locale.set(locale) - - result: dict = self.impl.remove_all_entitlements(proxy_options) - return json.dumps(result) - - @util.dbus_service_method( - constants.ENTITLEMENT_INTERFACE, - in_signature="asa{sv}s", - out_signature="s", - ) - @util.dbus_handle_sender - @util.dbus_handle_exceptions - def RemoveEntitlementsByPoolIds(self, pool_ids, proxy_options, locale, sender=None): - """ - Try to remove entitlements (subscriptions) by pool_ids - :param pool_ids: List of pool IDs - :param proxy_options: Settings of proxy - :param locale: String with locale (e.g. de_DE.UTF-8) - :param sender: Not used argument - :return: Json string representing list of serial numbers - """ - pool_ids = dbus_utils.dbus_to_python(pool_ids, expected_type=list) - proxy_options = dbus_utils.dbus_to_python(proxy_options, expected_type=dict) - locale = dbus_utils.dbus_to_python(locale, expected_type=str) - - Locale.set(locale) - - removed_serials = self.impl.remove_entitlements_by_pool_ids(pool_ids, proxy_options) - return json.dumps(removed_serials) - - @util.dbus_service_method( - constants.ENTITLEMENT_INTERFACE, - in_signature="asa{sv}s", - out_signature="s", - ) - @util.dbus_handle_sender - @util.dbus_handle_exceptions - def RemoveEntitlementsBySerials(self, serials, proxy_options, locale, sender=None): - """ - Try to remove entitlements (subscriptions) by serials - :param serials: List of serial numbers of subscriptions - :param proxy_options: Settings of proxy - :param locale: String with locale (e.g. de_DE.UTF-8) - :param sender: Not used argument - :return: Json string representing list of serial numbers - """ - serials = dbus_utils.dbus_to_python(serials, expected_type=list) - proxy_options = dbus_utils.dbus_to_python(proxy_options, expected_type=dict) - locale = dbus_utils.dbus_to_python(locale, expected_type=str) - - Locale.set(locale) - - removed_serials = self.impl.remove_entitlements_by_serials(serials, proxy_options) - return json.dumps(removed_serials) - @staticmethod def reload(): entitlement_service = EntitlementService() diff --git a/src/rhsmlib/dbus/objects/register.py b/src/rhsmlib/dbus/objects/register.py index f22d4b2efd..e2b68d559d 100644 --- a/src/rhsmlib/dbus/objects/register.py +++ b/src/rhsmlib/dbus/objects/register.py @@ -24,6 +24,7 @@ from rhsmlib.services.register import RegisterService from rhsmlib.services.unregister import UnregisterService from rhsmlib.services.entitlement import EntitlementService +from rhsmlib.services.environment import EnvironmentService from rhsmlib.client_info import DBusSender from subscription_manager.cp_provider import CPProvider @@ -36,6 +37,14 @@ log = logging.getLogger(__name__) +ENVIRONMENTS_KEYS_TO_FILTER: List[str] = [ + "contentPrefix", + "created", + "environmentContent", + "owner", + "updated", +] + class RegisterDBusImplementation(base_object.BaseImplementation): def __init__(self): @@ -181,6 +190,25 @@ def get_organizations(self, options: dict) -> List[dict]: owners: List[dict] = uep.getOwnerList(options["username"]) return owners + def get_environments(self, options: dict) -> List[dict]: + """Get environments for every org belonging to user + + :param options: Connection options including the 'username', 'password', and 'org_id' keys + :return: List of environments + """ + uep: UEPConnection = self.build_uep(options, basic_auth_method=True) + environment_service: EnvironmentService = EnvironmentService(uep) + + environments = environment_service.list(options["org_id"]) + + for environment in environments: + for key in ENVIRONMENTS_KEYS_TO_FILTER: + environment.pop(key, None) + if ("type" not in environment) or (environment["type"] is None): + environment["type"] = "" + + return environments + def register_with_credentials( self, organization: Optional[str], register_options: dict, connection_options: dict ) -> dict: @@ -333,6 +361,40 @@ def __init__(self, conn=None, object_path=None, bus_name=None, sender=None, cmd_ self.sender = sender self.cmd_line = cmd_line + @dbus.service.method( + dbus_interface=constants.PRIVATE_REGISTER_INTERFACE, + in_signature="sssa{sv}s", + out_signature="s", + ) + @util.dbus_handle_exceptions + def GetEnvironments(self, username, password, org_id, connection_options, locale): + """ + This method tries to return list of environments in the given orgs. This method also uses + basic authentication method (using username and password). + + :param username: string with username used for connection to candlepin server + :param password: string with password + :param org_id: string with org id to list environments for + :param connection_options: dictionary with connection options + :param locale: string with locale + :return: string with json returned by candlepin server + """ + connection_options = dbus_utils.dbus_to_python(connection_options, expected_type=dict) + connection_options["username"] = dbus_utils.dbus_to_python(username, expected_type=str) + connection_options["password"] = dbus_utils.dbus_to_python(password, expected_type=str) + connection_options["org_id"] = dbus_utils.dbus_to_python(org_id, expected_type=str) + locale = dbus_utils.dbus_to_python(locale, expected_type=str) + + with DBusSender() as dbus_sender: + dbus_sender.set_cmd_line(sender=self.sender, cmd_line=self.cmd_line) + Locale.set(locale) + + environments = self.impl.get_environments(connection_options) + + dbus_sender.reset_cmd_line() + + return json.dumps(environments) + @dbus.service.method( dbus_interface=constants.PRIVATE_REGISTER_INTERFACE, in_signature="ssa{sv}s", diff --git a/src/rhsmlib/services/entitlement.py b/src/rhsmlib/services/entitlement.py index 527cbc5f0c..d3d1d89ec5 100644 --- a/src/rhsmlib/services/entitlement.py +++ b/src/rhsmlib/services/entitlement.py @@ -15,7 +15,7 @@ import datetime import logging import time -from typing import Union, Callable +from typing import Union from subscription_manager import injection as inj from subscription_manager.i18n import ugettext as _ @@ -518,77 +518,6 @@ def validate_options(self, options: dict) -> None: elif not self.identity.is_valid() and "available" in options["pool_subsets"]: raise exceptions.ValidationError(_("Error: this system is not registered")) - def _unbind_ids(self, unbind_method: Callable, consumer_uuid: str, ids: list) -> tuple: - """ - Method for unbinding entitlements - :param unbind_method: unbindByPoolId or unbindBySerial - :param consumer_uuid: UUID of consumer - :param ids: List of serials or pool_ids - :return: Tuple of two lists containing unbinded and not-unbinded subscriptions - """ - success = [] - failure = [] - for id_ in ids: - try: - unbind_method(consumer_uuid, id_) - success.append(id_) - except connection.RestlibException as re: - if re.code == 410: - raise - failure.append(id_) - log.error(re) - return success, failure - - def remove_all_entitlements(self) -> dict: - """ - Try to remove all entitlements - :return: Result of REST API call - """ - - response = self.cp.unbindAll(self.identity.uuid) - self.entcertlib.update() - - return response - - def remove_entitlements_by_pool_ids(self, pool_ids: list) -> tuple: - """ - Try to remove entitlements by pool IDs - :param pool_ids: List of pool IDs - :return: List of serial numbers of removed subscriptions - """ - - removed_serials = [] - _pool_ids = utils.unique_list_items(pool_ids) # Don't allow duplicates - # FIXME: the cache of CertificateDirectory should be smart enough and refreshing - # should not be necessary. I vote for i-notify to be used there somehow. - self.entitlement_dir.refresh() - pool_id_to_serials = self.entitlement_dir.list_serials_for_pool_ids(_pool_ids) - removed_pools, unremoved_pools = self._unbind_ids( - self.cp.unbindByPoolId, self.identity.uuid, _pool_ids - ) - if removed_pools: - for pool_id in removed_pools: - removed_serials.extend(pool_id_to_serials[pool_id]) - self.entcertlib.update() - - return removed_pools, unremoved_pools, removed_serials - - def remove_entitlements_by_serials(self, serials: list) -> tuple: - """ - Try to remove pools by Serial numbers - :param serials: List of serial numbers - :return: Tuple of two items: list of serial numbers of already removed subscriptions and list - of not removed serials - """ - - _serials = utils.unique_list_items(serials) # Don't allow duplicates - removed_serials, unremoved_serials = self._unbind_ids( - self.cp.unbindBySerial, self.identity.uuid, _serials - ) - self.entcertlib.update() - - return removed_serials, unremoved_serials - @staticmethod def reload() -> None: """ @@ -612,11 +541,6 @@ def refresh(self, remove_cache: bool = False, force: bool = False) -> None: content_access = inj.require(inj.CONTENT_ACCESS_CACHE) if content_access.exists(): content_access.remove() - # Also remove the content access mode cache to be sure we display - # SCA or regular mode correctly - content_access_mode = inj.require(inj.CONTENT_ACCESS_MODE_CACHE) - if content_access_mode.exists(): - content_access_mode.delete_cache() if force is True: # Force a regen of the entitlement certs for this consumer diff --git a/src/rhsmlib/services/environment.py b/src/rhsmlib/services/environment.py new file mode 100644 index 0000000000..ef70163668 --- /dev/null +++ b/src/rhsmlib/services/environment.py @@ -0,0 +1,43 @@ +# Copyright (c) 2024 Red Hat, Inc. +# +# This software is licensed to you under the GNU General Public License, +# version 2 (GPLv2). There is NO WARRANTY for this software, express or +# implied, including the implied warranties of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 +# along with this software; if not, see +# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. +# +# Red Hat trademarks are not licensed under GPLv2. No permission is +# granted to use or replicate Red Hat trademarks that are incorporated +# in this software or its documentation. +import logging +from typing import List + + +from rhsm.connection import UEPConnection + +log = logging.getLogger(__name__) + + +class EnvironmentService: + """ + Class for using environments + """ + + def __init__(self, cp: UEPConnection) -> None: + """ + Initialization of EnvironmentService instance + :param cp: connection to Candlepin + """ + self.cp = cp + + def list(self, org_id: str) -> List[dict]: + """ + Method for listing environments + :param org_id: organization to list environments for + :return: List of environments. + """ + list_all = self.cp.has_capability("typed_environments") + environments = self.cp.getEnvironmentList(org_id, list_all=list_all) + + return environments diff --git a/src/rhsmlib/services/refresh.py b/src/rhsmlib/services/refresh.py index 6ddc2d57d3..37502a00ec 100644 --- a/src/rhsmlib/services/refresh.py +++ b/src/rhsmlib/services/refresh.py @@ -54,12 +54,6 @@ def refresh(self, force: bool = False) -> None: :return: None """ - # First remove the content access mode cache to be sure we display - # SCA or regular mode correctly - content_access_mode = inj.require(inj.CONTENT_ACCESS_MODE_CACHE) - if content_access_mode.exists(): - content_access_mode.delete_cache() - # Remove the release status cache, in case it was changed # on the server; it will be fetched when needed again inj.require(inj.RELEASE_STATUS_CACHE).delete_cache() diff --git a/src/rhsmlib/services/register.py b/src/rhsmlib/services/register.py index 0af7e3761a..b244b862d7 100644 --- a/src/rhsmlib/services/register.py +++ b/src/rhsmlib/services/register.py @@ -46,6 +46,8 @@ def register( org: Optional[str], activation_keys: list = None, environments: list = None, + environment_names: list = None, + environment_type: str = None, force: bool = False, name: str = None, consumerid: str = None, @@ -66,6 +68,11 @@ def register( if kwargs: raise exceptions.ValidationError(_("Unknown arguments: %s") % kwargs.keys()) + if environments is not None and environment_names is not None: + raise exceptions.ValidationError( + _("Environment IDs and environment names are mutually exclusive") + ) + syspurpose = syspurposelib.read_syspurpose() save_syspurpose = False @@ -95,6 +102,7 @@ def register( options = { "activation_keys": activation_keys, "environments": environments, + "environment_names": environment_names, "force": force, "name": name, "consumerid": consumerid, @@ -123,6 +131,7 @@ def register( facts=facts_dict, owner=org, environments=environments, + environment_names=environment_names, keys=options.get("activation_keys"), installed_products=self.installed_mgr.format_for_server(), content_tags=self.installed_mgr.tags, @@ -138,8 +147,44 @@ def register( cp_provider: CPProvider = inj.require(inj.CP_PROVIDER) cp_provider.close_all_connections() + # If environment type was specified, then check that all returned + # environments have required type. Otherwise, raise exception + wrong_env_names = [] + if environment_type is not None: + for environment in consumer.get("environments", []): + env_type = environment.get("type", None) + if env_type != environment_type: + environment_name = environment["name"] + log.error( + f"Environment: '{environment_name}' does not have required type: '{environment_type}," + f" it has '{env_type}' type" + ) + wrong_env_names.append(environment_name) + managerlib.persist_consumer_cert(consumer) + if len(wrong_env_names) > 0: + # We will not use this consumer object. Thus, delete this object + # on the server + self.identity.reload() + UnregisterService(inj.require(inj.CP_PROVIDER).get_consumer_auth_cp()).unregister() + if len(wrong_env_names) == 1: + raise exceptions.ServiceError( + _( + "Environment: '{env_names}' does not have required type '{environment_type}'".format( + env_names=wrong_env_names[0], environment_type=environment_type + ) + ) + ) + else: + raise exceptions.ServiceError( + _( + "Environments: '{env_names}' do not have required type '{environment_type}'".format( + env_names=", ".join(wrong_env_names), environment_type=environment_type + ) + ) + ) + access_mode: str = consumer.get("owner", {}).get("contentAccessMode", "unknown") if access_mode != "org_environment": log.error( @@ -185,26 +230,6 @@ def register( # Save syspurpose attributes from consumer to cache file syspurposelib.write_syspurpose_cache(syspurpose_dict) - content_access_mode_cache = inj.require(inj.CONTENT_ACCESS_MODE_CACHE) - - # Is information about content access mode included in consumer - if "owner" not in consumer: - log.warning("Consumer does not contain any information about owner.") - elif "contentAccessMode" in consumer["owner"]: - log.debug("Saving content access mode from consumer object to cache file.") - # When we know content access mode from consumer, then write it to cache file - content_access_mode = consumer["owner"]["contentAccessMode"] - content_access_mode_cache.set_data(content_access_mode, self.identity) - content_access_mode_cache.write_cache() - else: - # If not, then we have to do another REST API call to get this information - # It will not be included in cache file. When cache file is empty, then - # it will trigger accessing REST API and saving result in cache file. - log.debug("Information about content access mode is not included in consumer") - content_access_mode = content_access_mode_cache.read_data() - # Add information about content access mode to consumer - consumer["owner"]["contentAccessMode"] = content_access_mode - return consumer def validate_options(self, options: dict) -> None: @@ -243,8 +268,7 @@ def validate_options(self, options: dict) -> None: # TODO: add more checks here pass elif not getattr(self.cp, "username", None) or not getattr(self.cp, "password", None): - if not getattr(self.cp, "token", None): - raise exceptions.ValidationError(_("Error: Missing username or password.")) + raise exceptions.ValidationError(_("Error: Missing username or password.")) def determine_owner_key(self, username: str, get_owner_cb: Callable, no_owner_cb: Callable) -> str: """ diff --git a/src/subscription_manager/action_client.py b/src/subscription_manager/action_client.py index 66098b257b..e8b431ddba 100644 --- a/src/subscription_manager/action_client.py +++ b/src/subscription_manager/action_client.py @@ -47,8 +47,7 @@ def _get_libset(self) -> List["BaseActionInvoker"]: self.syspurposelib = SyspurposeSyncActionInvoker() # WARNING: order is important here, we need to update a number - # of things before attempting to autoheal, and we need to autoheal - # before attempting to fetch our certificates: + # of things before attempting to fetch our certificates: lib_set: List[BaseActionInvoker] = [ self.entcertlib, self.idcertlib, diff --git a/src/subscription_manager/cache.py b/src/subscription_manager/cache.py index 5fb2c4eb47..c5c6aa1b7e 100644 --- a/src/subscription_manager/cache.py +++ b/src/subscription_manager/cache.py @@ -1023,72 +1023,6 @@ def _is_cache_obsoleted(self, uep: connection.UEPConnection, identity: "Identity return True -class ContentAccessModeCache(ConsumerCache): - """ - Cache information about current owner (organization), specifically, the content access mode. - This value is used independently. - """ - - # Grab the current owner (and hence the content_access_mode of that owner) at most, once per - # 4 hours - TIMEOUT = 60 * 60 * 4 - - CACHE_FILE = "/var/lib/rhsm/cache/content_access_mode.json" - - def __init__(self, data: Any = None): - super(ContentAccessModeCache, self).__init__(data=data) - - def _sync_with_server( - self, uep: connection.UEPConnection, consumer_uuid: str, _: Optional[datetime.datetime] = None - ) -> str: - try: - current_owner: Dict = uep.getOwner(consumer_uuid) - except Exception: - log.debug( - "Error checking for content access mode," - "defaulting to assuming not in Simple Content Access mode" - ) - else: - if "contentAccessMode" in current_owner: - return current_owner["contentAccessMode"] - else: - log.debug( - "The owner returned from the server did not contain a " - "'content_access_mode'. Perhaps the connected Entitlement Server doesn't" - "support 'content_access_mode'?" - ) - return "unknown" - - def _is_cache_obsoleted(self, uep: connection.UEPConnection, identity: "Identity"): - """ - We don't know if the cache is valid until we get valid response - :param uep: object representing connection to candlepin server - :param identity: consumer identity - :return: True, when cache is obsoleted or validity of cache is unknown. - """ - if uep is None: - cp_provider: CPProvider = inj.require(inj.CP_PROVIDER) - uep: connection.UEPConnection = cp_provider.get_consumer_auth_cp() - - if hasattr(uep.conn, "is_consumer_cert_key_valid"): - if uep.conn.is_consumer_cert_key_valid is None: - log.debug( - f"Cache file {self.CACHE_FILE} cannot be considered as valid, because no connection has " - "been created yet" - ) - return True - elif uep.conn.is_consumer_cert_key_valid is True: - return False - else: - log.debug( - f"Cache file {self.CACHE_FILE} cannot be considered as valid, " - "because consumer certificate probably is not valid" - ) - return True - else: - return True - - class SupportedResourcesCache(ConsumerCache): """ Cache supported resources of candlepin server for current identity diff --git a/src/subscription_manager/certdirectory.py b/src/subscription_manager/certdirectory.py index 891d4d758a..c3138d44f1 100644 --- a/src/subscription_manager/certdirectory.py +++ b/src/subscription_manager/certdirectory.py @@ -336,15 +336,6 @@ def list_for_pool_id(self, pool_id: str) -> List["EntitlementCertificate"]: ] return entitlements - def list_serials_for_pool_ids(self, pool_ids: List[str]) -> Dict[str, List[str]]: - """ - Returns a dict of all entitlement certificate serials for each pool_id in the list provided - """ - pool_id_to_serials = {} - for pool_id in pool_ids: - pool_id_to_serials[pool_id] = [str(cert.serial) for cert in self.list_for_pool_id(pool_id)] - return pool_id_to_serials - class Path: # Used during Anaconda install by the yum pidplugin to ensure we operate diff --git a/src/subscription_manager/cli_command/abstract_syspurpose.py b/src/subscription_manager/cli_command/abstract_syspurpose.py index cc9151ceae..9dbf5f989e 100644 --- a/src/subscription_manager/cli_command/abstract_syspurpose.py +++ b/src/subscription_manager/cli_command/abstract_syspurpose.py @@ -145,11 +145,7 @@ def _validate_options(self): 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: + if not self.options.username or not self.options.password: system_exit( os.EX_USAGE, _( @@ -164,15 +160,11 @@ def _validate_options(self): 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" - ), + _("Error: --username, --password, and --org " "can be used only on unregistered systems"), ) def _get_valid_fields(self): @@ -241,11 +233,7 @@ def _are_provided_values_valid(self, values): # 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 self.is_registered() or (self.options.username and self.options.password): 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] @@ -423,17 +411,7 @@ def _do_command(self): # 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: + if 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: diff --git a/src/subscription_manager/cli_command/environments.py b/src/subscription_manager/cli_command/environments.py index 4d58e66fa5..d6a57089b8 100644 --- a/src/subscription_manager/cli_command/environments.py +++ b/src/subscription_manager/cli_command/environments.py @@ -94,14 +94,11 @@ def _do_command(self): if "environments" not in supported_resources: system_exit(os.EX_UNAVAILABLE, _("Error: Server does not support environments.")) try: - if self.options.token: - self.cp = self.cp_provider.get_keycloak_auth_cp(self.options.token) - else: - if not self.options.enabled: - if self.options.username is None or self.options.password is None: - print(_("This operation requires user credentials")) - self.cp_provider.set_user_pass(self.username, self.password) - self.cp = self.cp_provider.get_basic_auth_cp() + if not self.options.enabled: + if self.options.username is None or self.options.password is None: + print(_("This operation requires user credentials")) + self.cp_provider.set_user_pass(self.username, self.password) + self.cp = self.cp_provider.get_basic_auth_cp() self.identity = require(IDENTITY) if self.options.set: self._set_environments() diff --git a/src/subscription_manager/cli_command/identity.py b/src/subscription_manager/cli_command/identity.py index 430626a823..b6804818d1 100644 --- a/src/subscription_manager/cli_command/identity.py +++ b/src/subscription_manager/cli_command/identity.py @@ -61,8 +61,6 @@ def _validate_options(self): system_exit(os.EX_USAGE, _("--force can only be used with --regenerate")) if (self.options.username or self.options.password) and not self.options.force: system_exit(os.EX_USAGE, _("--username and --password can only be used with --force")) - if self.options.token and not self.options.force: - system_exit(os.EX_USAGE, _("--token can only be used with --force")) def _do_command(self): # get current consumer identity @@ -114,12 +112,9 @@ def _do_command(self): ) else: if self.options.force: - # get an UEP with basic auth or keycloak auth - if self.options.token: - self.cp = self.cp_provider.get_keycloak_auth_cp(self.options.token) - else: - self.cp_provider.set_user_pass(self.username, self.password) - self.cp = self.cp_provider.get_basic_auth_cp() + # get an UEP with basic auth + self.cp_provider.set_user_pass(self.username, self.password) + self.cp = self.cp_provider.get_basic_auth_cp() consumer = self.cp.regenIdCertificate(consumerid) managerlib.persist_consumer_cert(consumer) diff --git a/src/subscription_manager/cli_command/owners.py b/src/subscription_manager/cli_command/owners.py index 1b3c69b75f..761673f89d 100644 --- a/src/subscription_manager/cli_command/owners.py +++ b/src/subscription_manager/cli_command/owners.py @@ -44,11 +44,8 @@ def __init__(self): def _do_command(self): try: # get a UEP - if self.options.token: - self.cp = self.cp_provider.get_keycloak_auth_cp(self.options.token) - else: - self.cp_provider.set_user_pass(self.username, self.password) - self.cp = self.cp_provider.get_basic_auth_cp() + self.cp_provider.set_user_pass(self.username, self.password) + self.cp = self.cp_provider.get_basic_auth_cp() owners = self.cp.getOwnerList(self.username) log.debug("Successfully retrieved org list from server.") if len(owners): diff --git a/src/subscription_manager/cli_command/register.py b/src/subscription_manager/cli_command/register.py index 2dd858b9a7..a2b1e6e40d 100644 --- a/src/subscription_manager/cli_command/register.py +++ b/src/subscription_manager/cli_command/register.py @@ -119,7 +119,7 @@ def _validate_options(self): system_exit(os.EX_USAGE, _("This system is already registered. Use --force to override")) elif self.options.consumername == "": system_exit(os.EX_USAGE, _("Error: system name can not be empty.")) - elif (self.options.username or self.options.token) and self.options.activation_keys: + elif self.options.username and self.options.activation_keys: system_exit(os.EX_USAGE, _("Error: Activation keys do not require user credentials.")) elif self.options.consumerid and self.options.activation_keys: system_exit( @@ -257,9 +257,7 @@ def _do_command(self): # Proceed with new registration: try: - if self.options.token: - admin_cp = self.cp_provider.get_keycloak_auth_cp(self.options.token) - elif not self.options.activation_keys: + if not self.options.activation_keys: hostname = conf["server"]["hostname"] if ":" in hostname: normalized_hostname = "[{hostname}]".format(hostname=hostname) diff --git a/src/subscription_manager/cli_command/service_level.py b/src/subscription_manager/cli_command/service_level.py index 917e56fce3..6f0b09595b 100644 --- a/src/subscription_manager/cli_command/service_level.py +++ b/src/subscription_manager/cli_command/service_level.py @@ -71,7 +71,7 @@ def _validate_options(self): if not self.is_registered(): if self.options.list: - if not (self.options.username and self.options.password) and not self.options.token: + if not (self.options.username and self.options.password): system_exit( os.EX_USAGE, _( @@ -89,14 +89,13 @@ def _validate_options(self): 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) or getattr(self.options, "server_url", None) ): system_exit( os.EX_USAGE, _( - "Error: --username, --password, --token, --org and --serverurl " + "Error: --username, --password, --org and --serverurl " "can be used only on unregistered systems" ), ) @@ -107,9 +106,7 @@ def _do_command(self): # 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: - self.cp = self.cp_provider.get_keycloak_auth_cp(self.options.token) - elif self.options.username and self.options.password: + if self.options.username and self.options.password: self.cp_provider.set_user_pass(self.username, self.password) self.cp = self.cp_provider.get_basic_auth_cp() elif not self.is_registered() and self.options.show: diff --git a/src/subscription_manager/cli_command/user_pass.py b/src/subscription_manager/cli_command/user_pass.py index b5cb873247..1d760dbe00 100644 --- a/src/subscription_manager/cli_command/user_pass.py +++ b/src/subscription_manager/cli_command/user_pass.py @@ -44,11 +44,6 @@ def __init__(self, name, shortdesc=None, primary=False): dest="password", help=_("password to use when authorizing against the server"), ) - self.parser.add_argument( - "--token", - dest="token", - help=_("token to use when authorizing against the server"), - ) @staticmethod def _get_username_and_password(username, password): @@ -82,9 +77,6 @@ def _get_username_and_password(username, password): @property def username(self): if not self._username: - if self.options.token: - self._username = self.cp_provider.token_username - return self._username (self._username, self._password) = self._get_username_and_password( self.options.username, self.options.password ) @@ -92,7 +84,7 @@ def username(self): @property def password(self): - if not self._password and not self.options.token: + if not self._password: (self._username, self._password) = self._get_username_and_password( self.options.username, self.options.password ) diff --git a/src/subscription_manager/entcertlib.py b/src/subscription_manager/entcertlib.py index 84d3f0fc9d..c2ac33b516 100644 --- a/src/subscription_manager/entcertlib.py +++ b/src/subscription_manager/entcertlib.py @@ -108,9 +108,7 @@ def perform(self) -> "EntCertUpdateReport": log.info("certs updated:\n%s", self.report) self.syslog_results() - # We call EntCertlibActionInvoker.update() solo from - # the 'attach' cli instead of an ActionClient. So - # we need to refresh the ent_dir object before calling + # We need to refresh the ent_dir object before calling # content updating actions. self.ent_dir.refresh() diff --git a/src/subscription_manager/injection.py b/src/subscription_manager/injection.py index 1c4487bde3..4f2f09b500 100644 --- a/src/subscription_manager/injection.py +++ b/src/subscription_manager/injection.py @@ -19,7 +19,6 @@ PRODUCT_DATE_RANGE_CALCULATOR = "PRODUCT_DATE_RANGE_CALCULATOR" ENT_DIR = "ENT_DIR" PROD_DIR = "PROD_DIR" -CONTENT_ACCESS_MODE_CACHE = "CONTENT_ACCESS_MODE_CACHE" CURRENT_OWNER_CACHE = "CURRENT_OWNER_CACHE" SYSPURPOSE_VALID_FIELDS_CACHE = "SYSPURPOSE_VALID_FIELDS_CACHE" SUPPORTED_RESOURCES_CACHE = "SUPPORTED_RESOURCES_CACHE" diff --git a/src/subscription_manager/injectioninit.py b/src/subscription_manager/injectioninit.py index 463ccb574c..8a181d995f 100644 --- a/src/subscription_manager/injectioninit.py +++ b/src/subscription_manager/injectioninit.py @@ -30,7 +30,6 @@ AvailableEntitlementsCache, CurrentOwnerCache, SyspurposeValidFieldsCache, - ContentAccessModeCache, ) from subscription_manager.cert_sorter import CertSorter @@ -65,7 +64,6 @@ def init_dep_injection(): # but runs a new version of injectioninit...) inj.provide(inj.ENTITLEMENT_STATUS_CACHE, EntitlementStatusCache, singleton=True) inj.provide(inj.SYSTEMPURPOSE_COMPLIANCE_STATUS_CACHE, SyspurposeComplianceStatusCache, singleton=True) - inj.provide(inj.CONTENT_ACCESS_MODE_CACHE, ContentAccessModeCache, singleton=True) inj.provide(inj.CURRENT_OWNER_CACHE, CurrentOwnerCache, singleton=True) inj.provide(inj.SYSPURPOSE_VALID_FIELDS_CACHE, SyspurposeValidFieldsCache) inj.provide(inj.SUPPORTED_RESOURCES_CACHE, SupportedResourcesCache, singleton=True) diff --git a/src/subscription_manager/managerlib.py b/src/subscription_manager/managerlib.py index df2deb9975..e291ddc9e3 100644 --- a/src/subscription_manager/managerlib.py +++ b/src/subscription_manager/managerlib.py @@ -18,7 +18,6 @@ import logging import os import grp -import re import shutil import stat import syslog @@ -26,7 +25,6 @@ from rhsm.config import get_config_parser -from rhsm.certificate import Key, CertificateException, create_from_pem import subscription_manager.cache as cache from subscription_manager.cert_sorter import StackingGroupSorter, ComplianceManager @@ -749,166 +747,6 @@ def lookup_provided_products(self, pool_id: str) -> Optional[List[Tuple[str, str return provided_products -class ImportFileExtractor: - """ - Responsible for checking an import file and pulling cert and key from it. - An import file may include only the certificate, but may also include its - key. - - An import file is processed looking for: - - -----BEGIN ----- - - .. - -----END ----- - - and will only process if it finds CERTIFICATE or KEY in the text. - - For example the following would locate a key and cert. - - -----BEGIN CERTIFICATE----- - - -----END CERTIFICATE----- - -----BEGIN PUBLIC KEY----- - - -----END PUBLIC KEY----- - - """ - - _REGEX_START_GROUP = "start" - _REGEX_CONTENT_GROUP = "content" - _REGEX_END_GROUP = "end" - _REGEX = r"(?P<%s>[-]*BEGIN[\w\ ]*[-]*)(?P<%s>[^-]*)(?P<%s>[-]*END[\w\ ]*[-]*)" % ( - _REGEX_START_GROUP, - _REGEX_CONTENT_GROUP, - _REGEX_END_GROUP, - ) - _PATTERN = re.compile(_REGEX) - - _CERT_DICT_TAG = "CERTIFICATE" - _KEY_DICT_TAG = "KEY" - _ENT_DICT_TAG = "ENTITLEMENT" - _SIG_DICT_TAG = "RSA SIGNATURE" - - def __init__(self, cert_file_path: str): - self.path = cert_file_path - self.file_name = os.path.basename(cert_file_path) - - content = self._read(cert_file_path) - self.parts = self._process_content(content) - - def _read(self, file_path: str) -> str: - fd = open(file_path, "r") - file_content = fd.read() - fd.close() - return file_content - - def _process_content(self, content: str) -> Dict[str, str]: - part_dict = {} - matches = self._PATTERN.finditer(content) - for match in matches: - start = match.group(self._REGEX_START_GROUP) - meat = match.group(self._REGEX_CONTENT_GROUP) - end = match.group(self._REGEX_END_GROUP) - - dict_key = None - if not start.find(self._KEY_DICT_TAG) < 0: - dict_key = self._KEY_DICT_TAG - elif not start.find(self._CERT_DICT_TAG) < 0: - dict_key = self._CERT_DICT_TAG - elif not start.find(self._ENT_DICT_TAG) < 0: - dict_key = self._ENT_DICT_TAG - elif not start.find(self._SIG_DICT_TAG) < 0: - dict_key = self._SIG_DICT_TAG - - if dict_key is None: - continue - - part_dict[dict_key] = start + meat + end - return part_dict - - def contains_key_content(self) -> bool: - return self._KEY_DICT_TAG in self.parts - - def get_key_content(self) -> Optional[str]: - key_content = None - if self._KEY_DICT_TAG in self.parts: - key_content = self.parts[self._KEY_DICT_TAG] - return key_content - - def get_cert_content(self) -> str: - cert_content = "" - if self._CERT_DICT_TAG in self.parts: - cert_content = self.parts[self._CERT_DICT_TAG] - if self._ENT_DICT_TAG in self.parts: - cert_content = cert_content + os.linesep + self.parts[self._ENT_DICT_TAG] - if self._SIG_DICT_TAG in self.parts: - cert_content = cert_content + os.linesep + self.parts[self._SIG_DICT_TAG] - return cert_content - - def verify_valid_entitlement(self) -> bool: - """ - Verify that a valid entitlement was processed. - - @return: True if valid, False otherwise. - """ - try: - cert = self.get_cert() - # Don't want to check class explicitly, instead we'll look for - # order info, which only an entitlement cert could have: - if not hasattr(cert, "order"): - return False - except CertificateException: - return False - ent_key = Key(self.get_key_content()) - if ent_key.bogus(): - return False - return True - - # TODO: rewrite to use certlib.EntitlementCertBundleInstall? - def write_to_disk(self) -> None: - """ - Write/copy cert to the entitlement cert dir. - """ - self._ensure_entitlement_dir_exists() - dest_file_path = os.path.join(ENT_CONFIG_DIR, self._create_filename_from_cert_serial_number()) - - # Write the key/cert content to new files - log.debug("Writing certificate file: %s" % (dest_file_path)) - cert_content = self.get_cert_content() - self._write_file(dest_file_path, cert_content) - - if self.contains_key_content(): - dest_key_file_path = self._get_key_path_from_dest_cert_path(dest_file_path) - log.debug("Writing key file: %s" % (dest_key_file_path)) - self._write_file(dest_key_file_path, self.get_key_content()) - - def _write_file(self, target_path: str, content: str) -> None: - new_file = open(target_path, "w") - try: - new_file.write(content) - finally: - new_file.close() - - def _ensure_entitlement_dir_exists(self) -> None: - if not os.access(ENT_CONFIG_DIR, os.R_OK): - os.mkdir(ENT_CONFIG_DIR) - - def _get_key_path_from_dest_cert_path(self, dest_cert_path: str) -> str: - file_parts = os.path.splitext(dest_cert_path) - return file_parts[0] + "-key" + file_parts[1] - - def _create_filename_from_cert_serial_number(self) -> str: - "create from serial" - ent_cert = self.get_cert() - return "%s.pem" % (ent_cert.serial) - - def get_cert(self) -> "EntitlementCertificate": - cert_content: str = self.get_cert_content() - ent_cert: EntitlementCertificate = create_from_pem(cert_content) - return ent_cert - - def _sub_dict(datadict: dict, subkeys: Iterable[str], default: Optional[object] = None) -> dict: """Return a dict that is a subset of datadict matching only the keys in subkeys""" return dict([(k, datadict.get(k, default)) for k in subkeys]) diff --git a/src/subscription_manager/plugins.py b/src/subscription_manager/plugins.py index 650539100c..947464b23b 100644 --- a/src/subscription_manager/plugins.py +++ b/src/subscription_manager/plugins.py @@ -379,7 +379,6 @@ def __init__(self, clazz: Type[SubManPlugin], consumer_uuid: str, pool_id: str, consumer_uuid: the UUID of the consumer being subscribed pool_id: the id of the pool the subscription will come from (None if 'auto' is False) quantity: the quantity to consume from the pool (None if 'auto' is False). - auto: is this an auto-attach/healing event. """ super(SubscriptionConduit, self).__init__(clazz) self.consumer_uuid: str = consumer_uuid @@ -402,33 +401,6 @@ def __init__(self, clazz: Type[SubManPlugin], consumer_uuid: str, entitlement_da self.entitlement_data: Dict = entitlement_data -class AutoAttachConduit(BaseConduit): - slots = ["pre_auto_attach"] - - def __init__(self, clazz: Type[SubManPlugin], consumer_uuid: str): - """ - init for AutoAttachConduit - - Args: - consumer_uuid: the UUID of the consumer being auto-subscribed - """ - super(AutoAttachConduit, self).__init__(clazz) - self.consumer_uuid: str = consumer_uuid - - -class PostAutoAttachConduit(PostSubscriptionConduit): - slots = ["post_auto_attach"] - - def __init__(self, clazz: Type[SubManPlugin], consumer_uuid: str, entitlement_data: Dict): - """init for PostAutoAttachConduit - - Args: - consumer_uuid: the UUID of the consumer subscribed - entitlement_data: the data returned by the server - """ - super(PostAutoAttachConduit, self).__init__(clazz, consumer_uuid, entitlement_data) - - class PluginConfig: """Represents configuation for each rhsm plugin. @@ -917,8 +889,6 @@ def _get_conduits(self) -> List[type(BaseConduit)]: SubscriptionConduit, UpdateContentConduit, PostSubscriptionConduit, - AutoAttachConduit, - PostAutoAttachConduit, ] def _get_modules(self): diff --git a/src/subscription_manager/syspurposelib.py b/src/subscription_manager/syspurposelib.py index d26482fc05..0278747c86 100644 --- a/src/subscription_manager/syspurposelib.py +++ b/src/subscription_manager/syspurposelib.py @@ -42,31 +42,6 @@ syspurpose = None -def save_sla_to_syspurpose_metadata(uep: "UEPConnection", consumer_uuid: str, service_level: str): - """ - 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. - """ - - 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.") - - def get_sys_purpose_store() -> Optional[SyncedStore]: """ :return: Returns a singleton instance of the syspurpose store if it was imported. diff --git a/src/subscription_manager/utils.py b/src/subscription_manager/utils.py index 31c6cbfab1..ce19fd97fe 100644 --- a/src/subscription_manager/utils.py +++ b/src/subscription_manager/utils.py @@ -24,7 +24,7 @@ import socket import syslog import uuid -from typing import Callable, Dict, Iterable, Iterator, Optional, Tuple, TYPE_CHECKING +from typing import Dict, Iterable, Iterator, Optional, Tuple, TYPE_CHECKING import urllib @@ -202,8 +202,7 @@ def is_simple_content_access( # When identity is not known, then system is not registered if identity.uuid is None: return False - content_access_mode = inj.require(inj.CONTENT_ACCESS_MODE_CACHE).read_data(uep=uep) - return content_access_mode == "org_environment" + return True def get_current_owner(uep: Optional["UEPConnection"] = None, identity: "Identity" = None) -> dict: @@ -571,24 +570,6 @@ def print_error(message: str) -> None: sys.stderr.write("\n") -def unique_list_items(items: Iterable, hash_function: Callable = lambda x: x) -> list: - """ - Accepts a list of items. - Returns a list of the unique items in the input. - Maintains order. - """ - observed = set() - unique_items = [] - for item in items: - item_key = hash_function(item) - if item_key in observed: - continue - else: - unique_items.append(item) - observed.add(item_key) - return unique_items - - def generate_correlation_id() -> str: return str(uuid.uuid4()).replace("-", "") # FIXME cp should accept - diff --git a/subscription-manager.spec b/subscription-manager.spec index 16e73515c7..005526e55a 100644 --- a/subscription-manager.spec +++ b/subscription-manager.spec @@ -95,7 +95,7 @@ %global exclude_packages %{exclude_packages}" Name: subscription-manager -Version: 1.30.2 +Version: 1.30.3 Release: 1%{?dist} Summary: Tools and libraries for subscription and repository management %if 0%{?suse_version} @@ -151,7 +151,6 @@ Requires: %{py_package_prefix}-zypp-plugin %else Requires: %{py_package_prefix}-dateutil Requires: %{py_package_prefix}-dbus -Requires: usermode Requires: python3-gobject-base %endif @@ -385,6 +384,7 @@ make -f Makefile install VERSION=%{version}-%{release} \ OS_VERSION=%{?fedora}%{?rhel}%{?suse_version} OS_DIST=%{dist} \ COMPLETION_DIR=%{completion_dir} \ RUN_DIR=%{run_dir} \ + SBIN_DIR=%{_sbindir} \ %{?install_ostree} %{?install_container} \ %{?install_dnf_plugins} \ %{?install_zypper_plugins} \ @@ -457,14 +457,6 @@ find %{buildroot} -name \*.py* -exec touch -r %{SOURCE0} '{}' \; %{_sbindir}/rcrhsm-facts %{_sbindir}/rcrhsmcertd -%else - -# symlink to console-helper -%{_bindir}/subscription-manager -# PAM config -%{_sysconfdir}/pam.d/subscription-manager -%{_sysconfdir}/security/console.apps/subscription-manager - %endif %dir %{python_sitearch}/rhsmlib/candlepin @@ -734,14 +726,57 @@ fi %endif %posttrans +%systemd_posttrans_with_restart rhsm.service # Remove old *.egg-info empty directories not removed be previous versions of RPMs # due to this BZ: https://bugzilla.redhat.com/show_bug.cgi?id=1927245 rmdir %{python_sitearch}/subscription_manager-*-*.egg-info --ignore-fail-on-non-empty # Remove old cache files # The -f flag ensures that exit code 0 will be returned even if the file does not exist. rm -f /var/lib/rhsm/cache/rhsm_icon.json +rm -f /var/lib/rhsm/cache/content_access_mode.json %changelog +* Thu Dec 19 2024 Jiri Hnidek 1.30.3-1 +- Card ID: CCT-731 - integration tests for DBus Register method + (jstavel@redhat.com) +- chore: Remove the --token authentication (pkoprda@redhat.com) +- chore: Remove artifacts of import (stomsa@redhat.com) +- chore: Remove artifacts of redeem (stomsa@redhat.com) +- chore: Remove artifacts of remove (stomsa@redhat.com) +- chore: Remove artifacts of autoheal (glutexo@icloud.com) +- chore: Remove artifacts of --auto-attach (glutexo@icloud.com) +- chore: Remove artifacts of attach (stomsa@redhat.com) +- fix: Added missing python packages (jhnidek@redhat.com) +- fix: Fixed integration tests (jhnidek@redhat.com) +- chore: Remove auto-assign CI job (mhorky@redhat.com) +- feat: Add initial support for tmt (jhnidek@redhat.com) +- fix: Renamed integration-tests to cockpit-tests (jhnidek@redhat.com) +- Feat CCT-965: Include timezone in the logs In `subscription- + manager/src/rhsm/logutil.py`i: (mgrunwal@redhat.com) +- feat: remove content access mode cache (jajerome@redhat.com) +- feat: add GetEnvironments method to DBus register (ryanverdile@gmail.com) +- feat: Added basic configuration for Packit (jhnidek@redhat.com) +- fix: drop "autoattachinterval" from the rhsmcertd defaults + (ptoscano@redhat.com) +- feat/cct-875: Options -i and --cert-interval were removed from rhsmcertd + command autocompletion Options -i and --cert-interval were removed from + rhsmcertd command autocompletion. (mgrunwal@mgrunwal- + thinkpadp1gen3.rmtcz.csb) +- feat/cct-874: Autocompletion for option --servicelevel removed Autocompletion + for option --servicelevel (sub-man register --servicelevel) was removed. + (mgrunwal@mgrunwal-thinkpadp1gen3.rmtcz.csb) +- fix: Handle Retry-After headers better for 429 responses (mhorky@redhat.com) +- feat: Better automatic registration logging (mhorky@redhat.com) +- refactor: Moved some definition of lists from list.py (jhnidek@redhat.com) +- fix: Removed show_autosubscribe_output() method (jhnidek@redhat.com) +- feat: Remove useless CLI options from list command (jhnidek@redhat.com) +- fix: perform autoreg waiting when performing standard autoreg + (ptoscano@redhat.com) +- chore: move autoreg waiting code in own function (ptoscano@redhat.com) +- feat: support registering specifying environments with activation keys + (ryanverdile@gmail.com) +- feat: Remove autoheal functionality from rhsmcertd (jvlcek@redhat.com) + * Thu Sep 26 2024 Pino Toscano 1.30.2-1 - Translated using Weblate (Georgian) (temuri.doghonadze@gmail.com) - feat: Create consumer cert & key owner by rhsm group (jhnidek@redhat.com) diff --git a/systemtest/copr-setup.sh b/systemtest/copr-setup.sh new file mode 100644 index 0000000000..b62fd60500 --- /dev/null +++ b/systemtest/copr-setup.sh @@ -0,0 +1,16 @@ +#!/usr/bin/bash -eux +dnf install -y dnf-plugins-core + +# Determine the repo needed from copr +source /etc/os-release + +if [ "$ID" == "centos" ]; then + ID='centos-stream' +fi +VERSION_MAJOR=$(echo "${VERSION_ID}" | cut -d '.' -f 1) +COPR_REPO="${ID}-${VERSION_MAJOR}-$(uname -m)" + +# Install subscription-manager from COPR repository +dnf remove -y --noautoremove subscription-manager +dnf copr -y enable packit/candlepin-subscription-manager-"${ghprbPullId}" "${COPR_REPO}" +dnf install -y subscription-manager --disablerepo=* --enablerepo=*subscription-manager* diff --git a/systemtest/plans/main.fmf b/systemtest/plans/main.fmf new file mode 100644 index 0000000000..af4bed151e --- /dev/null +++ b/systemtest/plans/main.fmf @@ -0,0 +1,5 @@ +summary: rhsm test suite +discover: + how: fmf +execute: + how: tmt diff --git a/systemtest/tests/integration/main.fmf b/systemtest/tests/integration/main.fmf new file mode 100644 index 0000000000..9d6c3e5b57 --- /dev/null +++ b/systemtest/tests/integration/main.fmf @@ -0,0 +1,3 @@ +summary: Runs tmt tests +test: ./test.sh +duration: 1h diff --git a/systemtest/tests/integration/test.sh b/systemtest/tests/integration/test.sh new file mode 100755 index 0000000000..7349616c45 --- /dev/null +++ b/systemtest/tests/integration/test.sh @@ -0,0 +1,62 @@ +#!/bin/bash +set -ux + +# get to project root +cd ../../../ + +# Check for GitHub pull request ID and install build if needed. +# This is for the downstream PR jobs. +[ -z "${ghprbPullId+x}" ] || ./systemtest/copr-setup.sh + +dnf --setopt install_weak_deps=False install -y \ + podman git-core python3-pip python3-pytest logrotate \ + cairo-gobject-devel gobject-introspection-devel \ + python3-gobject python3-devel + +yum -y groupinstall 'Development Tools' + +python3 -m venv venv +# shellcheck disable=SC1091 +. venv/bin/activate + +# Install requirements for integration tests +pip install -r integration-tests/requirements.txt + +# configuration for the tests +cat < settings.toml +[testing] +candlepin.host = "localhost" +candlepin.port = 8443 +candlepin.insecure = true +candlepin.prefix = "/candlepin" +candlepin.username = "duey" +candlepin.password = "password" +candlepin.org = "donaldduck" +candlepin.activation_keys = ["act-key-01","act-key-02"] +candlepin.environment.names = ["env-name-01","env-name-02"] +candlepin.environment.ids = ["env-id-01","env-id-02"] +EOF + + +# run local candlepin for testing purpose +./integration-tests/scripts/run-local-candlepin.sh + +# create testing data in local candlepin +./integration-tests/scripts/post-activation-keys.sh +./integration-tests/scripts/post-environments.sh + +# There is a problem with SELinux in current version of selinux-roles (for rhsm.service) +# it is a temporary fix +setenforce 0 + +# Run all integration tests. They will use 'testing' environment in configuration +ENV_FOR_DYNACONF=testing pytest --junit-xml=./junit.xml -v integration-tests +retval=$? + +# Copy artifacts of integration tests +if [ -d "$TMT_PLAN_DATA" ]; then + cp ./junit.xml "$TMT_PLAN_DATA/junit.xml" +fi + +# Return exit code of integration tests +exit $retval diff --git a/test/cli_command/test_identity.py b/test/cli_command/test_identity.py index 8826584e79..3d75060e76 100644 --- a/test/cli_command/test_identity.py +++ b/test/cli_command/test_identity.py @@ -7,9 +7,3 @@ class TestIdentityCommand(TestCliProxyCommand): def test_regenerate_no_force(self): self.cc.main(["--regenerate"]) - - def test_token_no_force(self): - self._test_exception(["--token", "eyJhbGciOiJSUzI1NiIsInR5cCIg"]) - - def test_token_with_force(self): - self._test_no_exception(["--regenerate", "--token", "eyJhbGciOiJSUzI1NiIsInR5cCIg", "--force"]) diff --git a/test/cli_command/test_owners.py b/test/cli_command/test_owners.py index 390ca82f17..f5f8bf89ec 100644 --- a/test/cli_command/test_owners.py +++ b/test/cli_command/test_owners.py @@ -11,6 +11,3 @@ def test_main_server_url(self): def test_insecure(self): self.cc.main(["--insecure"]) - - def test_token_(self): - self.cc.main(["--token", "eyJhbGciOiJSUzI1NiIsInR5cCIg"]) diff --git a/test/cli_command/test_refresh.py b/test/cli_command/test_refresh.py index 9866b51580..223d4a6bcc 100644 --- a/test/cli_command/test_refresh.py +++ b/test/cli_command/test_refresh.py @@ -2,8 +2,8 @@ from ..test_managercli import TestCliProxyCommand from unittest.mock import Mock from subscription_manager import managercli -from subscription_manager.cache import ContentAccessCache, ContentAccessModeCache -from subscription_manager.injection import provide, CONTENT_ACCESS_CACHE, CONTENT_ACCESS_MODE_CACHE +from subscription_manager.cache import ContentAccessCache +from subscription_manager.injection import provide, CONTENT_ACCESS_CACHE class TestRefreshCommand(TestCliProxyCommand): @@ -28,15 +28,8 @@ def test_cache_removed(self): mock_content_access_cache = Mock(spec=ContentAccessCache) mock_content_access_cache.return_value.exists.return_value = True provide(CONTENT_ACCESS_CACHE, mock_content_access_cache) - mock_content_access_mode_cache = Mock(spec=ContentAccessModeCache) - mock_content_access_mode_cache.return_value.exists.return_value = True - provide(CONTENT_ACCESS_MODE_CACHE, mock_content_access_mode_cache) self.cc.main([]) # This cache should not be deleted to be able to use HTTP header 'If-Modified-Since' mock_content_access_cache.return_value.remove.assert_not_called() - # Cache about content access mode should be deleted, because content access mode - # can be changed from SCA to entitlement and vice versa - mock_content_access_mode_cache.return_value.exists.assert_called_once() - mock_content_access_mode_cache.return_value.delete_cache.assert_called_once() diff --git a/test/cli_command/test_register.py b/test/cli_command/test_register.py index 058aee8bff..b0d811af5c 100644 --- a/test/cli_command/test_register.py +++ b/test/cli_command/test_register.py @@ -65,6 +65,3 @@ def test_insecure(self): with patch.object(self.mock_cfg_parser, "save") as mock_save: self._test_no_exception(["--insecure"]) mock_save.assert_called_with() - - def test_token(self): - self._test_no_exception(["--token", "eyJhbGciOiJSUzI1NiIsInR5cCIg"]) diff --git a/test/cli_command/test_role.py b/test/cli_command/test_role.py index edeee97986..b68a628263 100644 --- a/test/cli_command/test_role.py +++ b/test/cli_command/test_role.py @@ -63,7 +63,6 @@ def test_list_only_username(self): self.cc.options.to_add = False self.cc.options.to_remove = False self.cc.options.list = True - self.cc.options.token = None self.cc.options.username = "admin" self.cc.options.password = None try: @@ -71,22 +70,6 @@ def test_list_only_username(self): except SystemExit as e: self.assertEqual(e.code, os.EX_USAGE) - def test_list_username_and_token(self): - self.cc.options = Mock() - self.cc.is_registered = Mock(return_value=False) - self.cc.options.set = False - self.cc.options.unset = False - self.cc.options.to_add = False - self.cc.options.to_remove = False - self.cc.options.list = True - self.cc.options.token = "TOKEN" - self.cc.options.username = "admin" - self.cc.options.password = "secret" - try: - self.cc._validate_options() - except SystemExit as e: - self.assertEqual(e.code, os.EX_USAGE) - def test_wrong_options_syspurpose_role(self): """It is possible to use --set or --unset options. It's not possible to use both of them together.""" self.cc.options = Mock() @@ -131,22 +114,6 @@ def test_password_on_registered_system(self): except SystemExit as e: self.assertEqual(e.code, os.EX_USAGE) - def test_token_on_registered_system(self): - """Argument --token cannot be used on registered system.""" - self.cc.is_registered = Mock(return_value=True) - self.cc.options = Mock() - self.cc.options.set = None - self.cc.options.unset = None - self.cc.options.to_add = None - self.cc.options.to_remove = None - self.cc.options.show = None - self.cc.options.list = True - self.cc.options.token = "TOKEN" - try: - self.cc._validate_options() - except SystemExit as e: - self.assertEqual(e.code, os.EX_USAGE) - def test_org_on_registered_system(self): """Argument --org cannot be used on registered system.""" self.cc.is_registered = Mock(return_value=True) diff --git a/test/cli_command/test_service_level.py b/test/cli_command/test_service_level.py index 01df66e1f4..17b2448cda 100644 --- a/test/cli_command/test_service_level.py +++ b/test/cli_command/test_service_level.py @@ -145,22 +145,6 @@ def test_password_on_registered_system(self): except SystemExit as e: self.assertEqual(e.code, os.EX_USAGE) - def test_token_on_registered_system(self): - """Argument --token cannot be used on registered system.""" - self.cc.is_registered = Mock(return_value=True) - self.cc.options = Mock() - self.cc.options.set = None - self.cc.options.unset = None - self.cc.options.to_add = None - self.cc.options.to_remove = None - self.cc.options.show = None - self.cc.options.list = True - self.cc.options.token = "TOKEN" - try: - self.cc._validate_options() - except SystemExit as e: - self.assertEqual(e.code, os.EX_USAGE) - def test_org_on_registered_system(self): """Argument --org cannot be used on registered system.""" self.cc.is_registered = Mock(return_value=True) diff --git a/test/data/anaconda-ks.cfg b/test/data/anaconda-ks.cfg index b98f7a434c..1f843b4547 100644 --- a/test/data/anaconda-ks.cfg +++ b/test/data/anaconda-ks.cfg @@ -59,9 +59,6 @@ clearpart --all --initlabel --drives=sda # Yeah, strip()'ing passwords seems like a bad idea. password = password - auto-attach = True - # If we should attempt to auto-attach - servicelevel = Premium %end diff --git a/test/fixture.py b/test/fixture.py index ed927de81d..e15bfd044a 100644 --- a/test/fixture.py +++ b/test/fixture.py @@ -203,7 +203,6 @@ def unstub_conf(): inj.provide(inj.SUPPORTED_RESOURCES_CACHE, stubs.StubSupportedResourcesCache()) inj.provide(inj.SYSPURPOSE_VALID_FIELDS_CACHE, stubs.StubSyspurposeValidFieldsCache()) inj.provide(inj.CURRENT_OWNER_CACHE, stubs.StubCurrentOwnerCache) - inj.provide(inj.CONTENT_ACCESS_MODE_CACHE, stubs.StubContentAccessModeCache()) inj.provide(inj.OVERRIDE_STATUS_CACHE, stubs.StubOverrideStatusCache()) inj.provide(inj.RELEASE_STATUS_CACHE, stubs.StubReleaseStatusCache()) inj.provide(inj.AVAILABLE_ENTITLEMENT_CACHE, stubs.StubAvailableEntitlementsCache()) diff --git a/test/functional_tests/README.md b/test/functional_tests/README.md index 02c5e2cf7b..231e8b1562 100644 --- a/test/functional_tests/README.md +++ b/test/functional_tests/README.md @@ -24,7 +24,6 @@ How to run functional tests? cd ./ansible_playbooks ansible-playbook ./configure_package_manager.yml --extra-vars="candlepin_hostname=" ansible-playbook ./register_system.yml - ansible-playbook ./attach_subscriptions.yml ansible-playbook ./test_install_remove_packages.yml ansible-playbook ./test_not_remove_prod_cert_for_disabled_repo.yml ``` diff --git a/test/functional_tests/ansible_playbooks/attach_subscriptions.yml b/test/functional_tests/ansible_playbooks/attach_subscriptions.yml deleted file mode 100644 index e0bb6152d9..0000000000 --- a/test/functional_tests/ansible_playbooks/attach_subscriptions.yml +++ /dev/null @@ -1,23 +0,0 @@ ---- -- hosts: clients - vars: - subscription_skus: ['awesomeos-x86_64', 'awesomeos-all-x86-cont'] - remote_user: root - tasks: - - # Try to find pool ids - - name: try to find pool ids - shell: subscription-manager list --available --matches={{ item }} | gawk '/Pool ID:/{ print $3; exit }' - register: cmd_output - with_items: "{{ subscription_skus }}" - - - set_fact: - pool_ids: "{{ cmd_output | json_query('results[*].stdout_lines[0]') }}" - - - debug: - msg: "{{ pool_ids }}" - - - name: attach pools - shell: subscription-manager attach --pool {{ item }} - with_items: "{{ pool_ids }}" - diff --git a/test/rhsm/functional/test_connection.py b/test/rhsm/functional/test_connection.py index 7660ed5ca5..4f7d38f445 100644 --- a/test/rhsm/functional/test_connection.py +++ b/test/rhsm/functional/test_connection.py @@ -22,7 +22,6 @@ from rhsm.connection import ( ContentConnection, UEPConnection, - BaseRestLib, UnauthorizedException, ForbiddenException, RestlibException, @@ -121,40 +120,6 @@ def tearDown(self): self.cp.unregisterConsumer(self.consumer_uuid) -@subman_marker_functional -class BindRequestTests(unittest.TestCase): - def setUp(self): - self.cp = UEPConnection(username="admin", password="admin", insecure=True) - - consumerInfo = self.cp.registerConsumer("test-consumer", "system", owner="admin") - self.consumer_uuid = consumerInfo["uuid"] - - @patch.object(BaseRestLib, "validateResult") - @patch("rhsm.connection.drift_check", return_value=False) - @patch("httplib.HTTPSConnection", autospec=True) - def test_bind_no_args(self, mock_conn, mock_drift, mock_validate): - self.cp.bind(self.consumer_uuid) - - # verify we called request() with kwargs that include 'body' as None - # Specifically, we are checking that passing in "" to post_request, as - # it does by default, results in None here. bin() passes no args there - # so we use the default, "". See bz #907536 - for name, args, kwargs in mock_conn.mock_calls: - if name == "().request": - self.assertEqual(None, kwargs["body"]) - - @patch.object(BaseRestLib, "validateResult") - @patch("rhsm.connection.drift_check", return_value=False) - @patch("httplib.HTTPSConnection", autospec=True) - def test_bind_by_pool(self, mock_conn, mock_drift, mock_validate): - # this test is just to verify we make the httplib connection with - # right args, we don't validate the bind here - self.cp.bindByEntitlementPool(self.consumer_uuid, "123121111", "1") - for name, args, kwargs in mock_conn.mock_calls: - if name == "().request": - self.assertEqual(None, kwargs["body"]) - - @subman_marker_functional class ContentConnectionTests(unittest.TestCase): def testInsecure(self): diff --git a/test/rhsm/unit/test_connection.py b/test/rhsm/unit/test_connection.py index e57da87a91..be562b5fd8 100644 --- a/test/rhsm/unit/test_connection.py +++ b/test/rhsm/unit/test_connection.py @@ -43,7 +43,6 @@ import subscription_manager.injection as inj from unittest.mock import Mock, patch, mock_open -from datetime import date from rhsm import ourjson as json from collections import namedtuple @@ -207,24 +206,6 @@ def test_has_proper_language_header_not_utf8(self, mock_locale): self.cp.conn._set_accept_language_in_header() self.assertEqual(self.cp.conn.headers["Accept-Language"], "ja-jp") - def test_entitle_date(self): - self.cp.conn = Mock() - self.cp.conn.request_post = Mock(return_value=[]) - self.cp.bind("abcd", date(2011, 9, 2)) - self.cp.conn.request_post.assert_called_with( - "/consumers/abcd/entitlements?entitle_date=2011-09-02", - description="Updating subscriptions", - ) - - def test_no_entitle_date(self): - self.cp.conn = Mock() - self.cp.conn.request_post = Mock(return_value=[]) - self.cp.bind("abcd") - self.cp.conn.request_post.assert_called_with( - "/consumers/abcd/entitlements", - description="Updating subscriptions", - ) - def test_clean_up_prefix(self): self.assertTrue(self.cp.handler == "/Test/") diff --git a/test/rhsmlib/dbus/test_entitlement.py b/test/rhsmlib/dbus/test_entitlement.py index 21fb477c5d..fc0ddad0cb 100644 --- a/test/rhsmlib/dbus/test_entitlement.py +++ b/test/rhsmlib/dbus/test_entitlement.py @@ -34,77 +34,3 @@ def test_get_status(self): result = self.impl.get_status("") self.assertEqual(expected, result) - - def test_remove_entitlements_by_serials(self): - remove_entitlements_by_serials_patch = mock.patch( - "rhsmlib.services.entitlement.EntitlementService.remove_entitlements_by_serials", - name="remove_entitlements_by_serials", - ) - self.patches["remove_entitlements_by_serials"] = remove_entitlements_by_serials_patch.start() - self.addCleanup(remove_entitlements_by_serials_patch.stop) - - removed_nonremoved = (["123"], []) - self.patches["remove_entitlements_by_serials"].return_value = removed_nonremoved - - expected = removed_nonremoved[0] - result = self.impl.remove_entitlements_by_serials(["123"], {}) - self.assertEqual(expected, result) - - def test_remove_entitlements_by_serials__multiple(self): - remove_entitlements_by_serials_patch = mock.patch( - "rhsmlib.services.entitlement.EntitlementService.remove_entitlements_by_serials", - name="remove_entitlements_by_serials", - ) - self.patches["remove_entitlements_by_serials"] = remove_entitlements_by_serials_patch.start() - self.addCleanup(remove_entitlements_by_serials_patch.stop) - - removed_nonremoved = (["123", "456"], []) - self.patches["remove_entitlements_by_serials"].return_value = removed_nonremoved - - expected = removed_nonremoved[0] - result = self.impl.remove_entitlements_by_serials(["123", "456"], {}) - self.assertEqual(expected, result) - - def test_remove_entitlements_by_serials__good_and_bad(self): - remove_entitlements_by_serials_patch = mock.patch( - "rhsmlib.services.entitlement.EntitlementService.remove_entitlements_by_serials", - name="remove_entitlements_by_serials", - ) - self.patches["remove_entitlements_by_serials"] = remove_entitlements_by_serials_patch.start() - self.addCleanup(remove_entitlements_by_serials_patch.stop) - - removed_nonremoved = (["123"], ["456"]) - self.patches["remove_entitlements_by_serials"].return_value = removed_nonremoved - - expected = removed_nonremoved[0] - result = self.impl.remove_entitlements_by_serials(["123", "789"], {}) - self.assertEqual(expected, result) - - def test_remove_entitlements_by_pool_ids(self): - remove_entitlements_by_pool_ids_patch = mock.patch( - "rhsmlib.services.entitlement.EntitlementService.remove_entitlements_by_pool_ids", - name="remove_entitlements_by_pool_ids", - ) - self.patches["remove_entitlements_by_pool_ids"] = remove_entitlements_by_pool_ids_patch.start() - self.addCleanup(remove_entitlements_by_pool_ids_patch.stop) - - removed_nonremoved_serials = (["123"], [], ["456"]) - self.patches["remove_entitlements_by_pool_ids"].return_value = removed_nonremoved_serials - - expected = removed_nonremoved_serials[2] - result = self.impl.remove_entitlements_by_pool_ids(["123"], {}) - self.assertEqual(expected, result) - - def test_remove_all_entitlements(self): - remove_all_entitlements_patch = mock.patch( - "rhsmlib.services.entitlement.EntitlementService.remove_all_entitlements", - name="remove_all_entitlements", - ) - self.patches["remove_all_entitlements"] = remove_all_entitlements_patch.start() - self.addCleanup(remove_all_entitlements_patch.stop) - - records = {"deletedRecords": 1} - self.patches["remove_all_entitlements"].return_value = records - - result = self.impl.remove_all_entitlements({}) - self.assertEqual(records, result) diff --git a/test/rhsmlib/dbus/test_register.py b/test/rhsmlib/dbus/test_register.py index c9b5aceef7..3715d17c25 100644 --- a/test/rhsmlib/dbus/test_register.py +++ b/test/rhsmlib/dbus/test_register.py @@ -123,6 +123,77 @@ ] """ +ENVIRONMENTS_CONTENT_JSON = """[ + { + "created": "2024-11-07T20:01:47+0000", + "updated": "2024-11-07T20:01:47+0000", + "id": "fake-id", + "name": "test-environment", + "description": "test description", + "contentPrefix": null, + "type": "content-template", + "environmentContent": [] + }, + { + "created": "2024-11-07T20:01:47+0000", + "updated": "2024-11-07T20:01:47+0000", + "id": "fake-id-2", + "name": "test-environment-2", + "description": "test description", + "contentPrefix": null, + "type": "content-template", + "environmentContent": [] + }, + { + "created": "2024-11-07T20:01:47+0000", + "updated": "2024-11-07T20:01:47+0000", + "id": "fake-id-3", + "name": "test-environment-3", + "description": "test description", + "contentPrefix": null, + "type": null, + "environmentContent": [] + }, + { + "created": "2024-11-07T20:01:47+0000", + "updated": "2024-11-07T20:01:47+0000", + "id": "fake-id-4", + "name": "test-environment-4", + "description": "test description", + "contentPrefix": null, + "environmentContent": [] + } +] +""" + +ENVIRONMENTS_DBUS_JSON = """[ + { + "id": "fake-id", + "name": "test-environment", + "description": "test description", + "type": "content-template" + }, + { + "id": "fake-id-2", + "name": "test-environment-2", + "description": "test description", + "type": "content-template" + }, + { + "id": "fake-id-3", + "name": "test-environment-3", + "description": "test description", + "type": "" + }, + { + "id": "fake-id-4", + "name": "test-environment-4", + "description": "test description", + "type": "" + } +] +""" + class RegisterDBusObjectTest(SubManDBusFixture): socket_dir: Optional[tempfile.TemporaryDirectory] = None @@ -308,6 +379,21 @@ def test_GetOrgs(self): result = self.impl.get_organizations({"username": "username", "password": "password"}) self.assertEqual(expected, result) + def test_GetEnvironments(self): + self.patches["is_registered"].return_value = False + mock_cp = mock.Mock(spec=connection.UEPConnection, name="UEPConnection") + mock_cp.username = "username" + mock_cp.password = "password" + mock_cp.getEnvironmentList = mock.Mock() + mock_cp.getEnvironmentList.return_value = json.loads(ENVIRONMENTS_CONTENT_JSON) + self.patches["build_uep"].return_value = mock_cp + + expected = json.loads(ENVIRONMENTS_DBUS_JSON) + result = self.impl.get_environments( + {"username": "username", "password": "password", "org_id": "org_id"} + ) + self.assertEqual(expected, result) + def test_RegisterWithActivationKeys(self): expected = json.loads(CONSUMER_CONTENT_JSON_SCA) self.patches["is_registered"].return_value = False diff --git a/test/rhsmlib/services/test_entitlement.py b/test/rhsmlib/services/test_entitlement.py index 21beb85fbe..0c5a754ef2 100644 --- a/test/rhsmlib/services/test_entitlement.py +++ b/test/rhsmlib/services/test_entitlement.py @@ -289,172 +289,6 @@ def test_no_pool_with_specified_filter(self, mock_managerlib): filtered = service.get_available_pools(service_level="NotFound") self.assertEqual(0, len(filtered)) - def test_remove_all_pools(self): - """ - Test of removing all pools - """ - ent_service = EntitlementService(self.mock_cp) - ent_service.entcertlib = mock.Mock().return_value - ent_service.entcertlib.update = mock.Mock() - ent_service.cp.unbindAll = mock.Mock(return_value="[]") - - response = ent_service.remove_all_entitlements() - self.assertEqual(response, "[]") - - def test_remove_all_pools_by_id(self): - """ - Test of removing all pools by IDs of pool - """ - ent_service = EntitlementService(self.mock_cp) - ent_service.cp.unbindByPoolId = mock.Mock() - ent_service.entitlement_dir.list_serials_for_pool_ids = mock.Mock( - return_value={ - "4028fa7a5dea087d015dea0b025003f6": ["6219625278114868779"], - "4028fa7a5dea087d015dea0adf560152": ["3573249574655121394"], - } - ) - ent_service.entcertlib = mock.Mock().return_value - ent_service.entcertlib.update = mock.Mock() - - removed_pools, unremoved_pools, removed_serials = ent_service.remove_entitlements_by_pool_ids( - ["4028fa7a5dea087d015dea0b025003f6", "4028fa7a5dea087d015dea0adf560152"] - ) - - expected_removed_serials = ["6219625278114868779", "3573249574655121394"] - expected_removed_pools = ["4028fa7a5dea087d015dea0b025003f6", "4028fa7a5dea087d015dea0adf560152"] - - self.assertEqual(expected_removed_serials, removed_serials) - self.assertEqual(expected_removed_pools, removed_pools) - self.assertEqual([], unremoved_pools) - - def test_remove_dupli_pools_by_id(self): - """ - Test of removing pools specified with duplicities - (one pool id is set twice) - """ - ent_service = EntitlementService(self.mock_cp) - ent_service.cp.unbindByPoolId = mock.Mock() - ent_service.entitlement_dir.list_serials_for_pool_ids = mock.Mock( - return_value={ - "4028fa7a5dea087d015dea0b025003f6": ["6219625278114868779"], - "4028fa7a5dea087d015dea0adf560152": ["3573249574655121394"], - } - ) - ent_service.entcertlib = mock.Mock().return_value - ent_service.entcertlib.update = mock.Mock() - - removed_pools, unremoved_pools, removed_serials = ent_service.remove_entitlements_by_pool_ids( - [ - "4028fa7a5dea087d015dea0b025003f6", - "4028fa7a5dea087d015dea0b025003f6", - "4028fa7a5dea087d015dea0adf560152", - ] - ) - - expected_removed_serials = ["6219625278114868779", "3573249574655121394"] - expected_removed_pools = ["4028fa7a5dea087d015dea0b025003f6", "4028fa7a5dea087d015dea0adf560152"] - - self.assertEqual(expected_removed_serials, removed_serials) - self.assertEqual(expected_removed_pools, removed_pools) - self.assertEqual([], unremoved_pools) - - def test_remove_some_pools_by_id(self): - """ - Test of removing only some pools, because one pool ID is not valid - """ - ent_service = EntitlementService(self.mock_cp) - - def stub_unbind(uuid, pool_id): - if pool_id == "does_not_exist_d015dea0adf560152": - raise connection.RestlibException(400, "Error") - - ent_service.cp.unbindByPoolId = mock.Mock(side_effect=stub_unbind) - ent_service.entitlement_dir.list_serials_for_pool_ids = mock.Mock( - return_value={ - "4028fa7a5dea087d015dea0b025003f6": ["6219625278114868779"], - "4028fa7a5dea087d015dea0adf560152": ["3573249574655121394"], - } - ) - ent_service.entcertlib = mock.Mock().return_value - ent_service.entcertlib.update = mock.Mock() - - removed_pools, unremoved_pools, removed_serials = ent_service.remove_entitlements_by_pool_ids( - ["4028fa7a5dea087d015dea0b025003f6", "does_not_exist_d015dea0adf560152"] - ) - - expected_removed_serials = ["6219625278114868779"] - expected_removed_pools = ["4028fa7a5dea087d015dea0b025003f6"] - expected_unremoved_pools = ["does_not_exist_d015dea0adf560152"] - - self.assertEqual(expected_removed_serials, removed_serials) - self.assertEqual(expected_removed_pools, removed_pools) - self.assertEqual(expected_unremoved_pools, unremoved_pools) - - def test_remove_all_pools_by_serial(self): - """ - Test of removing all pools by serial numbers - """ - ent_service = EntitlementService(self.mock_cp) - ent_service.cp.unbindBySerial = mock.Mock() - - ent_service.entcertlib = mock.Mock().return_value - ent_service.entcertlib.update = mock.Mock() - - removed_serial, unremoved_serials = ent_service.remove_entitlements_by_serials( - ["6219625278114868779", "3573249574655121394"] - ) - - expected_removed_serials = ["6219625278114868779", "3573249574655121394"] - - self.assertEqual(expected_removed_serials, removed_serial) - self.assertEqual([], unremoved_serials) - - def test_remove_dupli_pools_by_serial(self): - """ - Test of removing pools specified with duplicities - (one serial number is set twice) - """ - ent_service = EntitlementService(self.mock_cp) - ent_service.cp.unbindBySerial = mock.Mock() - - ent_service.entcertlib = mock.Mock().return_value - ent_service.entcertlib.update = mock.Mock() - - removed_serial, unremoved_serials = ent_service.remove_entitlements_by_serials( - ["6219625278114868779", "6219625278114868779", "3573249574655121394"] - ) - - expected_removed_serials = ["6219625278114868779", "3573249574655121394"] - - self.assertEqual(expected_removed_serials, removed_serial) - self.assertEqual([], unremoved_serials) - - def test_remove_some_pools_by_serial(self): - """ - Test of removing some of pools by serial numbers, because one serial - number is not valid. - """ - ent_service = EntitlementService(self.mock_cp) - - def stub_unbind(uuid, serial): - if serial == "does_not_exist_1394": - raise connection.RestlibException(400, "Error") - - ent_service.cp.unbindBySerial = mock.Mock(side_effect=stub_unbind) - - ent_service.entcertlib = mock.Mock().return_value - ent_service.entcertlib.update = mock.Mock() - - removed_serial, unremoved_serials = ent_service.remove_entitlements_by_serials( - ["6219625278114868779", "does_not_exist_1394"] - ) - - expected_removed_serials = ["6219625278114868779"] - expected_unremoved_serials = ["does_not_exist_1394"] - - self.assertEqual(expected_removed_serials, removed_serial) - self.assertEqual(expected_unremoved_serials, unremoved_serials) - def test_parse_valid_date(self): """ Test parsing valid date diff --git a/test/rhsmlib/services/test_environment.py b/test/rhsmlib/services/test_environment.py new file mode 100644 index 0000000000..1abc418ba9 --- /dev/null +++ b/test/rhsmlib/services/test_environment.py @@ -0,0 +1,77 @@ +# Copyright (c) 2024 Red Hat, Inc. +# +# This software is licensed to you under the GNU General Public License, +# version 2 (GPLv2). There is NO WARRANTY for this software, express or +# implied, including the implied warranties of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 +# along with this software; if not, see +# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. +# +# Red Hat trademarks are not licensed under GPLv2. No permission is +# granted to use or replicate Red Hat trademarks that are incorporated +# in this software or its documentation. +from unittest import mock + +from test.rhsmlib.base import InjectionMockingTest + +from rhsm import connection + +from rhsmlib.services import environment + + +ENVIRONMENTS_JSON = [ + { + "created": "2024-10-03T18:12:56+0000", + "updated": "2024-10-03T18:12:56+0000", + "id": "8bdf14cf9e534119a1fe617c03304768", + "name": "template 1", + "type": "content-template", + "description": "my template", + "owner": { + "id": "8ad980939253781c01925378340e0002", + "key": "content-sources-test", + "displayName": "ContentSourcesTest", + "href": "/owners/content-sources-test", + "contentAccessMode": "org_environment", + }, + "environmentContent": [ + {"contentId": "11055", "enabled": True}, + {"contentId": "56a3a98c76ea4e16bd68424a2c9cc1c1", "enabled": True}, + {"contentId": "11049", "enabled": True}, + ], + }, + { + "created": "2024-10-09T19:08:14+0000", + "updated": "2024-10-09T19:08:14+0000", + "id": "6c62889601be41128fe2fece53141fd4", + "name": "template 2", + "type": "content-template", + "description": "my template", + "owner": { + "id": "8ad980939253781c01925378340e0002", + "key": "content-sources-test", + "displayName": "ContentSourcesTest", + "href": "/owners/content-sources-test", + "contentAccessMode": "org_environment", + }, + "environmentContent": [ + {"contentId": "11055", "enabled": True}, + {"contentId": "11049", "enabled": True}, + ], + }, +] + + +class TestEnvironmentService(InjectionMockingTest): + def setUp(self): + super(TestEnvironmentService, self).setUp() + self.mock_cp = mock.Mock(spec=connection.UEPConnection, name="UEPConnection") + + def injection_definitions(self, *args, **kwargs): + return None + + def test_list_environments(self): + self.mock_cp.getEnvironmentList.return_value = ENVIRONMENTS_JSON + + result = environment.EnvironmentService(self.mock_cp).list("org") + self.assertEqual(ENVIRONMENTS_JSON, result) diff --git a/test/rhsmlib/services/test_register.py b/test/rhsmlib/services/test_register.py index 4eccc814dd..e047a81fb9 100644 --- a/test/rhsmlib/services/test_register.py +++ b/test/rhsmlib/services/test_register.py @@ -16,7 +16,7 @@ import subscription_manager.injection as inj -from subscription_manager.cache import InstalledProductsManager, ContentAccessModeCache +from subscription_manager.cache import InstalledProductsManager from subscription_manager.cp_provider import CPProvider from subscription_manager.facts import Facts from subscription_manager.identity import Identity @@ -60,14 +60,157 @@ "facts": {}, "id": "ff808081550d997c015511b0406d1065", "uuid": "c1b8648c-6f0a-4aa5-b34e-b9e62c0e4364", "guestIds": null, "capabilities": null, - "environment": null, "installedProducts": null, + "environment": { + "created" : "2024-12-09T09:25:17+0000", + "updated" : "2024-12-09T09:25:17+0000", + "id" : "env-id-1", + "name" : "env-name-1", + "type" : "content-template", + "description" : "Testing environment #1", + "contentPrefix" : null, + "owner" : { + "id" : "ff808081550d997c01550d9adaf40003", + "key" : "admin", + "displayName" : "Admin Owner", + "href" : "/owners/admin", + "contentAccessMode" : "org_environment" + }, + "environmentContent" : [ ] + }, + "installedProducts": null, "canActivate": false, "type": {"manifest": false, "id": "1000", "label": "system"}, "annotations": null, "username": "admin", "updated": "2016-06-02T15:16:51+0000", "lastCheckin": null, "entitlementCount": 0, "releaseVer": {"releaseVer": null}, "entitlementStatus": "valid", "name": "test.example.com", "created": "2016-06-02T15:16:51+0000", - "contentTags": null, "dev": false}""" + "contentTags": null, + "dev": false, + "environments": [ { + "created" : "2024-12-09T09:25:17+0000", + "updated" : "2024-12-09T09:25:17+0000", + "id" : "env-id-1", + "name" : "env-name-1", + "type" : "content-template", + "description" : "Testing environment #1", + "contentPrefix" : null, + "owner" : { + "id" : "ff808081550d997c01550d9adaf40003", + "key" : "admin", + "displayName" : "Admin Owner", + "href" : "/owners/admin", + "contentAccessMode" : "org_environment" + }, + "environmentContent" : [ ] + }, + { + "created" : "2024-12-09T09:25:17+0000", + "updated" : "2024-12-09T09:25:17+0000", + "id" : "env-id-2", + "name" : "env-name-2", + "type" : "content-template", + "description" : "Testing environment #2", + "contentPrefix" : null, + "owner" : { + "id" : "ff808081550d997c01550d9adaf40003", + "key" : "admin", + "displayName" : "Admin Owner", + "href" : "/owners/admin", + "contentAccessMode" : "org_environment" + }, + "environmentContent" : [ ] + } ] }""" + +CONSUMER_CONTENT_JSON_WRONG_ENT_TYPE = """{"hypervisorId": null, + "serviceLevel": "", + "autoheal": true, + "idCert": { + "key": "FAKE_KEY", + "cert": "FAKE_CERT", + "serial" : { + "id" : 5196045143213189102, + "revoked" : false, + "collected" : false, + "expiration" : "2033-04-25T18:03:06+0000", + "serial" : 5196045143213189102, + "created" : "2017-04-25T18:03:06+0000", + "updated" : "2017-04-25T18:03:06+0000" + }, + "id" : "8a8d011e5ba64700015ba647fbd20b88", + "created" : "2017-04-25T18:03:07+0000", + "updated" : "2017-04-25T18:03:07+0000" + }, + "owner": { + "href": "/owners/admin", + "displayName": "Admin Owner", + "id": "ff808081550d997c01550d9adaf40003", + "key": "admin", + "contentAccessMode": "org_environment" + }, + "href": "/consumers/c1b8648c-6f0a-4aa5-b34e-b9e62c0e4364", + "facts": {}, "id": "ff808081550d997c015511b0406d1065", + "uuid": "c1b8648c-6f0a-4aa5-b34e-b9e62c0e4364", + "guestIds": null, "capabilities": null, + "environment": { + "created" : "2024-12-09T09:25:17+0000", + "updated" : "2024-12-09T09:25:17+0000", + "id" : "env-id-1", + "name" : "env-name-1", + "type" : "content-template", + "description" : "Testing environment #1", + "contentPrefix" : null, + "owner" : { + "id" : "ff808081550d997c01550d9adaf40003", + "key" : "admin", + "displayName" : "Admin Owner", + "href" : "/owners/admin", + "contentAccessMode" : "org_environment" + }, + "environmentContent" : [ ] + }, + "installedProducts": null, + "canActivate": false, "type": {"manifest": false, + "id": "1000", "label": "system"}, "annotations": null, + "username": "admin", "updated": "2016-06-02T15:16:51+0000", + "lastCheckin": null, "entitlementCount": 0, "releaseVer": + {"releaseVer": null}, "entitlementStatus": "valid", "name": + "test.example.com", "created": "2016-06-02T15:16:51+0000", + "contentTags": null, + "dev": false, + "environments": [ { + "created" : "2024-12-09T09:25:17+0000", + "updated" : "2024-12-09T09:25:17+0000", + "id" : "env-id-1", + "name" : "env-name-1", + "type" : "content-template", + "description" : "Testing environment #1", + "contentPrefix" : null, + "owner" : { + "id" : "ff808081550d997c01550d9adaf40003", + "key" : "admin", + "displayName" : "Admin Owner", + "href" : "/owners/admin", + "contentAccessMode" : "org_environment" + }, + "environmentContent" : [ ] + }, + { + "created" : "2024-12-09T09:25:17+0000", + "updated" : "2024-12-09T09:25:17+0000", + "id" : "env-id-2", + "name" : "env-name-2", + "type" : "wrong_type_foo", + "description" : "Testing environment #2", + "contentPrefix" : null, + "owner" : { + "id" : "ff808081550d997c01550d9adaf40003", + "key" : "admin", + "displayName" : "Admin Owner", + "href" : "/owners/admin", + "contentAccessMode" : "org_environment" + }, + "environmentContent" : [ ] + } ] }""" # Following consumer do not contain information about content access mode OLD_CONSUMER_CONTENT_JSON = """{"hypervisorId": null, @@ -180,12 +323,6 @@ def setUp(self): # Add a mock cp_provider self.mock_cp_provider = mock.Mock(spec=CPProvider, name="CPProvider") - # Add a mock for content access mode cache - self.mock_content_access_mode_cache = mock.Mock( - spec=ContentAccessModeCache, name="ContentAccessModeCache" - ) - self.mock_content_access_mode_cache.read_data = mock.Mock(return_value="entitlement") - # For the tests in which it's used, the consumer_auth cp and basic_auth cp can be the same self.mock_cp_provider.get_consumer_auth_cp.return_value = self.mock_cp self.mock_cp_provider.get_basic_auth_cp.return_value = self.mock_cp @@ -215,8 +352,6 @@ def injection_definitions(self, *args, **kwargs): return self.mock_facts elif args[0] == inj.CP_PROVIDER: return self.mock_cp_provider - elif args[0] == inj.CONTENT_ACCESS_MODE_CACHE: - return self.mock_content_access_mode_cache else: return None @@ -230,13 +365,90 @@ def test_register_normally(self, mock_persist_consumer, mock_write_cache): self.mock_cp.registerConsumer.return_value = expected_consumer register_service = register.RegisterService(self.mock_cp) - register_service.register("org", name="name", environments="environment") + register_service.register("org", name="name", environments=["environment"]) + + self.mock_cp.registerConsumer.assert_called_once_with( + name="name", + facts={}, + owner="org", + environments=["environment"], + environment_names=None, + keys=None, + installed_products=[], + jwt_token=None, + content_tags=[], + consumer_type="system", + role="", + addons=[], + service_level="", + usage="", + ) + self.mock_installed_products.write_cache.assert_called() + + mock_persist_consumer.assert_called_once_with(expected_consumer) + mock_write_cache.assert_called_once() + expected_plugin_calls = [ + mock.call("pre_register_consumer", name="name", facts={}), + mock.call("post_register_consumer", consumer=expected_consumer, facts={}), + ] + self.assertEqual(expected_plugin_calls, self.mock_pm.run.call_args_list) + + @mock.patch("rhsmlib.services.register.syspurposelib.write_syspurpose_cache", return_value=True) + @mock.patch("rhsmlib.services.register.managerlib.persist_consumer_cert") + def test_register_multiple_environment_ids(self, mock_persist_consumer, mock_write_cache): + self.mock_identity.is_valid.return_value = False + self.mock_installed_products.format_for_server.return_value = [] + self.mock_installed_products.tags = [] + expected_consumer = json.loads(CONSUMER_CONTENT_JSON) + self.mock_cp.registerConsumer.return_value = expected_consumer + + register_service = register.RegisterService(self.mock_cp) + register_service.register("org", name="name", environments=["env-id-1", "env-id-2"]) + + self.mock_cp.registerConsumer.assert_called_once_with( + name="name", + facts={}, + owner="org", + environments=["env-id-1", "env-id-2"], + environment_names=None, + keys=None, + installed_products=[], + jwt_token=None, + content_tags=[], + consumer_type="system", + role="", + addons=[], + service_level="", + usage="", + ) + self.mock_installed_products.write_cache.assert_called() + + mock_persist_consumer.assert_called_once_with(expected_consumer) + mock_write_cache.assert_called_once() + expected_plugin_calls = [ + mock.call("pre_register_consumer", name="name", facts={}), + mock.call("post_register_consumer", consumer=expected_consumer, facts={}), + ] + self.assertEqual(expected_plugin_calls, self.mock_pm.run.call_args_list) + + @mock.patch("rhsmlib.services.register.syspurposelib.write_syspurpose_cache", return_value=True) + @mock.patch("rhsmlib.services.register.managerlib.persist_consumer_cert") + def test_register_multiple_environment_names(self, mock_persist_consumer, mock_write_cache): + self.mock_identity.is_valid.return_value = False + self.mock_installed_products.format_for_server.return_value = [] + self.mock_installed_products.tags = [] + expected_consumer = json.loads(CONSUMER_CONTENT_JSON) + self.mock_cp.registerConsumer.return_value = expected_consumer + + register_service = register.RegisterService(self.mock_cp) + register_service.register("org", name="name", environment_names=["env-name-1", "env-name-2"]) self.mock_cp.registerConsumer.assert_called_once_with( name="name", facts={}, owner="org", - environments="environment", + environments=None, + environment_names=["env-name-1", "env-name-2"], keys=None, installed_products=[], jwt_token=None, @@ -257,6 +469,88 @@ def test_register_normally(self, mock_persist_consumer, mock_write_cache): ] self.assertEqual(expected_plugin_calls, self.mock_pm.run.call_args_list) + @mock.patch("rhsmlib.services.register.syspurposelib.write_syspurpose_cache", return_value=True) + @mock.patch("rhsmlib.services.register.managerlib.persist_consumer_cert") + def test_register_environment_name_type(self, mock_persist_consumer, mock_write_cache): + self.mock_identity.is_valid.return_value = False + self.mock_installed_products.format_for_server.return_value = [] + self.mock_installed_products.tags = [] + expected_consumer = json.loads(CONSUMER_CONTENT_JSON) + self.mock_cp.registerConsumer.return_value = expected_consumer + + register_service = register.RegisterService(self.mock_cp) + register_service.register( + "org", + name="name", + environment_names=["env-name-1", "env-name-2"], + environment_type="content-template", + ) + + self.mock_cp.registerConsumer.assert_called_once_with( + name="name", + facts={}, + owner="org", + environments=None, + environment_names=["env-name-1", "env-name-2"], + keys=None, + installed_products=[], + jwt_token=None, + content_tags=[], + consumer_type="system", + role="", + addons=[], + service_level="", + usage="", + ) + self.mock_installed_products.write_cache.assert_called() + + mock_persist_consumer.assert_called_once_with(expected_consumer) + mock_write_cache.assert_called_once() + expected_plugin_calls = [ + mock.call("pre_register_consumer", name="name", facts={}), + mock.call("post_register_consumer", consumer=expected_consumer, facts={}), + ] + self.assertEqual(expected_plugin_calls, self.mock_pm.run.call_args_list) + + @mock.patch("rhsmlib.services.register.syspurposelib.write_syspurpose_cache", return_value=True) + @mock.patch("rhsmlib.services.register.managerlib.persist_consumer_cert") + def test_register_environment_name_wrong_type(self, mock_persist_consumer, mock_write_cache): + self.mock_identity.is_valid.return_value = False + self.mock_installed_products.format_for_server.return_value = [] + self.mock_installed_products.tags = [] + expected_consumer = json.loads(CONSUMER_CONTENT_JSON_WRONG_ENT_TYPE) + self.mock_cp.registerConsumer.return_value = expected_consumer + + register_service = register.RegisterService(self.mock_cp) + + with self.assertRaises(Exception): + register_service.register( + "org", + name="name", + environment_names=["env-name-1", "env-name-2"], + environment_type="content-template", + ) + + @mock.patch("rhsmlib.services.register.syspurposelib.write_syspurpose_cache", return_value=True) + @mock.patch("rhsmlib.services.register.managerlib.persist_consumer_cert") + def test_register_not_allow_environment_ids_and_names(self, mock_persist_consumer, mock_write_cache): + self.mock_identity.is_valid.return_value = False + self.mock_installed_products.format_for_server.return_value = [] + self.mock_installed_products.tags = [] + expected_consumer = json.loads(CONSUMER_CONTENT_JSON) + self.mock_cp.registerConsumer.return_value = expected_consumer + + register_service = register.RegisterService(self.mock_cp) + with self.assertRaisesRegex( + exceptions.ValidationError, r".*Environment IDs and environment names are mutually exclusive.*" + ): + register_service.register( + "org", + name="name", + environments=["env-id-1", "env-id-2"], + environment_names=["env-name-1", "env-name-2"], + ) + @mock.patch("rhsmlib.services.register.syspurposelib.write_syspurpose_cache", return_value=True) @mock.patch("rhsmlib.services.register.managerlib.clean_all_data", return_value=None) @mock.patch("rhsmlib.services.register.managerlib.persist_consumer_cert") @@ -349,13 +643,14 @@ def _no_owner_cb(username): self.assertIsNotNone(org) - register_service.register(org, name="name", environments="environment") + register_service.register(org, name="name") self.mock_cp.registerConsumer.assert_called_once_with( name="name", facts={}, owner="snowwhite", - environments="environment", + environments=None, + environment_names=None, keys=None, installed_products=[], jwt_token=None, @@ -396,6 +691,7 @@ def test_register_with_activation_keys(self, mock_persist_consumer, mock_write_c facts={}, owner="org", environments=None, + environment_names=None, keys=[1], installed_products=[], jwt_token=None, @@ -481,6 +777,7 @@ def test_reads_syspurpose(self, mock_persist_consumer, mock_write_cache): addons=["addon1"], content_tags=[], environments=None, + environment_names=None, facts={}, installed_products=[], jwt_token=None, diff --git a/test/smoke.sh b/test/smoke.sh index 9130825723..eb8e9a5b34 100755 --- a/test/smoke.sh +++ b/test/smoke.sh @@ -226,7 +226,6 @@ run_sm "0" list --available run_sm "0" service-level run_sm "0" service-level --list run_sm "0" repos -run_sm "0" attach # Note: with current test data, the awesome-os repos will never be enabled run_yum "0" repolist @@ -282,7 +281,6 @@ run_rhsmcertd "0" run_rhsmcertd "0" -n run_rhsmcertd_worker "0" -run_rhsmcertd_worker "0" --autoheal # too slow # run_rhsm_debug "0" system @@ -319,8 +317,6 @@ run_sm "0" repos --list # fully entitled, hence the '1' run_sm "1" register --activationkey "${ACTIVATION_KEY}" --org "${ORG}" --force run_sm "0" unregister -run_sm "64" register --activationkey "${ACTIVATION_KEY}" --org "${ORG}" --force --auto-attach -run_sm "1" unregister run_sm "0" clean diff --git a/test/stubs.py b/test/stubs.py index c1faab0a5d..b595c87c9c 100644 --- a/test/stubs.py +++ b/test/stubs.py @@ -32,7 +32,6 @@ AvailableEntitlementsCache, SyspurposeValidFieldsCache, CurrentOwnerCache, - ContentAccessModeCache, SyspurposeComplianceStatusCache, ) from subscription_manager.facts import Facts @@ -483,8 +482,6 @@ def __init__( self.environment_list = [] self.called_unregister_uuid = None self.called_unbind_uuid = None - self.called_unbind_serial = [] - self.called_unbind_pool_id = [] self.username = username self.password = password self._capabilities = [] @@ -495,8 +492,6 @@ def __init__( def reset(self): self.called_unregister_uuid = None self.called_unbind_uuid = None - self.called_unbind_serial = [] - self.called_unbind_pool_id = [] def has_capability(self, capability): return capability in self._capabilities @@ -563,12 +558,6 @@ def getConsumer(self, consumerId): def unbindAll(self, consumer): self.called_unbind_uuid = consumer - def unbindBySerial(self, consumer, serial): - self.called_unbind_serial.append(serial) - - def unbindByPoolId(self, consumer_uuid, pool_id): - self.called_unbind_pool_id.append(pool_id) - def getCertificateSerials(self, consumer): return [] @@ -785,14 +774,6 @@ def delete_cache(self): self.server_status = None -class StubContentAccessModeCache(ContentAccessModeCache): - def write_cache(self, debug=False): - pass - - def delete_cache(self): - self.server_status = None - - class StubSupportedResourcesCache(SupportedResourcesCache): def write_cache(self, debug=False): pass diff --git a/test/test_cache.py b/test/test_cache.py index 76640603fd..88354c3360 100644 --- a/test/test_cache.py +++ b/test/test_cache.py @@ -47,7 +47,6 @@ SupportedResourcesCache, AvailableEntitlementsCache, CurrentOwnerCache, - ContentAccessModeCache, SyspurposeComplianceStatusCache, ) @@ -1327,28 +1326,6 @@ def test_max_timeout(self): self.assertEqual(timeout, self.cache.UBOUND) -class TestContentAccessModeCache(SubManFixture): - MOCK_CACHE_FILE_CONTENT = '{"7f85da06-5c35-44ba-931d-f11f6e581f89": "entitlement"}' - - def setUp(self): - super(TestContentAccessModeCache, self).setUp() - self.cache = ContentAccessModeCache() - - def test_reading_nonexisting_cache(self): - data = self.cache.read_cache_only() - self.assertIsNone(data) - - def test_reading_existing_cache(self): - temp_cache_dir = tempfile.mkdtemp() - self.addCleanup(shutil.rmtree, temp_cache_dir) - self.cache.CACHE_FILE = os.path.join(temp_cache_dir, "content_access_mode.json") - with open(self.cache.CACHE_FILE, "w") as cache_file: - cache_file.write(self.MOCK_CACHE_FILE_CONTENT) - data = self.cache.read_cache_only() - self.assertTrue("7f85da06-5c35-44ba-931d-f11f6e581f89" in data) - self.assertEqual(data["7f85da06-5c35-44ba-931d-f11f6e581f89"], "entitlement") - - class TestSyspurposeComplianceStatusCache(SubManFixture): def setUp(self): super(TestSyspurposeComplianceStatusCache, self).setUp() diff --git a/test/test_cert_sorter.py b/test/test_cert_sorter.py index 93d522142f..14a6979d93 100644 --- a/test/test_cert_sorter.py +++ b/test/test_cert_sorter.py @@ -63,8 +63,9 @@ def stub_prod_cert(pid): class CertSorterTests(SubManFixture): + @patch("subscription_manager.cert_sorter.utils.is_simple_content_access") @patch("subscription_manager.cache.InstalledProductsManager.update_check") - def setUp(self, mock_update): + def setUp(self, mock_update, mock_is_simple_content_access): SubManFixture.setUp(self) # Setup mock product and entitlement certs: self.prod_dir = StubProductDirectory(pids=[INST_PID_1, INST_PID_2, INST_PID_3, INST_PID_4]) @@ -79,6 +80,7 @@ def setUp(self, mock_update): ), ] ) + mock_is_simple_content_access.return_value = False self.mock_uep = StubUEP() @@ -170,11 +172,13 @@ def test_installed_mismatch_unentitled(self, mock_update): # server reported it here: self.assertFalse(INST_PID_3 in sorter.unentitled_products) + @patch("subscription_manager.cert_sorter.utils.is_simple_content_access") @patch("subscription_manager.cache.InstalledProductsManager.update_check") - def test_missing_installed_product(self, mock_update): + def test_missing_installed_product(self, mock_update, mock_is_simple_content_access): # Add a new installed product server doesn't know about: prod_dir = StubProductDirectory(pids=[INST_PID_1, INST_PID_2, INST_PID_3, "product4"]) inj.provide(inj.PROD_DIR, prod_dir) + mock_is_simple_content_access.return_value = False sorter = CertSorter() self.assertTrue("product4" in sorter.unentitled_products) diff --git a/test/test_certmgr.py b/test/test_certmgr.py index 99586f07f6..7c5013706e 100644 --- a/test/test_certmgr.py +++ b/test/test_certmgr.py @@ -38,7 +38,7 @@ "releaseVer": {"id": 1, "releaseVer": "123123"}, "serviceLevel": "Pro Turbo HD Plus Ultra", "owner": {"key": "admin"}, - "autoheal": 1, + "autoheal": True, "idCert": {"serial": {"serial": 3787455826750723380}}, } diff --git a/test/test_managercli.py b/test/test_managercli.py index 9b921a459f..1412c266b0 100644 --- a/test/test_managercli.py +++ b/test/test_managercli.py @@ -212,7 +212,7 @@ def test_unknown_args_cause_exit(self): sys, "argv", # test with some subcommand; sub-man prints help without it - ["subscription-manager", "attach", "--foo", "bar", "baz"], + ["subscription-manager", "register", "--foo", "bar", "baz"], ): try: self.cc.main() diff --git a/test/test_managerlib.py b/test/test_managerlib.py index f7ec64a526..975e0d067b 100644 --- a/test/test_managerlib.py +++ b/test/test_managerlib.py @@ -715,201 +715,6 @@ def MockSystemLog(self, message, priority): EXPECTED_CONTENT_V3 = EXPECTED_CERT_CONTENT_V3 + os.linesep + EXPECTED_KEY_CONTENT_V3 -class ExtractorStub(managerlib.ImportFileExtractor): - def __init__(self, content, file_path="test/file/path"): - self.content = content - self.writes = [] - managerlib.ImportFileExtractor.__init__(self, file_path) - - # Stub out any file system access - def _read(self, file_path): - return self.content - - def _write_file(self, target, content): - self.writes.append((target, content)) - - def _ensure_entitlement_dir_exists(self): - # Do nothing but stub out the dir check to avoid file system access. - pass - - -class TestImportFileExtractor(unittest.TestCase): - def test_contains_key_content_when_key_and_cert_exists_in_import_file(self): - extractor = ExtractorStub(EXPECTED_CONTENT) - self.assertTrue(extractor.contains_key_content()) - - def test_contains_key_content_when_key_and_cert_exists_in_import_file_v3(self): - extractor = ExtractorStub(EXPECTED_CONTENT_V3) - self.assertTrue(extractor.contains_key_content()) - - def test_does_not_contain_key_when_key_does_not_exist_in_import_file(self): - extractor = ExtractorStub(EXPECTED_CERT_CONTENT) - self.assertFalse(extractor.contains_key_content()) - - def test_does_not_contain_key_when_key_does_not_exist_in_import_file_v3(self): - extractor = ExtractorStub(EXPECTED_CERT_CONTENT_V3) - self.assertFalse(extractor.contains_key_content()) - - def test_get_key_content_when_key_exists(self): - extractor = ExtractorStub(EXPECTED_CONTENT, file_path="12345.pem") - self.assertTrue(extractor.contains_key_content()) - self.assertEqual(EXPECTED_KEY_CONTENT, extractor.get_key_content()) - - def test_get_key_content_when_key_exists_v3(self): - extractor = ExtractorStub(EXPECTED_CONTENT_V3, file_path="12345.pem") - self.assertTrue(extractor.contains_key_content()) - self.assertEqual(EXPECTED_KEY_CONTENT_V3, extractor.get_key_content()) - - def test_get_key_content_returns_None_when_key_does_not_exist(self): - extractor = ExtractorStub(EXPECTED_CERT_CONTENT, file_path="12345.pem") - self.assertFalse(extractor.get_key_content()) - - def test_get_key_content_returns_None_when_key_does_not_exist_v3(self): - extractor = ExtractorStub(EXPECTED_CERT_CONTENT_V3, file_path="12345.pem") - self.assertFalse(extractor.get_key_content()) - - def test_get_cert_content(self): - extractor = ExtractorStub(EXPECTED_CONTENT, file_path="12345.pem") - self.assertTrue(extractor.contains_key_content()) - self.assertEqual(EXPECTED_CERT_CONTENT, extractor.get_cert_content()) - - def test_get_cert_content_v3(self): - extractor = ExtractorStub(EXPECTED_CONTENT_V3, file_path="12345.pem") - self.assertTrue(extractor.contains_key_content()) - self.assertEqual(EXPECTED_CERT_CONTENT_V3, extractor.get_cert_content()) - - def test_get_cert_content_returns_None_when_cert_does_not_exist(self): - extractor = ExtractorStub(EXPECTED_KEY_CONTENT, file_path="12345.pem") - self.assertFalse(extractor.get_cert_content()) - - def test_get_cert_content_returns_None_when_cert_does_not_exist_v3(self): - extractor = ExtractorStub(EXPECTED_KEY_CONTENT_V3, file_path="12345.pem") - self.assertFalse(extractor.get_cert_content()) - - def test_verify_valid_entitlement_for_invalid_cert(self): - extractor = ExtractorStub(EXPECTED_KEY_CONTENT, file_path="12345.pem") - self.assertFalse(extractor.verify_valid_entitlement()) - - def test_verify_valid_entitlement_for_invalid_cert_v3(self): - extractor = ExtractorStub(EXPECTED_KEY_CONTENT_V3, file_path="12345.pem") - self.assertFalse(extractor.verify_valid_entitlement()) - - def test_verify_valid_entitlement_for_invalid_cert_bundle(self): - # Use a bundle of cert + key, but the cert is not an entitlement cert: - extractor = ExtractorStub(IDENTITY_CERT_WITH_KEY, file_path="12345.pem") - self.assertFalse(extractor.verify_valid_entitlement()) - - def test_verify_valid_entitlement_for_no_key(self): - extractor = ExtractorStub(EXPECTED_CERT_CONTENT, file_path="12345.pem") - self.assertFalse(extractor.verify_valid_entitlement()) - - def test_verify_valid_entitlement_for_no_key_v3(self): - extractor = ExtractorStub(EXPECTED_CERT_CONTENT_V3, file_path="12345.pem") - self.assertFalse(extractor.verify_valid_entitlement()) - - def test_verify_valid_entitlement_for_no_cert_content(self): - extractor = ExtractorStub("", file_path="12345.pem") - self.assertFalse(extractor.verify_valid_entitlement()) - - def test_write_cert_only(self): - expected_cert_file = "%d.pem" % (EXPECTED_CERT.serial) - extractor = ExtractorStub(EXPECTED_CERT_CONTENT, file_path=expected_cert_file) - extractor.write_to_disk() - - self.assertEqual(1, len(extractor.writes)) - - write_one = extractor.writes[0] - self.assertEqual(os.path.join(ENT_CONFIG_DIR, expected_cert_file), write_one[0]) - self.assertEqual(EXPECTED_CERT_CONTENT, write_one[1]) - - def test_write_cert_only_v3(self): - expected_cert_file = "%d.pem" % (EXPECTED_CERT_V3.serial) - extractor = ExtractorStub(EXPECTED_CERT_CONTENT_V3, file_path=expected_cert_file) - extractor.write_to_disk() - - self.assertEqual(1, len(extractor.writes)) - - write_one = extractor.writes[0] - self.assertEqual(os.path.join(ENT_CONFIG_DIR, expected_cert_file), write_one[0]) - self.assertEqual(EXPECTED_CERT_CONTENT_V3, write_one[1]) - - def test_write_key_and_cert(self): - filename = "%d.pem" % (EXPECTED_CERT.serial) - self._assert_correct_cert_and_key_files_generated_with_filename(filename) - - def test_write_key_and_cert_v3(self): - filename = "%d.pem" % (EXPECTED_CERT_V3.serial) - self._assert_correct_cert_and_key_files_generated_with_filename_v3(filename) - - def test_file_renamed_when_imported_with_serial_no_and_custom_extension(self): - filename = "%d.cert" % (EXPECTED_CERT.serial) - self._assert_correct_cert_and_key_files_generated_with_filename(filename) - - def test_file_renamed_when_imported_with_serial_no_and_custom_extension_v3(self): - filename = "%d.cert" % (EXPECTED_CERT_V3.serial) - self._assert_correct_cert_and_key_files_generated_with_filename_v3(filename) - - def test_file_renamed_when_imported_with_serial_no_and_no_extension(self): - filename = str(EXPECTED_CERT.serial) - self._assert_correct_cert_and_key_files_generated_with_filename(filename) - - def test_file_renamed_when_imported_with_serial_no_and_no_extension_v3(self): - filename = str(EXPECTED_CERT_V3.serial) - self._assert_correct_cert_and_key_files_generated_with_filename_v3(filename) - - def test_file_renamed_when_imported_with_custom_name_and_pem_extension(self): - filename = "entitlement.pem" - self._assert_correct_cert_and_key_files_generated_with_filename(filename) - - def test_file_renamed_when_imported_with_custom_name_and_pem_extension_v3(self): - filename = "entitlement.pem" - self._assert_correct_cert_and_key_files_generated_with_filename_v3(filename) - - def test_file_renamed_when_imported_with_custom_name_no_extension(self): - filename = "entitlement" - self._assert_correct_cert_and_key_files_generated_with_filename(filename) - - def test_file_renamed_when_imported_with_custom_name_no_extension_v3(self): - filename = "entitlement" - self._assert_correct_cert_and_key_files_generated_with_filename_v3(filename) - - def _assert_correct_cert_and_key_files_generated_with_filename(self, filename): - expected_file_prefix = "%d" % (EXPECTED_CERT.serial) - expected_cert_file = expected_file_prefix + ".pem" - expected_key_file = expected_file_prefix + "-key.pem" - - extractor = ExtractorStub(EXPECTED_CONTENT, file_path=filename) - extractor.write_to_disk() - - self.assertEqual(2, len(extractor.writes)) - - write_one = extractor.writes[0] - self.assertEqual(os.path.join(ENT_CONFIG_DIR, expected_cert_file), write_one[0]) - self.assertEqual(EXPECTED_CERT_CONTENT, write_one[1]) - - write_two = extractor.writes[1] - self.assertEqual(os.path.join(ENT_CONFIG_DIR, expected_key_file), write_two[0]) - self.assertEqual(EXPECTED_KEY_CONTENT, write_two[1]) - - def _assert_correct_cert_and_key_files_generated_with_filename_v3(self, filename): - expected_file_prefix = "%d" % (EXPECTED_CERT_V3.serial) - expected_cert_file = expected_file_prefix + ".pem" - expected_key_file = expected_file_prefix + "-key.pem" - - extractor = ExtractorStub(EXPECTED_CONTENT_V3, file_path=filename) - extractor.write_to_disk() - - self.assertEqual(2, len(extractor.writes)) - - write_one = extractor.writes[0] - self.assertEqual(os.path.join(ENT_CONFIG_DIR, expected_cert_file), write_one[0]) - self.assertEqual(EXPECTED_CERT_CONTENT_V3, write_one[1]) - - write_two = extractor.writes[1] - self.assertEqual(os.path.join(ENT_CONFIG_DIR, expected_key_file), write_two[0]) - self.assertEqual(EXPECTED_KEY_CONTENT_V3, write_two[1]) - - class TestMergedPoolsStackingGroupSorter(unittest.TestCase): def test_sorter_adds_group_for_non_stackable_entitlement(self): pool = self._create_pool("test-prod-1", "Test Prod 1") diff --git a/test/test_plugins.py b/test/test_plugins.py index e58c0827df..6a1fa9288f 100644 --- a/test/test_plugins.py +++ b/test/test_plugins.py @@ -1075,19 +1075,6 @@ def test_post_subscription_conduit(self): self.assertEqual({}, conduit.entitlement_data) -class TestAutoAttachConduit(unittest.TestCase): - def test_auto_attach_conduit(self): - conduit = plugins.AutoAttachConduit(StubPluginClass, "a-consumer-uuid") - self.assertEqual("a-consumer-uuid", conduit.consumer_uuid) - - -class TestPostAutoAttachConduit(unittest.TestCase): - def test_post_auto_attach_conduit(self): - conduit = plugins.PostAutoAttachConduit(StubPluginClass, "a-consumer-uuid", {}) - self.assertEqual("a-consumer-uuid", conduit.consumer_uuid) - self.assertEqual({}, conduit.entitlement_data) - - class BasePluginException(unittest.TestCase): """At least create and raise all the exceptions.""" diff --git a/test/test_syspurposestore_interface.py b/test/test_syspurposestore_interface.py deleted file mode 100644 index f733e34fe1..0000000000 --- a/test/test_syspurposestore_interface.py +++ /dev/null @@ -1,134 +0,0 @@ -# Copyright (c) 2018 Red Hat, Inc. -# -# This software is licensed to you under the GNU General Public License, -# version 2 (GPLv2). There is NO WARRANTY for this software, express or -# implied, including the implied warranties of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 -# along with this software; if not, see -# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. -# -# Red Hat trademarks are not licensed under GPLv2. No permission is -# granted to use or replicate Red Hat trademarks that are incorporated -# in this software or its documentation. -# - -import json -import shutil -import tempfile -import unittest -from .fixture import set_up_mock_sp_store - -import os -from unittest import mock - - -class SyspurposeStoreInterfaceTests(unittest.TestCase): - def setUp(self): - temp_dir = tempfile.mkdtemp() - self.addCleanup(shutil.rmtree, temp_dir) - mock_syspurpose_file = os.path.join(temp_dir, "mock_syspurpose.json") - syspurpose_values = {} - with open(mock_syspurpose_file, "w") as f: - json.dump(syspurpose_values, f) - f.flush() - - from subscription_manager import syspurposelib - - self.syspurposelib = syspurposelib - syspurposelib.USER_SYSPURPOSE = mock_syspurpose_file - - syspurpose_patch = mock.patch("subscription_manager.syspurposelib.SyncedStore") - self.mock_sp_store = syspurpose_patch.start() - self.mock_sp_store, self.mock_sp_store_contents = set_up_mock_sp_store(self.mock_sp_store) - self.addCleanup(syspurpose_patch.stop) - - def _set_up_mock_sp_store(self): - """ - Sets up the mock syspurpose store with methods that are mock versions of the real deal. - Allows us to test in the absence of the syspurpose module. - :return: - """ - contents = {} - self.mock_sp_store_contents = contents - - def set(item, value): - contents[item] = value - - def read(path, raise_on_error=False): - return self.mock_sp_store - - def unset(item): - contents[item] = None - - def add(item, value): - current = contents.get(item, []) - if value not in current: - current.append(value) - contents[item] = current - - def remove(item, value): - current = contents.get(item) - if current is not None and isinstance(current, list) and value in current: - current.remove(value) - - self.mock_sp_store.set = mock.Mock(side_effect=set) - self.mock_sp_store.read = mock.Mock(side_effect=read) - self.mock_sp_store.unset = mock.Mock(side_effect=unset) - self.mock_sp_store.add = mock.Mock(side_effect=add) - self.mock_sp_store.remove = mock.Mock(side_effect=remove) - self.mock_sp_store.contents = self.mock_sp_store_contents - - def tearDown(self): - self.syspurposelib.USER_SYSPURPOSE = "/etc/rhsm/syspurpose/syspurpose.json" - - def test_save_sla_to_syspurpose_metadata_sla_is_set_when_syspurpose_module_exists(self): - """ - Tests that the syspurpose sla is set through the syspurposestore interface - when the syspurpose module is available for import. - """ - self.syspurposelib.save_sla_to_syspurpose_metadata(None, None, "Freemium") - - contents = self.syspurposelib.read_syspurpose() - self.assertEqual(contents.get("service_level_agreement"), "Freemium") - - def test_save_sla_to_syspurpose_metadata_sla_is_not_set_when_None_is_provided(self): - """ - Tests that the syspurpose sla is not set through the syspurposestore interface - when None is passed to save_sla_to_syspurpose_metadata method. - """ - self.syspurposelib.save_sla_to_syspurpose_metadata(None, None, None) - - contents = self.syspurposelib.read_syspurpose() - self.assertEqual(contents.get("service_level_agreement"), None) - - def test_save_sla_to_syspurpose_metadata_sla_is_not_set_when_empty_string_is_provided(self): - """ - Tests that the syspurpose sla is not set through the syspurposestore interface - when an empty string is passed to save_sla_to_syspurpose_metadata method. - """ - self.syspurposelib.save_sla_to_syspurpose_metadata(None, None, "") - - contents = self.syspurposelib.read_syspurpose() - self.assertEqual(contents.get("service_level_agreement"), None) - - def test_save_sla_to_syspurpose_metadata_sla_is_not_set_when_syspurpose_module_does_not_exist(self): - """ - Tests that the syspurpose sla is NOT set through the syspurposestore interface - when the syspurpose module is not available for import. - """ - # Remove SyspurposeStore and USER_SYSPURPOSE from syspurposestore_interface's scope temporarily - # to simulate that importing them failed. - tmp_syspurpose_store = self.syspurposelib.SyncedStore - tmp_user_syspurpose = self.syspurposelib.USER_SYSPURPOSE - del self.syspurposelib.SyncedStore - del self.syspurposelib.USER_SYSPURPOSE - - self.syspurposelib.save_sla_to_syspurpose_metadata(None, None, "Freemium") - - # Add SyspurposeStore and USER_SYSPURPOSE back to syspurposestore_interface's scope - self.syspurposelib.SyncedStore = tmp_syspurpose_store - self.syspurposelib.USER_SYSPURPOSE = tmp_user_syspurpose - - # Check that the contents of the syspurpose.json are empty (thus sla was not set) - contents = self.syspurposelib.read_syspurpose() - self.assertFalse(contents) diff --git a/test/test_utils.py b/test/test_utils.py index 3110a03709..57b7c69505 100644 --- a/test/test_utils.py +++ b/test/test_utils.py @@ -19,7 +19,6 @@ format_baseurl, get_version, get_client_versions, - unique_list_items, get_server_versions, friendly_join, is_true_value, @@ -585,22 +584,6 @@ def test_false_value(self): self.assertFalse(is_true_value("f")) -class TestUniqueListItems(fixture.SubManFixture): - def test_preserves_order(self): - input_list = [1, 1, 2, 2, 3, 3] - expected = [1, 2, 3] - self.assertEqual(expected, unique_list_items(input_list)) - - def test_hash_function(self): - mock_item_1 = Mock() - mock_item_1.value = 1 - mock_item_2 = Mock() - mock_item_2.value = 2 - input_list = [mock_item_1, mock_item_1, mock_item_2, mock_item_2] - expected = [mock_item_1, mock_item_2] - self.assertEqual(expected, unique_list_items(input_list, lambda x: x.value)) - - class TestProductCertificateFilter(fixture.SubManFixture): def test_set_filter_string(self): test_data = [ @@ -798,17 +781,12 @@ def setUp(self): super(TestIsOwnerUsingSimpleContentAccess, self).setUp() self.cp_provider = Mock() self.mock_uep = Mock() - self.mock_uep.getOwner = Mock(return_value=self.MOCK_ENTITLEMENT_OWNER) + self.mock_uep.getOwner = Mock(return_value=self.MOCK_ORG_ENVIRONMENT_OWNER) self.cp_provider.get_consumer_auth_cp = Mock(return_value=self.mock_uep) self.identity = Mock() self.identity.uuid = Mock(return_value="7f85da06-5c35-44ba-931d-f11f6e581f89") - def test_get_entitlement_owner(self): - ret = is_simple_content_access(uep=self.mock_uep, identity=self.identity) - self.assertFalse(ret) - def test_get_org_environment_owner(self): - self.mock_uep.getOwner = Mock(return_value=self.MOCK_ORG_ENVIRONMENT_OWNER) ret = is_simple_content_access(uep=self.mock_uep, identity=self.identity) self.assertTrue(ret) diff --git a/test/zypper/test_serviceplugin.py b/test/zypper/test_serviceplugin.py index 16472d525c..f4d858edcf 100644 --- a/test/zypper/test_serviceplugin.py +++ b/test/zypper/test_serviceplugin.py @@ -40,9 +40,6 @@ def test_provides_subman_repos_if_registered_and_subscribed(self): "--serverurl={RHSM_URL}".format(sub_man=self.SUB_MAN, **os.environ), shell=True, ) - subprocess.call( - "{sub_man} attach --pool={RHSM_POOL}".format(sub_man=self.SUB_MAN, **os.environ), shell=True - ) self.assertTrue(self.has_subman_repos()) def test_can_download_rpm(self): @@ -51,9 +48,6 @@ def test_can_download_rpm(self): "--serverurl={RHSM_URL}".format(sub_man=self.SUB_MAN, **os.environ), shell=True, ) - subprocess.check_call( - "{sub_man} attach --pool={RHSM_POOL}".format(sub_man=self.SUB_MAN, **os.environ), shell=True - ) subprocess.check_call( "{sub_man} repos --enable={RHSM_TEST_REPO}".format(sub_man=self.SUB_MAN, **os.environ), shell=True )