Add default project quota configuration for compute

Prior to this, the charm config did not support default quota
configurations for compute (ie. instances, compute, ram, etc.).
Default quota configuration changes will not impact existing
projects with modified quotas. Only new projects and projects with
unmodified quotas will adopt the defaults in the configuration file.

The following default quota settings were added:
instances
cores
ram
metadata_items
injected_files
injected_file_content_bytes
injected_file_path_length
key_pairs
server_groups
server_group_members

The functional test added checks that nova.conf quotas are set in
the correct section of the file.

Change-Id: Iae8c84dbfec97e1879d51963125f7674ea20ba22
Closes-Bug: 1386911
This commit is contained in:
Syed Mohammad Adnan Karim 2018-12-12 18:39:32 +00:00
parent f6be8e6fe8
commit 95cd9bfd10
14 changed files with 491 additions and 4 deletions

View File

@ -40,7 +40,7 @@ If both 'vip' and 'dns-ha' are set as they are mutually exclusive
If 'dns-ha' is set and none of the os-{admin,internal,public}-hostname(s) are
set
# Network Space support
## Network Space support
This charm supports the use of Juju Network Spaces, allowing the charm to be bound to network space configurations managed directly by Juju. This is only supported with Juju 2.0 and above.
@ -66,3 +66,24 @@ alternatively these can also be provided as part of a juju native bundle configu
NOTE: Spaces must be configured in the underlying provider prior to attempting to use them.
NOTE: Existing deployments using os-*-network configuration options will continue to function; these options are preferred over any network space binding provided if set.
## Default Quota Configuration
This charm supports default quota settings for projects.
This feature is only available from Openstack Icehouse and later releases.
The default quota settings do not overwrite post-deployment CLI quotas set by operators.
Existing projects whose quotas were not modified will adopt the new defaults when a config-changed hook occurs.
Newly created projects will also adopt the defaults set in the charm's config.
By default, the charm's quota configs are not set and openstack projects have the values below as default:
quota-instances - 10
quota-cores - 20
quota-ram - 51200
quota-metadata_items - 128
quota-injected_files - 5
quota-injected_file_content_bytes - 10240
quota-injected_file_path_length - 255
quota-key_pairs - 100
quota-server_groups - 10 (only available after Icehouse)
quota-server_group_members - 10 (only available after Icehouse)

View File

@ -449,3 +449,89 @@ options:
.
Only supported in OpenStack Newton and higher. For deployments prior to
Queens this value should be set in the neutron-gateway charm.
quota-instances:
type: int
default:
description: |
The number of instances allowed per project.
Possible Values are positive integers or 0 and -1 to disable the quota.
quota-cores:
type: int
default:
description: |
The number of instance cores or vCPUs allowed per project.
Possible Values are positive integers or 0 and -1 to disable the quota.
quota-ram:
type: int
default:
description: |
The number of megabytes of instance RAM allowed per project.
Possible Values are positive integers or 0 and -1 to disable the quota.
quota-metadata-items:
type: int
default:
description: |
The number of metadata items allowed per instance.
.
Users can associate metadata with an instance during instance creation.
This metadata takes the form of key-value pairs.
.
Possible Values are positive integers or 0 and -1 to disable the quota.
quota-injected-files:
type: int
default:
description: |
The number of injected files allowed.
.
File injection allows users to customize the personality of an instance
by injecting data into it upon boot.
Only text file injection is permitted: binary or ZIP files are not accepted.
During file injection, any existing files that match specified files are
renamed to include .bak extension appended with a timestamp.
.
Possible Values are positive integers or 0 and -1 to disable the quota.
quota-injected-file-size:
type: int
default:
description: |
The number of bytes allowed per injected file.
.
Possible Values are positive integers or 0 and -1 to disable the quota.
quota-injected-path-size:
type: int
default:
description: |
The maximum allowed injected file path length.
.
Possible Values are positive integers or 0 and -1 to disable the quota.
quota-key-pairs:
type: int
default:
description: |
The maximum number of key pairs allowed per user.
.
Users can create at least one key pair for each project and use the key
pair for multiple instances that belong to that project.
.
Possible Values are positive integers or 0 and -1 to disable the quota.
quota-server-groups:
type: int
default:
description: |
The maxiumum number of server groups per project. Not supported in Icehouse
and before
.
Server groups are used to control the affinity and anti-affinity
scheduling policy for a group of servers or instances. Reducing the
quota will not affect any existing group, but new servers will not be
allowed into groups that have become over quota.
.
Possible Values are positive integers or 0 and -1 to disable the quota.
quota-server-group-members:
type: int
default:
description: |
The maximum number of servers per server group. Not supported in Icehouse
and before
.
Possible Values are positive integers or 0 and -1 to disable the quota.

View File

@ -289,6 +289,19 @@ class NovaConfigContext(ch_context.WorkerConfigContext):
ctxt['ram_allocation_ratio'] = hookenv.config('ram-allocation-ratio')
addr = ch_ip.resolve_address(ch_ip.INTERNAL)
ctxt['host_ip'] = ch_network_ip.format_ipv6_addr(addr) or addr
ctxt['quota_instances'] = hookenv.config('quota-instances')
ctxt['quota_cores'] = hookenv.config('quota-cores')
ctxt['quota_ram'] = hookenv.config('quota-ram')
ctxt['quota_metadata_items'] = hookenv.config('quota-metadata-items')
ctxt['quota_injected_files'] = hookenv.config('quota-injected-files')
ctxt['quota_injected_file_content_bytes'] = hookenv.config(
'quota-injected-file-size')
ctxt['quota_injected_file_path_length'] = hookenv.config(
'quota-injected-path-size')
ctxt['quota_key_pairs'] = hookenv.config('quota-key-pairs')
ctxt['quota_server_groups'] = hookenv.config('quota-server-groups')
ctxt['quota_server_group_members'] = hookenv.config(
'quota-server-group-members')
return ctxt

View File

@ -39,6 +39,31 @@ disk_allocation_ratio = {{ disk_allocation_ratio }}
use_syslog={{ use_syslog }}
my_ip = {{ host_ip }}
{% if quota_instances is not none -%}
quota_instances = {{ quota_instances }}
{% endif -%}
{% if quota_cores is not none -%}
quota_cores = {{ quota_cores }}
{% endif -%}
{% if quota_ram is not none -%}
quota_ram = {{ quota_ram }}
{% endif -%}
{% if quota_metadata_items is not none -%}
quota_metadata_items = {{ quota_metadata_items }}
{% endif -%}
{% if quota_injected_files is not none -%}
quota_injected_files = {{ quota_injected_files }}
{% endif -%}
{% if quota_injected_file_content_bytes is not none -%}
quota_injected_file_content_bytes = {{ quota_injected_file_content_bytes }}
{% endif -%}
{% if quota_injected_file_path_length is not none -%}
quota_injected_file_path_length = {{ quota_injected_file_path_length }}
{% endif -%}
{% if quota_key_pairs is not none -%}
quota_key_pairs = {{ quota_key_pairs }}
{% endif -%}
{% if memcached_servers %}
memcached_servers = {{ memcached_servers }}
{% endif %}

View File

@ -39,6 +39,37 @@ disk_allocation_ratio = {{ disk_allocation_ratio }}
use_syslog={{ use_syslog }}
my_ip = {{ host_ip }}
{% if quota_instances is not none -%}
quota_instances = {{ quota_instances }}
{% endif -%}
{% if quota_cores is not none -%}
quota_cores = {{ quota_cores }}
{% endif -%}
{% if quota_ram is not none -%}
quota_ram = {{ quota_ram }}
{% endif -%}
{% if quota_metadata_items is not none -%}
quota_metadata_items = {{ quota_metadata_items }}
{% endif -%}
{% if quota_injected_files is not none -%}
quota_injected_files = {{ quota_injected_files }}
{% endif -%}
{% if quota_injected_file_content_bytes is not none -%}
quota_injected_file_content_bytes = {{ quota_injected_file_content_bytes }}
{% endif -%}
{% if quota_injected_file_path_length is not none -%}
quota_injected_file_path_length = {{ quota_injected_file_path_length }}
{% endif -%}
{% if quota_key_pairs is not none -%}
quota_key_pairs = {{ quota_key_pairs }}
{% endif -%}
{% if quota_server_groups is not none -%}
quota_server_groups = {{ quota_server_groups }}
{% endif -%}
{% if quota_server_group_members is not none -%}
quota_server_group_members = {{ quota_server_group_members }}
{% endif -%}
{% if memcached_servers %}
memcached_servers = {{ memcached_servers }}
{% endif %}

View File

@ -48,6 +48,37 @@ disk_allocation_ratio = {{ disk_allocation_ratio }}
use_syslog={{ use_syslog }}
my_ip = {{ host_ip }}
{% if quota_instances is not none -%}
quota_instances = {{ quota_instances }}
{% endif -%}
{% if quota_cores is not none -%}
quota_cores = {{ quota_cores }}
{% endif -%}
{% if quota_ram is not none -%}
quota_ram = {{ quota_ram }}
{% endif -%}
{% if quota_metadata_items is not none -%}
quota_metadata_items = {{ quota_metadata_items }}
{% endif -%}
{% if quota_injected_files is not none -%}
quota_injected_files = {{ quota_injected_files }}
{% endif -%}
{% if quota_injected_file_content_bytes is not none -%}
quota_injected_file_content_bytes = {{ quota_injected_file_content_bytes }}
{% endif -%}
{% if quota_injected_file_path_length is not none -%}
quota_injected_file_path_length = {{ quota_injected_file_path_length }}
{% endif -%}
{% if quota_key_pairs is not none -%}
quota_key_pairs = {{ quota_key_pairs }}
{% endif -%}
{% if quota_server_groups is not none -%}
quota_server_groups = {{ quota_server_groups }}
{% endif -%}
{% if quota_server_group_members is not none -%}
quota_server_group_members = {{ quota_server_group_members }}
{% endif -%}
{% if memcached_servers %}
memcached_servers = {{ memcached_servers }}
{% endif %}

View File

@ -48,6 +48,37 @@ disk_allocation_ratio = {{ disk_allocation_ratio }}
use_syslog={{ use_syslog }}
my_ip = {{ host_ip }}
{% if quota_instances is not none -%}
quota_instances = {{ quota_instances }}
{% endif -%}
{% if quota_cores is not none -%}
quota_cores = {{ quota_cores }}
{% endif -%}
{% if quota_ram is not none -%}
quota_ram = {{ quota_ram }}
{% endif -%}
{% if quota_metadata_items is not none -%}
quota_metadata_items = {{ quota_metadata_items }}
{% endif -%}
{% if quota_injected_files is not none -%}
quota_injected_files = {{ quota_injected_files }}
{% endif -%}
{% if quota_injected_file_content_bytes is not none -%}
quota_injected_file_content_bytes = {{ quota_injected_file_content_bytes }}
{% endif -%}
{% if quota_injected_file_path_length is not none -%}
quota_injected_file_path_length = {{ quota_injected_file_path_length }}
{% endif -%}
{% if quota_key_pairs is not none -%}
quota_key_pairs = {{ quota_key_pairs }}
{% endif -%}
{% if quota_server_groups is not none -%}
quota_server_groups = {{ quota_server_groups }}
{% endif -%}
{% if quota_server_group_members is not none -%}
quota_server_group_members = {{ quota_server_group_members }}
{% endif -%}
{% if memcached_servers %}
memcached_servers = {{ memcached_servers }}
{% endif %}

View File

@ -45,6 +45,37 @@ disk_allocation_ratio = {{ disk_allocation_ratio }}
use_syslog={{ use_syslog }}
my_ip = {{ host_ip }}
{% if quota_instances is not none -%}
quota_instances = {{ quota_instances }}
{% endif -%}
{% if quota_cores is not none -%}
quota_cores = {{ quota_cores }}
{% endif -%}
{% if quota_ram is not none -%}
quota_ram = {{ quota_ram }}
{% endif -%}
{% if quota_metadata_items is not none -%}
quota_metadata_items = {{ quota_metadata_items }}
{% endif -%}
{% if quota_injected_files is not none -%}
quota_injected_files = {{ quota_injected_files }}
{% endif -%}
{% if quota_injected_file_content_bytes is not none -%}
quota_injected_file_content_bytes = {{ quota_injected_file_content_bytes }}
{% endif -%}
{% if quota_injected_file_path_length is not none -%}
quota_injected_file_path_length = {{ quota_injected_file_path_length }}
{% endif -%}
{% if quota_key_pairs is not none -%}
quota_key_pairs = {{ quota_key_pairs }}
{% endif -%}
{% if quota_server_groups is not none -%}
quota_server_groups = {{ quota_server_groups }}
{% endif -%}
{% if quota_server_group_members is not none -%}
quota_server_group_members = {{ quota_server_group_members }}
{% endif -%}
{% include "parts/novnc" %}
{% if rbd_pool -%}

View File

@ -47,6 +47,37 @@ disk_allocation_ratio = {{ disk_allocation_ratio }}
use_syslog={{ use_syslog }}
my_ip = {{ host_ip }}
{% if quota_instances is not none -%}
quota_instances = {{ quota_instances }}
{% endif -%}
{% if quota_cores is not none -%}
quota_cores = {{ quota_cores }}
{% endif -%}
{% if quota_ram is not none -%}
quota_ram = {{ quota_ram }}
{% endif -%}
{% if quota_metadata_items is not none -%}
quota_metadata_items = {{ quota_metadata_items }}
{% endif -%}
{% if quota_injected_files is not none -%}
quota_injected_files = {{ quota_injected_files }}
{% endif -%}
{% if quota_injected_file_content_bytes is not none -%}
quota_injected_file_content_bytes = {{ quota_injected_file_content_bytes }}
{% endif -%}
{% if quota_injected_file_path_length is not none -%}
quota_injected_file_path_length = {{ quota_injected_file_path_length }}
{% endif -%}
{% if quota_key_pairs is not none -%}
quota_key_pairs = {{ quota_key_pairs }}
{% endif -%}
{% if quota_server_groups is not none -%}
quota_server_groups = {{ quota_server_groups }}
{% endif -%}
{% if quota_server_group_members is not none -%}
quota_server_group_members = {{ quota_server_group_members }}
{% endif -%}
{% include "parts/novnc" %}
{% if rbd_pool -%}
@ -178,4 +209,3 @@ memcache_servers = {{ memcached_servers }}
[wsgi]
api_paste_config=/etc/nova/api-paste.ini

View File

@ -201,3 +201,35 @@ alias = {{ alias }}
{% endfor -%}
{% include "section-oslo-middleware" %}
[quota]
{% if quota_instances is not none -%}
instances = {{ quota_instances }}
{% endif -%}
{% if quota_cores is not none -%}
cores = {{ quota_cores }}
{% endif -%}
{% if quota_ram is not none -%}
ram = {{ quota_ram }}
{% endif -%}
{% if quota_metadata_items is not none -%}
metadata_items = {{ quota_metadata_items }}
{% endif -%}
{% if quota_injected_files is not none -%}
injected_files = {{ quota_injected_files }}
{% endif -%}
{% if quota_injected_file_content_bytes is not none -%}
injected_file_content_bytes = {{ quota_injected_file_content_bytes }}
{% endif -%}
{% if quota_injected_file_path_length is not none -%}
injected_file_path_length = {{ quota_injected_file_path_length }}
{% endif -%}
{% if quota_key_pairs is not none -%}
key_pairs = {{ quota_key_pairs }}
{% endif -%}
{% if quota_server_groups is not none -%}
server_groups = {{ quota_server_groups }}
{% endif -%}
{% if quota_server_group_members is not none -%}
server_group_members = {{ quota_server_group_members }}
{% endif -%}

View File

@ -208,3 +208,35 @@ alias = {{ alias }}
{% endfor -%}
{% include "section-oslo-middleware" %}
[quota]
{% if quota_instances is not none -%}
instances = {{ quota_instances }}
{% endif -%}
{% if quota_cores is not none -%}
cores = {{ quota_cores }}
{% endif -%}
{% if quota_ram is not none -%}
ram = {{ quota_ram }}
{% endif -%}
{% if quota_metadata_items is not none -%}
metadata_items = {{ quota_metadata_items }}
{% endif -%}
{% if quota_injected_files is not none -%}
injected_files = {{ quota_injected_files }}
{% endif -%}
{% if quota_injected_file_content_bytes is not none -%}
injected_file_content_bytes = {{ quota_injected_file_content_bytes }}
{% endif -%}
{% if quota_injected_file_path_length is not none -%}
injected_file_path_length = {{ quota_injected_file_path_length }}
{% endif -%}
{% if quota_key_pairs is not none -%}
key_pairs = {{ quota_key_pairs }}
{% endif -%}
{% if quota_server_groups is not none -%}
server_groups = {{ quota_server_groups }}
{% endif -%}
{% if quota_server_group_members is not none -%}
server_group_members = {{ quota_server_group_members }}
{% endif -%}

View File

@ -214,3 +214,35 @@ alias = {{ alias }}
{% endfor -%}
{% include "section-oslo-middleware" %}
[quota]
{% if quota_instances is not none -%}
instances = {{ quota_instances }}
{% endif -%}
{% if quota_cores is not none -%}
cores = {{ quota_cores }}
{% endif -%}
{% if quota_ram is not none -%}
ram = {{ quota_ram }}
{% endif -%}
{% if quota_metadata_items is not none -%}
metadata_items = {{ quota_metadata_items }}
{% endif -%}
{% if quota_injected_files is not none -%}
injected_files = {{ quota_injected_files }}
{% endif -%}
{% if quota_injected_file_content_bytes is not none -%}
injected_file_content_bytes = {{ quota_injected_file_content_bytes }}
{% endif -%}
{% if quota_injected_file_path_length is not none -%}
injected_file_path_length = {{ quota_injected_file_path_length }}
{% endif -%}
{% if quota_key_pairs is not none -%}
key_pairs = {{ quota_key_pairs }}
{% endif -%}
{% if quota_server_groups is not none -%}
server_groups = {{ quota_server_groups }}
{% endif -%}
{% if quota_server_group_members is not none -%}
server_group_members = {{ quota_server_group_members }}
{% endif -%}

View File

@ -15,6 +15,7 @@
import amulet
import json
import tempfile
import os
from charmhelpers.contrib.openstack.amulet.deployment import (
OpenStackAmuletDeployment
@ -823,8 +824,8 @@ class NovaCCBasicDeployment(OpenStackAmuletDeployment):
services['apache2'] = conf_file
# Expected default and alternate values
flags_default = 'quota_cores=20,quota_instances=40,quota_ram=102400'
flags_alt = 'quota_cores=10,quota_instances=20,quota_ram=51200'
flags_default = 'cpu-allocation-ratio=16.0,ram-allocation-ratio=1.5'
flags_alt = 'cpu-allocation-ratio=32.0,ram-allocation-ratio=3.0'
set_default = {'config-flags': flags_default}
set_alternate = {'config-flags': flags_alt}
@ -857,3 +858,64 @@ class NovaCCBasicDeployment(OpenStackAmuletDeployment):
action_id = u.run_action(self.nova_cc_sentry, "resume")
assert u.wait_on_action(action_id), "Resume action failed"
self._assert_services(should_run=True)
def test_902_default_quota_settings(self):
"""Test default quota settings."""
config_file = '/etc/nova/nova.conf'
quotas = {
'quota-instances': 20,
'quota-cores': 40,
'quota-ram': 102400,
'quota-metadata-items': 256,
'quota-injected-files': 10,
'quota-injected-file-size': 20480,
'quota-injected-path-size': 512,
'quota-key-pairs': 200,
'quota-server-groups': 20,
'quota-server-group-members': 20,
}
cmp_os_release = CompareOpenStackReleases(
self._get_openstack_release_string()
)
if cmp_os_release > 'newton':
section = 'quota'
else:
section = 'DEFAULT'
u.log.debug('Changing quotas in charm config')
self.d.configure('nova-cloud-controller', quotas)
self._auto_wait_for_status(exclude_services=self.exclude_services)
self.d.sentry.wait()
if not u.validate_config_data(self.nova_cc_sentry, config_file,
section, quotas):
amulet.raise_status(amulet.FAIL, msg='update failed')
u.log.debug('New default quotas found in correct section in nova.conf')
u.log.debug('test_902_default_quota_settings PASSED - (OK)')
# Amulet test framework currently does not support setting charm-config
# values to None when an integer is expected by the configuration.
# By default, the quota settings are not written to nova.conf unless
# explicitly set. In order to keep tests idempotent, the following juju
# CLI commands are run to reset the quota values to None.
os.system("juju config nova-cloud-controller --reset"
" quota-instances")
os.system("juju config nova-cloud-controller --reset"
" quota-cores")
os.system("juju config nova-cloud-controller --reset"
" quota-ram")
os.system("juju config nova-cloud-controller --reset"
" quota-metadata-items")
os.system("juju config nova-cloud-controller --reset"
" quota-injected-files")
os.system("juju config nova-cloud-controller --reset"
" quota-injected-file-size")
os.system("juju config nova-cloud-controller --reset"
" quota-injected-path-size")
os.system("juju config nova-cloud-controller --reset"
" quota-key-pairs")
os.system("juju config nova-cloud-controller --reset"
" quota-server-groups")
os.system("juju config nova-cloud-controller --reset"
" quota-server-group-members")
self._auto_wait_for_status(exclude_services=self.exclude_services)

View File

@ -323,6 +323,36 @@ class NovaComputeContextTests(CharmTestCase):
self.config('ram-allocation-ratio'))
self.assertEqual(ctxt['disk_allocation_ratio'],
self.config('disk-allocation-ratio'))
self.assertEqual(ctxt['quota_instances'],
self.config('quota-instances'))
self.assertEqual(ctxt['quota_instances'], None)
self.assertEqual(ctxt['quota_cores'],
self.config('quota-cores'))
self.assertEqual(ctxt['quota_cores'], None)
self.assertEqual(ctxt['quota_ram'],
self.config('quota-ram'))
self.assertEqual(ctxt['quota_ram'], None)
self.assertEqual(ctxt['quota_metadata_items'],
self.config('quota-metadata-items'))
self.assertEqual(ctxt['quota_metadata_items'], None)
self.assertEqual(ctxt['quota_injected_files'],
self.config('quota-injected-files'))
self.assertEqual(ctxt['quota_injected_files'], None)
self.assertEqual(ctxt['quota_injected_file_content_bytes'],
self.config('quota-injected-file-size'))
self.assertEqual(ctxt['quota_injected_file_content_bytes'], None)
self.assertEqual(ctxt['quota_injected_file_path_length'],
self.config('quota-injected-path-size'))
self.assertEqual(ctxt['quota_injected_file_path_length'], None)
self.assertEqual(ctxt['quota_key_pairs'],
self.config('quota-key-pairs'))
self.assertEqual(ctxt['quota_key_pairs'], None)
self.assertEqual(ctxt['quota_server_groups'],
self.config('quota-server-groups'))
self.assertEqual(ctxt['quota_server_groups'], None)
self.assertEqual(ctxt['quota_server_group_members'],
self.config('quota-server-group-members'))
self.assertEqual(ctxt['quota_server_group_members'], None)
_pci_alias1 = {
"name": "IntelNIC",