diff --git a/config.yaml b/config.yaml index f220a2bb..5a11b4d7 100644 --- a/config.yaml +++ b/config.yaml @@ -12,6 +12,11 @@ options: default: False description: | Setting this to True will allow supporting services to log to syslog. + audit-middleware: + type: boolean + default: False + description: | + Enable Keystone auditing middleware for logging API calls. openstack-origin: type: string default: bobcat @@ -187,7 +192,7 @@ options: the nova-cloud-controller serving the request to be used. console-keymap: type: string - default: 'en-us' + default: "en-us" description: | Console keymap. console-ssl-cert: @@ -224,18 +229,18 @@ options: type: boolean default: True description: | - Enable new nova-compute services on this host automatically. - When a new nova-compute service starts up, it gets registered in the - database as an enabled service. Sometimes it can be useful to register - new compute services in disabled state and then enabled them at a later - point in time. This option only sets this behavior for nova-compute - services, it does not auto-disable other services like nova-conductor, - nova-scheduler, nova-consoleauth, or nova-osapi_compute. - Possible values: True: Each new compute service is enabled as soon as - it registers itself. False: Compute services must be enabled via an - os-services REST API call or with the CLI with - nova service-enable , otherwise they are not ready - to use. + Enable new nova-compute services on this host automatically. + When a new nova-compute service starts up, it gets registered in the + database as an enabled service. Sometimes it can be useful to register + new compute services in disabled state and then enabled them at a later + point in time. This option only sets this behavior for nova-compute + services, it does not auto-disable other services like nova-conductor, + nova-scheduler, nova-consoleauth, or nova-osapi_compute. + Possible values: True: Each new compute service is enabled as soon as + it registers itself. False: Compute services must be enabled via an + os-services REST API call or with the CLI with + nova service-enable , otherwise they are not ready + to use. worker-multiplier: type: float default: diff --git a/hooks/nova_cc_utils.py b/hooks/nova_cc_utils.py index 4dcc52ab..5b02fe4f 100644 --- a/hooks/nova_cc_utils.py +++ b/hooks/nova_cc_utils.py @@ -95,6 +95,7 @@ ] AWS_COMPAT_SERVICES = ['nova-api-ec2', 'nova-objectstore'] +AUDIT_SERVICES = ['nova-api-os-compute'] SERVICE_BLACKLIST = { 'liberty': AWS_COMPAT_SERVICES, 'newton': ['nova-cert'], @@ -286,6 +287,18 @@ def resource_map(actual_services=True): ssl_dir=NOVA_CONF_DIR) ) + if cmp_os_release >= 'yoga': + # Conditionally render audit middleware for yoga and later + NOVA_AUDIT_MAP = '%s/api_audit_map.conf' % NOVA_CONF_DIR + _resource_map[NOVA_CONF]['contexts'].append( + ch_context.KeystoneAuditMiddleware(service='nova')) + _resource_map[NOVA_API_PASTE]['contexts'].append( + ch_context.KeystoneAuditMiddleware(service='nova')) + _BASE_RESOURCE_MAP[NOVA_AUDIT_MAP] = { + 'contexts': [ch_context.KeystoneAuditMiddleware(service='nova')], + 'services': AUDIT_SERVICES, + } + if common.console_attributes('services'): _resource_map[NOVA_CONF]['services'] += ( common.console_attributes('services')) diff --git a/templates/yoga/api-paste.ini b/templates/yoga/api-paste.ini new file mode 100644 index 00000000..5a930a6b --- /dev/null +++ b/templates/yoga/api-paste.ini @@ -0,0 +1,137 @@ +############ +# Metadata # +############ +[composite:metadata] +use = egg:Paste#urlmap +/: meta + +[pipeline:meta] +pipeline = cors metaapp + +[app:metaapp] +paste.app_factory = nova.api.metadata.handler:MetadataRequestHandler.factory + +############# +# OpenStack # +############# + +[composite:osapi_compute] +use = call:nova.api.openstack.urlmap:urlmap_factory +/: oscomputeversions +# starting in Liberty the v21 implementation replaces the v2 +# implementation and is suggested that you use it as the default. If +# this causes issues with your clients you can rollback to the +# *frozen* v2 api by commenting out the above stanza and using the +# following instead:: +# /v2: openstack_compute_api_legacy_v2 +# if rolling back to v2 fixes your issue please file a critical bug +# at - https://bugs.launchpad.net/nova/+bugs +# +# v21 is an exactly feature match for v2, except it has more stringent +# input validation on the wsgi surface (prevents fuzzing early on the +# API). It also provides new features via API microversions which are +# opt into for clients. Unaware clients will receive the same frozen +# v2 API feature set, but with some relaxed validation +/v2: openstack_compute_api_v21_legacy_v2_compatible +/v2.1: openstack_compute_api_v21 + +# NOTE: this is deprecated in favor of openstack_compute_api_v21_legacy_v2_compatible +[composite:openstack_compute_api_legacy_v2] +use = call:nova.api.auth:pipeline_factory +noauth2 = cors compute_req_id faultwrap sizelimit noauth2 legacy_ratelimit osapi_compute_app_legacy_v2 +{% if audit_middleware and service_name -%} +keystone = cors compute_req_id faultwrap sizelimit authtoken keystonecontext legacy_ratelimit audit osapi_compute_app_legacy_v2 +keystone_nolimit = cors compute_req_id faultwrap sizelimit authtoken keystonecontext audit osapi_compute_app_legacy_v2 +{% else -%} +keystone = cors compute_req_id faultwrap sizelimit authtoken keystonecontext legacy_ratelimit osapi_compute_app_legacy_v2 +keystone_nolimit = cors compute_req_id faultwrap sizelimit authtoken keystonecontext osapi_compute_app_legacy_v2 +{% endif %} + +[composite:openstack_compute_api_v21] +use = call:nova.api.auth:pipeline_factory_v21 +noauth2 = cors http_proxy_to_wsgi compute_req_id faultwrap sizelimit noauth2 osapi_compute_app_v21 +{% if audit_middleware and service_name -%} +keystone = cors http_proxy_to_wsgi compute_req_id faultwrap sizelimit authtoken keystonecontext audit osapi_compute_app_v21 +{% else -%} +keystone = cors http_proxy_to_wsgi compute_req_id faultwrap sizelimit authtoken keystonecontext osapi_compute_app_v21 +{% endif %} + +[composite:openstack_compute_api_v21_legacy_v2_compatible] +use = call:nova.api.auth:pipeline_factory_v21 +noauth2 = cors http_proxy_to_wsgi compute_req_id faultwrap sizelimit noauth2 legacy_v2_compatible osapi_compute_app_v21 +{% if audit_middleware and service_name -%} +keystone = cors http_proxy_to_wsgi compute_req_id faultwrap sizelimit authtoken keystonecontext legacy_v2_compatible audit osapi_compute_app_v21 +{% else -%} +keystone = cors http_proxy_to_wsgi compute_req_id faultwrap sizelimit authtoken keystonecontext legacy_v2_compatible osapi_compute_app_v21 +{% endif %} + +[filter:request_id] +paste.filter_factory = oslo_middleware:RequestId.factory + +[filter:compute_req_id] +paste.filter_factory = nova.api.compute_req_id:ComputeReqIdMiddleware.factory + +[filter:faultwrap] +paste.filter_factory = nova.api.openstack:FaultWrapper.factory + +[filter:noauth2] +paste.filter_factory = nova.api.openstack.auth:NoAuthMiddleware.factory + +[filter:legacy_ratelimit] +paste.filter_factory = nova.api.openstack.compute.limits:RateLimitingMiddleware.factory +{% if api_rate_limit_rules -%} +limits = {{ api_rate_limit_rules }} +{% endif -%} + +[filter:sizelimit] +paste.filter_factory = oslo_middleware:RequestBodySizeLimiter.factory + +[filter:http_proxy_to_wsgi] +paste.filter_factory = oslo_middleware.http_proxy_to_wsgi:HTTPProxyToWSGI.factory + +[filter:legacy_v2_compatible] +paste.filter_factory = nova.api.openstack:LegacyV2CompatibleWrapper.factory + +[app:osapi_compute_app_legacy_v2] +paste.app_factory = nova.api.openstack.compute:APIRouter.factory + +[app:osapi_compute_app_v21] +paste.app_factory = nova.api.openstack.compute:APIRouterV21.factory + +[pipeline:oscomputeversions] +pipeline = faultwrap http_proxy_to_wsgi oscomputeversionapp + +[app:oscomputeversionapp] +paste.app_factory = nova.api.openstack.compute.versions:Versions.factory + +########## +# Shared # +########## + +[filter:cors] +paste.filter_factory = oslo_middleware.cors:filter_factory +oslo_config_project = nova + +[filter:keystonecontext] +paste.filter_factory = nova.api.auth:NovaKeystoneContext.factory + +[filter:authtoken] +paste.filter_factory = keystonemiddleware.auth_token:filter_factory + +{% include "section-filter-audit" %} + +{% if service_host -%} +# NOTE(jamespage) - not used - but required for relation to nova-compute +service_protocol = {{ service_protocol }} +service_host = {{ service_host }} +service_port = {{ service_port }} +auth_host = {{ auth_host }} +auth_port = {{ auth_port }} +auth_protocol = {{ auth_protocol }} +admin_tenant_name = {{ admin_tenant_name }} +admin_user = {{ admin_user }} +admin_password = {{ admin_password }} +{% if admin_domain_name -%} +admin_domain_name = {{ admin_domain_name }} +{% endif -%} +{% endif -%} diff --git a/templates/yoga/api_audit_map.conf b/templates/yoga/api_audit_map.conf new file mode 100644 index 00000000..d819fe6f --- /dev/null +++ b/templates/yoga/api_audit_map.conf @@ -0,0 +1,72 @@ +[DEFAULT] +# default target endpoint type +# should match the endpoint type defined in service catalog +target_endpoint_type = None + +[custom_actions] +enable = enable +disable = disable +delete = delete +startup = start/startup +shutdown = stop/shutdown +reboot = start/reboot +os-migrations/get = read +os-server-password/post = update + +# possible end path of api requests +[path_keywords] +add = None +action = None +enable = None +disable = None +configure-project = None +defaults = None +delete = None +detail = None +diagnostics = None +entries = entry +extensions = alias +flavors = flavor +images = image +ips = label +limits = None +metadata = key +os-agents = os-agent +os-aggregates = os-aggregate +os-availability-zone = None +os-certificates = None +os-cloudpipe = None +os-fixed-ips = ip +os-extra_specs = key +os-flavor-access = None +os-floating-ip-dns = domain +os-floating-ips-bulk = host +os-floating-ip-pools = None +os-floating-ips = floating-ip +os-hosts = host +os-hypervisors = hypervisor +os-instance-actions = instance-action +os-keypairs = keypair +os-migrations = None +os-networks = network +os-quota-sets = tenant +os-security-groups = security_group +os-security-group-rules = rule +os-server-password = None +os-services = None +os-simple-tenant-usage = tenant +os-virtual-interfaces = None +os-volume_attachments = attachment +os-volumes_boot = None +os-volumes = volume +os-volume-types = volume-type +os-snapshots = snapshot +reboot = None +servers = server +shutdown = None +startup = None +statistics = None + +# map endpoint type defined in service catalog to CADF typeURI +[service_endpoints] +compute = service/compute \ No newline at end of file diff --git a/templates/yoga/nova.conf b/templates/yoga/nova.conf new file mode 100644 index 00000000..22f51410 --- /dev/null +++ b/templates/yoga/nova.conf @@ -0,0 +1,310 @@ +# yoga +############################################################################### +# [ WARNING ] +# Configuration file maintained by Juju. Local changes may be overwritten. +############################################################################### +[DEFAULT] +verbose={{ verbose }} +debug={{ debug }} +dhcpbridge_flagfile=/etc/nova/nova.conf +dhcpbridge=/usr/bin/nova-dhcpbridge +logdir=/var/log/nova +state_path=/var/lib/nova +iscsi_helper=tgtadm +libvirt_use_virtio_for_bridges=True +connection_type=libvirt +root_helper=sudo nova-rootwrap /etc/nova/rootwrap.conf +volumes_path=/var/lib/nova/volumes +enabled_apis=osapi_compute,metadata +compute_driver=libvirt.LibvirtDriver +use_ipv6 = {{ use_ipv6 }} +osapi_compute_listen = {{ bind_host }} +{% if unique_server_names -%} +osapi_compute_unique_unique_server_names = {{ unique_server_names }} +{% endif -%} +metadata_host = {{ bind_host }} +s3_listen = {{ bind_host }} +enable_new_services = {{ enable_new_services }} + +{% if debug -%} +default_log_levels = "amqp=WARN, amqplib=WARN, boto=WARN, qpid=WARN, sqlalchemy=WARN, suds=INFO, oslo.messaging=INFO, oslo_messaging=DEBUG, iso8601=WARN, requests.packages.urllib3.connectionpool=WARN, urllib3.connectionpool=WARN, websocket=WARN, requests.packages.urllib3.util.retry=WARN, urllib3.util.retry=WARN, keystonemiddleware=WARN, routes.middleware=WARN, stevedore=WARN, taskflow=WARN, keystoneauth=WARN, oslo.cache=INFO, dogpile.core.dogpile=INFO, glanceclient=WARN, oslo.privsep.daemon=INFO" +glance.debug = True +{% endif -%} + +{% if transport_url %} +transport_url = {{ transport_url }} +{% endif %} + +{% if dns_domain -%} +# Per LP#1805645, dhcp_domain needs to be configured for nova-metadata-api +# It gets this information from neutron. +dhcp_domain = {{ dns_domain }} +{% endif -%} + +osapi_compute_workers = {{ workers }} + +cpu_allocation_ratio = {{ cpu_allocation_ratio }} +ram_allocation_ratio = {{ ram_allocation_ratio }} +disk_allocation_ratio = {{ disk_allocation_ratio }} + +use_syslog={{ use_syslog }} +my_ip = {{ host_ip }} + +{% include "parts/novnc" %} + +{% if max_local_block_devices is not none -%} +max_local_block_devices = {{ max_local_block_devices }} +{% endif -%} + +{% if rbd_pool -%} +rbd_pool = {{ rbd_pool }} +rbd_user = {{ rbd_user }} +rbd_secret_uuid = {{ rbd_secret_uuid }} +{% endif -%} + +{% if neutron_plugin and neutron_plugin in ('ovs', 'midonet') -%} +libvirt_vif_driver = nova.virt.libvirt.vif.LibvirtGenericVIFDriver +libvirt_user_virtio_for_bridges = True +{% if neutron_security_groups -%} +security_group_api = {{ network_manager }} +nova_firewall_driver = nova.virt.firewall.NoopFirewallDriver +{% endif -%} +{% if external_network -%} +default_floating_pool = {{ external_network }} +{% endif -%} +{% endif -%} + +{% if neutron_plugin and neutron_plugin == 'vsp' -%} +neutron_ovs_bridge = alubr0 +{% endif -%} + +{% if neutron_plugin and neutron_plugin == 'nvp' -%} +security_group_api = neutron +nova_firewall_driver = nova.virt.firewall.NoopFirewallDriver +{% if external_network -%} +default_floating_pool = {{ external_network }} +{% endif -%} +{% endif -%} + +{% if neutron_plugin and neutron_plugin == 'Calico' -%} +security_group_api = neutron +nova_firewall_driver = nova.virt.firewall.NoopFirewallDriver +{% endif -%} + +{% if neutron_plugin and neutron_plugin == 'plumgrid' -%} +security_group_api=neutron +firewall_driver = nova.virt.firewall.NoopFirewallDriver +{% endif -%} + +{% if network_manager_config -%} +{% for key, value in network_manager_config.items() -%} +{{ key }} = {{ value }} +{% endfor -%} +{% endif -%} + +{% if network_manager and network_manager == 'neutron' -%} +network_api_class = nova.network.neutronv2.api.API +{% else -%} +network_manager = nova.network.manager.FlatDHCPManager +{% endif -%} + +{% if default_floating_pool -%} +default_floating_pool = {{ default_floating_pool }} +{% endif -%} + +{% if volume_service -%} +volume_api_class=nova.volume.cinder.API +{% endif -%} + +{% if allow_resize_to_same_host -%} +allow_resize_to_same_host = True +{% endif -%} + +{% if user_config_flags -%} +{% for key, value in user_config_flags.items() -%} +{{ key }} = {{ value }} +{% endfor -%} +{% endif -%} + +{% if listen_ports -%} +{% for key, value in listen_ports.items() -%} +{{ key }} = {{ value }} +{% endfor -%} +{% endif -%} + +{% if sections and 'DEFAULT' in sections -%} +{% for key, value in sections['DEFAULT'] -%} +{{ key }} = {{ value }} +{% endfor -%} +{% endif %} + +[upgrade_levels] +compute = auto + +{% include "section-zeromq" %} + +{% include "parts/database-v2" %} + +{% include "parts/database-api" %} + +{% if glance_api_servers -%} +[glance] +api_servers = {{ glance_api_servers }} +{% endif -%} + +{% if network_manager and network_manager == 'neutron' %} +{% include "parts/section-neutron" %} +{% endif %} + +{% include "section-keystone-authtoken-mitaka" %} + +{% include "section-service-user" %} + +{% include "parts/section-cinder" %} + +[osapi_v3] +enabled=True + +{% include "parts/cell" %} + +[conductor] +workers = {{ workers }} + +{% include "section-oslo-messaging-rabbit" %} + +{% include "section-oslo-notifications" %} + +[oslo_concurrency] +lock_path=/var/lock/nova + +[vnc] +{% if console_access_port and console_access_protocol == 'novnc' -%} +novncproxy_port = {{ console_access_port }} +{% endif %} +{% if console_access_port and console_access_protocol == 'xvpvnc' -%} +xvpvncproxy_port = {{ console_access_port }} +{% endif %} + +[spice] +{% include "parts/spice" %} +{% if console_access_port and console_access_protocol == 'spice' -%} +html5proxy_port = {{ console_access_port }} +{% endif %} + +{% include "parts/section-serial-console" %} + +{% include "parts/section-console" %} + +{% if memcached_servers %} +[cache] +enabled = true +backend = oslo_cache.memcache_pool +memcache_servers = {{ memcached_servers }} +{% endif %} + +{% include "section-placement" %} + +[scheduler] +# NOTE(jamespage): perform automatic host cell mapping +# until we can orchestrate this better +# using the nova-cc <--> nova-compute +# relation +discover_hosts_in_cells_interval = 30 + +workers = {{ workers }} + +{% if limit_tenants_to_placement_aggregate -%} +limit_tenants_to_placement_aggregate = True +{%- endif %} +{%- if placement_aggregate_required_for_tenants %} +placement_aggregate_required_for_tenants = True +{%- endif %} +{%- if enable_isolated_aggregate_filtering %} +enable_isolated_aggregate_filtering = True +{%- endif %} + +[filter_scheduler] +{% if additional_neutron_filters is defined %} +enabled_filters = {{ scheduler_default_filters }},{{ additional_neutron_filters }} +{% else %} +enabled_filters = {{ scheduler_default_filters }} +{% endif %} + +{% if not skip_hosts_with_build_failures %} +# Disable BuildFailureWeigher as any failed build will result +# in a very low weighting for the hypervisor, resulting in +# instances all being scheduled to hypervisors with no build +# failures. +# https://bugs.launchpad.net/charm-nova-cloud-controller/+bug/1818239 +build_failure_weight_multiplier = 0.0 +{% endif %} + +{%- if scheduler_host_subset_size %} +host_subset_size = {{ scheduler_host_subset_size }} +{%- endif %} + +{%- if scheduler_max_attempts %} +max_attempts = {{ scheduler_max_attempts }} +{%- endif %} + +[api] +auth_strategy=keystone +{% if vendor_data or vendor_data_url -%} +vendordata_providers = {{ vendordata_providers }} +{% if vendor_data -%} +vendordata_jsonfile_path = /etc/nova/vendor_data.json +{% endif -%} +{% if vendor_data_url -%} +vendordata_dynamic_targets = {{ vendor_data_url }} +{% endif -%} +{% endif -%} + +[wsgi] +api_paste_config=/etc/nova/api-paste.ini + +[pci] +{% if pci_alias %} +alias = {{ pci_alias }} +{% endif %} +{% for alias in pci_aliases -%} +alias = {{ alias }} +{% endfor -%} + +{% include "section-oslo-middleware" %} + +{% include "section-audit-middleware-notifications" %} + +[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 -%} +{% if quota_count_usage_from_placement is sameas true -%} +count_usage_from_placement = {{ quota_count_usage_from_placement }} +{% endif -%} diff --git a/tests/tests.yaml b/tests/tests.yaml index 8a3bff6e..d20bf37f 100644 --- a/tests/tests.yaml +++ b/tests/tests.yaml @@ -26,7 +26,11 @@ tests: - zaza.openstack.charm_tests.nova.tests.SecurityTests - zaza.openstack.charm_tests.nova.tests.NovaCloudController - zaza.openstack.charm_tests.nova.tests.NovaCloudControllerActionTest + - zaza.openstack.charm_tests.audit.tests.KeystoneAuditMiddlewareTest tests_options: + audit-middleware: + service: nova + application: nova-cloud-controller force_deploy: - noble-caracal