diff --git a/awx/api/generics.py b/awx/api/generics.py index 20476bbb06..5daba31fa0 100644 --- a/awx/api/generics.py +++ b/awx/api/generics.py @@ -39,6 +39,7 @@ from awx.main.models import UnifiedJob, UnifiedJobTemplate, User, Role, Credenti from awx.main.access import access_registry from awx.main.utils import camelcase_to_underscore, get_search_fields, getattrd, get_object_or_400, decrypt_field, get_awx_version from awx.main.utils.db import get_all_field_names +from awx.main.utils.licensing import server_product_name from awx.main.views import ApiErrorView from awx.api.serializers import ResourceAccessListElementSerializer, CopySerializer, UserSerializer from awx.api.versioning import URLPathVersioning @@ -184,9 +185,6 @@ class APIView(views.APIView): """ Log warning for 400 requests. Add header with elapsed time. """ - from awx.main.utils import get_licenser - from awx.main.utils.licensing import OpenLicense - # # If the URL was rewritten, and we get a 404, we should entirely # replace the view in the request context with an ApiErrorView() @@ -226,7 +224,7 @@ class APIView(views.APIView): response = super(APIView, self).finalize_response(request, response, *args, **kwargs) time_started = getattr(self, 'time_started', None) response['X-API-Product-Version'] = get_awx_version() - response['X-API-Product-Name'] = 'AWX' if isinstance(get_licenser(), OpenLicense) else 'Red Hat Ansible Tower' + response['X-API-Product-Name'] = server_product_name() response['X-API-Node'] = settings.CLUSTER_HOST_ID if time_started: diff --git a/awx/api/views/root.py b/awx/api/views/root.py index 8784fddf6f..8e1419551d 100644 --- a/awx/api/views/root.py +++ b/awx/api/views/root.py @@ -30,6 +30,7 @@ from awx.api.versioning import reverse, drf_reverse from awx.main.constants import PRIVILEGE_ESCALATION_METHODS from awx.main.models import Project, Organization, Instance, InstanceGroup, JobTemplate from awx.main.utils import set_environ +from awx.main.utils.licensing import get_licenser logger = logging.getLogger('awx.api.views.root') @@ -173,8 +174,6 @@ class ApiV2SubscriptionView(APIView): self.permission_denied(request) # Raises PermissionDenied exception. def post(self, request): - from awx.main.utils.common import get_licenser - data = request.data.copy() if data.get('subscriptions_password') == '$encrypted$': data['subscriptions_password'] = settings.SUBSCRIPTIONS_PASSWORD @@ -222,7 +221,6 @@ class ApiV2AttachView(APIView): user = getattr(settings, 'SUBSCRIPTIONS_USERNAME', None) pw = getattr(settings, 'SUBSCRIPTIONS_PASSWORD', None) if pool_id and user and pw: - from awx.main.utils.common import get_licenser data = request.data.copy() try: @@ -264,8 +262,6 @@ class ApiV2ConfigView(APIView): def get(self, request, format=None): '''Return various sitewide configuration settings''' - from awx.main.utils.common import get_licenser - license_data = get_licenser().validate() if not license_data.get('valid_key', False): @@ -320,8 +316,6 @@ class ApiV2ConfigView(APIView): logger.info(smart_text(u"Invalid JSON submitted for license."), extra=dict(actor=request.user.username)) return Response({"error": _("Invalid JSON")}, status=status.HTTP_400_BAD_REQUEST) - from awx.main.utils.common import get_licenser - license_data = json.loads(data_actual) if 'license_key' in license_data: return Response({"error": _('Legacy license submitted. A subscription manifest is now required.')}, status=status.HTTP_400_BAD_REQUEST) diff --git a/awx/main/models/inventory.py b/awx/main/models/inventory.py index a0a30bb844..585f1c6592 100644 --- a/awx/main/models/inventory.py +++ b/awx/main/models/inventory.py @@ -51,6 +51,7 @@ from awx.main.models.credential.injectors import _openstack_data from awx.main.utils import _inventory_updates from awx.main.utils.safe_yaml import sanitize_jinja from awx.main.utils.execution_environments import to_container_path +from awx.main.utils.licensing import server_product_name __all__ = ['Inventory', 'Host', 'Group', 'InventorySource', 'InventoryUpdate', 'SmartInventoryMembership'] @@ -1336,6 +1337,7 @@ class PluginFileInjector(object): namespace = None collection = None collection_migration = '2.9' # Starting with this version, we use collections + use_fqcn = False # plugin: name versus plugin: namespace.collection.name # TODO: delete this method and update unit tests @classmethod @@ -1362,7 +1364,12 @@ class PluginFileInjector(object): Note that a plugin value of '' should still be overridden. ''' if self.plugin_name is not None: - source_vars['plugin'] = self.plugin_name + if hasattr(self, 'downstream_namespace') and server_product_name() != 'AWX': + source_vars['plugin'] = f'{self.downstream_namespace}.{self.downstream_collection}.{self.plugin_name}' + elif self.use_fqcn: + source_vars['plugin'] = f'{self.namespace}.{self.collection}.{self.plugin_name}' + else: + source_vars['plugin'] = self.plugin_name return source_vars def build_env(self, inventory_update, env, private_data_dir, private_data_files): @@ -1510,12 +1517,17 @@ class rhv(PluginFileInjector): initial_version = '2.9' namespace = 'ovirt' collection = 'ovirt' + downstream_namespace = 'redhat' + downstream_collection = 'rhv' class satellite6(PluginFileInjector): plugin_name = 'foreman' namespace = 'theforeman' collection = 'foreman' + downstream_namespace = 'redhat' + downstream_collection = 'satellite' + use_fqcn = True def get_plugin_env(self, inventory_update, private_data_dir, private_data_files): # this assumes that this is merged @@ -1528,18 +1540,14 @@ class satellite6(PluginFileInjector): ret['FOREMAN_PASSWORD'] = credential.get_input('password', default='') return ret - def inventory_as_dict(self, inventory_update, private_data_dir): - ret = super(satellite6, self).inventory_as_dict(inventory_update, private_data_dir) - # this inventory plugin requires the fully qualified inventory plugin name - ret['plugin'] = f'{self.namespace}.{self.collection}.{self.plugin_name}' - return ret - class tower(PluginFileInjector): plugin_name = 'tower' base_injector = 'template' namespace = 'awx' collection = 'awx' + downstream_namespace = 'ansible' + downstream_collection = 'controller' class insights(PluginFileInjector): diff --git a/awx/main/utils/__init__.py b/awx/main/utils/__init__.py index e635e44f62..2ffec9d8b6 100644 --- a/awx/main/utils/__init__.py +++ b/awx/main/utils/__init__.py @@ -11,3 +11,4 @@ from awx.main.utils.encryption import ( # noqa decrypt_value, encrypt_dict, ) +from awx.main.utils.licensing import get_licenser # noqa diff --git a/awx/main/utils/common.py b/awx/main/utils/common.py index c0f8de4abc..2492b9a87a 100644 --- a/awx/main/utils/common.py +++ b/awx/main/utils/common.py @@ -51,7 +51,6 @@ __all__ = [ 'underscore_to_camelcase', 'memoize', 'memoize_delete', - 'get_licenser', 'get_awx_http_client_headers', 'get_awx_version', 'update_scm_url', @@ -255,18 +254,6 @@ def get_awx_http_client_headers(): return headers -def get_licenser(*args, **kwargs): - from awx.main.utils.licensing import Licenser, OpenLicense - - try: - if os.path.exists('/var/lib/awx/.tower_version'): - return Licenser(*args, **kwargs) - else: - return OpenLicense() - except Exception as e: - raise ValueError(_('Error importing License: %s') % e) - - def update_scm_url(scm_type, url, username=True, password=True, check_special_cases=True, scp_format=False): """ Update the given SCM URL to add/replace/remove the username/password. When diff --git a/awx/main/utils/licensing.py b/awx/main/utils/licensing.py index da2ceedde7..eeae581655 100644 --- a/awx/main/utils/licensing.py +++ b/awx/main/utils/licensing.py @@ -15,6 +15,7 @@ from datetime import datetime, timezone import collections import copy import io +import os import json import logging import re @@ -34,8 +35,6 @@ from cryptography import x509 from django.conf import settings from django.utils.translation import ugettext_lazy as _ -# AWX -from awx.main.models import Host, HostMetric, Instance MAX_INSTANCES = 9999999 @@ -379,10 +378,9 @@ class Licenser(object): return attrs attrs['valid_key'] = True - if Host: - current_instances = Host.objects.active_count() - else: - current_instances = 0 + from awx.main.models import Host, HostMetric, Instance + + current_instances = Host.objects.active_count() license_date = int(attrs.get('license_date', 0) or 0) automated_instances = HostMetric.objects.count() first_host = HostMetric.objects.only('first_automation').order_by('first_automation').first() @@ -408,3 +406,19 @@ class Licenser(object): attrs['date_warning'] = bool(time_remaining < self.SUBSCRIPTION_TIMEOUT) attrs['date_expired'] = bool(time_remaining <= 0) return attrs + + +def get_licenser(*args, **kwargs): + from awx.main.utils.licensing import Licenser, OpenLicense + + try: + if os.path.exists('/var/lib/awx/.tower_version'): + return Licenser(*args, **kwargs) + else: + return OpenLicense() + except Exception as e: + raise ValueError(_('Error importing License: %s') % e) + + +def server_product_name(): + return 'AWX' if isinstance(get_licenser(), OpenLicense) else 'Red Hat Ansible Automation Platform' diff --git a/awx/ui_next/urls.py b/awx/ui_next/urls.py index c91f1390bb..4ce182d7bc 100644 --- a/awx/ui_next/urls.py +++ b/awx/ui_next/urls.py @@ -2,8 +2,7 @@ from django.conf.urls import url from django.utils.translation import ugettext_lazy as _ from django.views.generic.base import TemplateView -from awx.main.utils.common import get_licenser -from awx.main.utils.licensing import OpenLicense +from awx.main.utils.licensing import server_product_name class IndexView(TemplateView): @@ -17,7 +16,7 @@ class MigrationsNotran(TemplateView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - product_name = 'AWX' if isinstance(get_licenser(), OpenLicense) else 'Red Hat Ansible Automation Platform' + product_name = server_product_name() context['title'] = _('%s Upgrading' % product_name) context['image_alt'] = _('Logo') context['aria_spinner'] = _('Loading')