mirror of
https://github.com/ansible/awx.git
synced 2026-01-15 20:00:43 -03:30
Adjust subscription calculation & warning behavior
- Add a field for hosts automated across This is populated by the new table we've added. - Update the subs check to check against this, not imported hosts. - Reword messages on inventory import
This commit is contained in:
parent
d3b20e6585
commit
ca07946c24
@ -36,20 +36,20 @@ from awx.main.utils.pglock import advisory_lock
|
||||
logger = logging.getLogger('awx.main.commands.inventory_import')
|
||||
|
||||
LICENSE_EXPIRED_MESSAGE = '''\
|
||||
License expired.
|
||||
See http://www.ansible.com/renew for license extension information.'''
|
||||
Subscription expired.
|
||||
Contact us (https://www.redhat.com/contact) for subscription extension information.'''
|
||||
|
||||
LICENSE_NON_EXISTANT_MESSAGE = '''\
|
||||
No license.
|
||||
See http://www.ansible.com/renew for license information.'''
|
||||
No subscription.
|
||||
Contact us (https://www.redhat.com/contact) for subscription information.'''
|
||||
|
||||
LICENSE_MESSAGE = '''\
|
||||
Number of licensed instances exceeded, would bring available instances to %(new_count)d, system is licensed for %(instance_count)d.
|
||||
See http://www.ansible.com/renew for license extension information.'''
|
||||
%(new_count)d instances have been automated, system is subscribed for %(instance_count)d.
|
||||
Contact us (https://www.redhat.com/contact) for upgrade information.'''
|
||||
|
||||
DEMO_LICENSE_MESSAGE = '''\
|
||||
Demo mode free license count exceeded, would bring available instances to %(new_count)d, demo mode allows %(instance_count)d.
|
||||
See http://www.ansible.com/renew for licensing information.'''
|
||||
Demo mode free subscription count exceeded. Current automated instances are %(new_count)d, demo mode allows %(instance_count)d.
|
||||
Contact us (https://www.redhat.com/contact) for subscription information.'''
|
||||
|
||||
|
||||
def functioning_dir(path):
|
||||
@ -761,29 +761,22 @@ class Command(BaseCommand):
|
||||
instance_count = license_info.get('instance_count', 0)
|
||||
free_instances = license_info.get('free_instances', 0)
|
||||
time_remaining = license_info.get('time_remaining', 0)
|
||||
automated_count = license_info.get('automated_instances', 0)
|
||||
hard_error = license_info.get('trial', False) is True or license_info['instance_count'] == 10
|
||||
new_count = Host.objects.active_count()
|
||||
if time_remaining <= 0:
|
||||
if hard_error:
|
||||
logger.error(LICENSE_EXPIRED_MESSAGE)
|
||||
raise PermissionDenied("License has expired!")
|
||||
raise PermissionDenied("Subscription has expired!")
|
||||
else:
|
||||
logger.warning(LICENSE_EXPIRED_MESSAGE)
|
||||
# special check for tower-type inventory sources
|
||||
# but only if running the plugin
|
||||
TOWER_SOURCE_FILES = ['tower.yml', 'tower.yaml']
|
||||
if self.inventory_source.source == 'tower' and any(f in self.inventory_source.source_path for f in TOWER_SOURCE_FILES):
|
||||
# only if this is the 2nd call to license check, we cannot compare before running plugin
|
||||
if hasattr(self, 'all_group'):
|
||||
self.remote_tower_license_compare(local_license_type)
|
||||
if free_instances < 0:
|
||||
d = {
|
||||
'new_count': new_count,
|
||||
'new_count': automated_count,
|
||||
'instance_count': instance_count,
|
||||
}
|
||||
if hard_error:
|
||||
logger.error(LICENSE_MESSAGE % d)
|
||||
raise PermissionDenied('License count exceeded!')
|
||||
raise PermissionDenied('Subscription count exceeded!')
|
||||
else:
|
||||
logger.warning(LICENSE_MESSAGE % d)
|
||||
|
||||
|
||||
@ -552,7 +552,7 @@ class JobEvent(BasePlaybookEvent):
|
||||
summaries = dict()
|
||||
updated_hosts_list = list()
|
||||
for host in hostnames:
|
||||
updated_hosts_list.append(host)
|
||||
updated_hosts_list.append(host.lower())
|
||||
host_id = self.host_map.get(host, None)
|
||||
if host_id not in existing_host_ids:
|
||||
host_id = None
|
||||
|
||||
@ -35,7 +35,7 @@ from django.conf import settings
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
# AWX
|
||||
from awx.main.models import Host
|
||||
from awx.main.models import Host, HostMetric, Instance
|
||||
|
||||
MAX_INSTANCES = 9999999
|
||||
|
||||
@ -383,12 +383,20 @@ class Licenser(object):
|
||||
current_instances = Host.objects.active_count()
|
||||
else:
|
||||
current_instances = 0
|
||||
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()
|
||||
if first_host:
|
||||
automated_since = int(first_host.first_automation.timestamp())
|
||||
else:
|
||||
automated_since = int(Instance.objects.order_by('id').first().created.timestamp())
|
||||
instance_count = int(attrs.get('instance_count', 0))
|
||||
attrs['current_instances'] = current_instances
|
||||
free_instances = instance_count - current_instances
|
||||
attrs['automated_instances'] = automated_instances
|
||||
attrs['automated_since'] = automated_since
|
||||
free_instances = instance_count - automated_instances
|
||||
attrs['free_instances'] = max(0, free_instances)
|
||||
|
||||
license_date = int(attrs.get('license_date', 0) or 0)
|
||||
current_date = int(time.time())
|
||||
time_remaining = license_date - current_date
|
||||
attrs['time_remaining'] = time_remaining
|
||||
|
||||
29
awx/ui_next/src/components/DetailList/NumberSinceDetail.jsx
Normal file
29
awx/ui_next/src/components/DetailList/NumberSinceDetail.jsx
Normal file
@ -0,0 +1,29 @@
|
||||
import React from 'react';
|
||||
import { node, string } from 'prop-types';
|
||||
import { t } from '@lingui/macro';
|
||||
import styled from 'styled-components';
|
||||
import { formatDateString } from '../../util/dates';
|
||||
import _Detail from './Detail';
|
||||
|
||||
const Detail = styled(_Detail)`
|
||||
word-break: break-word;
|
||||
`;
|
||||
|
||||
function NumberSinceDetail({ label, number, date, dataCy = null }) {
|
||||
const dateStr = formatDateString(date);
|
||||
|
||||
return (
|
||||
<Detail
|
||||
label={label}
|
||||
dataCy={dataCy}
|
||||
value={t`${number} since ${dateStr}`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
NumberSinceDetail.propTypes = {
|
||||
label: node.isRequired,
|
||||
number: string.isRequired,
|
||||
date: string.isRequired,
|
||||
};
|
||||
|
||||
export default NumberSinceDetail;
|
||||
@ -5,6 +5,7 @@ export { default as UserDateDetail } from './UserDateDetail';
|
||||
export { default as DetailBadge } from './DetailBadge';
|
||||
export { default as ArrayDetail } from './ArrayDetail';
|
||||
export { default as LaunchedByDetail } from './LaunchedByDetail';
|
||||
export { default as NumberSinceDetail } from './NumberSinceDetail';
|
||||
/*
|
||||
NOTE: CodeDetail cannot be imported here, as it causes circular
|
||||
dependencies in testing environment. Import it directly from
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -10,7 +10,11 @@ import {
|
||||
} from '@patternfly/react-icons';
|
||||
import RoutedTabs from '../../../../components/RoutedTabs';
|
||||
import { CardBody, CardActionsRow } from '../../../../components/Card';
|
||||
import { DetailList, Detail } from '../../../../components/DetailList';
|
||||
import {
|
||||
DetailList,
|
||||
Detail,
|
||||
NumberSinceDetail,
|
||||
} from '../../../../components/DetailList';
|
||||
import { useConfig } from '../../../../contexts/Config';
|
||||
import {
|
||||
formatDateString,
|
||||
@ -126,10 +130,21 @@ function SubscriptionDetail() {
|
||||
/>
|
||||
)}
|
||||
<Detail
|
||||
dataCy="subscription-hosts-used"
|
||||
label={t`Hosts used`}
|
||||
dataCy="subscription-hosts-imported"
|
||||
label={t`Hosts imported`}
|
||||
value={license_info.current_instances}
|
||||
/>
|
||||
<NumberSinceDetail
|
||||
dataCy="subscription-hosts-automated"
|
||||
label={t`Hosts automated`}
|
||||
number={license_info.automated_instances}
|
||||
date={
|
||||
license_info.automated_since &&
|
||||
formatDateString(
|
||||
new Date(license_info.automated_since * 1000).toISOString()
|
||||
)
|
||||
}
|
||||
/>
|
||||
<Detail
|
||||
dataCy="subscription-hosts-remaining"
|
||||
label={t`Hosts remaining`}
|
||||
|
||||
@ -14,6 +14,8 @@ const config = {
|
||||
date_expired: false,
|
||||
date_warning: true,
|
||||
free_instances: 1000,
|
||||
automated_instances: '12',
|
||||
automated_since: '1614714228',
|
||||
grace_period_remaining: 2904229,
|
||||
instance_count: 1001,
|
||||
license_date: '1614401999',
|
||||
@ -65,8 +67,9 @@ describe('<SubscriptionDetail />', () => {
|
||||
assertDetail('Trial', 'False');
|
||||
assertDetail('Expires on', '2/27/2021, 4:59:59 AM');
|
||||
assertDetail('Days remaining', '3');
|
||||
assertDetail('Hosts used', '1');
|
||||
assertDetail('Hosts imported', '1');
|
||||
assertDetail('Hosts remaining', '1000');
|
||||
assertDetail('Hosts automated', '12 since 3/2/2021, 7:43:48 PM');
|
||||
|
||||
expect(wrapper.find('Button[aria-label="edit"]').length).toBe(1);
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user