diff --git a/.gitreview b/.gitreview index 659ee49..9481037 100644 --- a/.gitreview +++ b/.gitreview @@ -2,3 +2,5 @@ host=review.opendev.org port=29418 project=openstack/charm-glance.git + +defaultbranch=stable/yoga diff --git a/.zuul.yaml b/.zuul.yaml index 7ffc71c..b590b54 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -1,4 +1,4 @@ - project: templates: - - openstack-python3-charm-yoga-jobs - - openstack-cover-jobs + - openstack-python3-charm-jobs-legacy + - openstack-cover-charm-jobs diff --git a/build-requirements.txt b/build-requirements.txt deleted file mode 100644 index b6d2452..0000000 --- a/build-requirements.txt +++ /dev/null @@ -1,7 +0,0 @@ -# NOTES(lourot): -# * We don't install charmcraft via pip anymore because it anyway spins up a -# container and scp the system's charmcraft snap inside it. So the charmcraft -# snap is necessary on the system anyway. -# * `tox -e build` successfully validated with charmcraft 1.2.1 - -cffi==1.14.6; python_version < '3.6' # cffi 1.15.0 drops support for py35. diff --git a/charm-helpers-hooks.yaml b/charm-helpers-hooks.yaml index 5d56aae..147a553 100644 --- a/charm-helpers-hooks.yaml +++ b/charm-helpers-hooks.yaml @@ -1,4 +1,4 @@ -repo: https://github.com/juju/charm-helpers +repo: https://github.com/juju/charm-helpers@stable/yoga destination: charmhelpers include: - core diff --git a/charmcraft.yaml b/charmcraft.yaml index eb74c30..680e401 100644 --- a/charmcraft.yaml +++ b/charmcraft.yaml @@ -21,16 +21,12 @@ parts: - metadata.yaml - README.md -bases: - - build-on: - - name: ubuntu - channel: "20.04" - architectures: - - amd64 - run-on: - - name: ubuntu - channel: "20.04" - architectures: [amd64, s390x, ppc64el, arm64] - - name: ubuntu - channel: "22.04" - architectures: [amd64, s390x, ppc64el, arm64] +platforms: + ubuntu@20.04:amd64: + ubuntu@20.04:arm64: + ubuntu@20.04:ppc64el: + ubuntu@20.04:s390x: + ubuntu@22.04:amd64: + ubuntu@22.04:arm64: + ubuntu@22.04:ppc64el: + ubuntu@22.04:s390x: diff --git a/charmhelpers/contrib/hahelpers/cluster.py b/charmhelpers/contrib/hahelpers/cluster.py index 146beba..7b30925 100644 --- a/charmhelpers/contrib/hahelpers/cluster.py +++ b/charmhelpers/contrib/hahelpers/cluster.py @@ -221,6 +221,13 @@ def https(): return True if config_get('ssl_cert') and config_get('ssl_key'): return True + # Local import to avoid ciruclar dependency. + import charmhelpers.contrib.openstack.cert_utils as cert_utils + if ( + cert_utils.get_certificate_request() and not + cert_utils.get_requests_for_local_unit("certificates") + ): + return False for r_id in relation_ids('certificates'): for unit in relation_list(r_id): ca = relation_get('ca', rid=r_id, unit=unit) @@ -324,7 +331,7 @@ def valid_hacluster_config(): ''' vip = config_get('vip') dns = config_get('dns-ha') - if not(bool(vip) ^ bool(dns)): + if not (bool(vip) ^ bool(dns)): msg = ('HA: Either vip or dns-ha must be set but not both in order to ' 'use high availability') status_set('blocked', msg) diff --git a/charmhelpers/contrib/network/ip.py b/charmhelpers/contrib/network/ip.py index de56584..f3b4864 100644 --- a/charmhelpers/contrib/network/ip.py +++ b/charmhelpers/contrib/network/ip.py @@ -16,6 +16,7 @@ import re import subprocess import socket +import ssl from functools import partial @@ -467,7 +468,7 @@ def ns_query(address): try: answers = dns.resolver.query(address, rtype) - except dns.resolver.NXDOMAIN: + except (dns.resolver.NXDOMAIN, dns.resolver.NoNameservers): return None if answers: @@ -527,19 +528,56 @@ def get_hostname(address, fqdn=True): return result.split('.')[0] -def port_has_listener(address, port): +class SSLPortCheckInfo(object): + + def __init__(self, key, cert, ca_cert, check_hostname=False): + self.key = key + self.cert = cert + self.ca_cert = ca_cert + # NOTE: by default we do not check hostname since the port check is + # typically performed using 0.0.0.0 which will not match the + # certificate. Hence the default for this is False. + self.check_hostname = check_hostname + + @property + def ssl_context(self): + context = ssl.create_default_context() + context.check_hostname = self.check_hostname + context.load_cert_chain(self.cert, self.key) + context.load_verify_locations(self.ca_cert) + return context + + +def port_has_listener(address, port, sslinfo=None): """ Returns True if the address:port is open and being listened to, - else False. + else False. By default uses netcat to check ports but if sslinfo is + provided will use an SSL connection instead. @param address: an IP address or hostname @param port: integer port + @param sslinfo: optional SSLPortCheckInfo object. + If provided, the check is performed using an ssl + connection. Note calls 'zc' via a subprocess shell """ - cmd = ['nc', '-z', address, str(port)] - result = subprocess.call(cmd) - return not(bool(result)) + if not sslinfo: + cmd = ['nc', '-z', address, str(port)] + result = subprocess.call(cmd) + return not (bool(result)) + + try: + with socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) as sock: + ssock = sslinfo.ssl_context.wrap_socket(sock, + server_hostname=address) + ssock.connect((address, port)) + # this bit is crucial to ensure tls close_notify is sent + ssock.unwrap() + + return True + except ConnectionRefusedError: + return False def assert_charm_supports_ipv6(): diff --git a/charmhelpers/contrib/openstack/cert_utils.py b/charmhelpers/contrib/openstack/cert_utils.py index 5c961c5..6620f59 100644 --- a/charmhelpers/contrib/openstack/cert_utils.py +++ b/charmhelpers/contrib/openstack/cert_utils.py @@ -409,13 +409,33 @@ def get_requests_for_local_unit(relation_name=None): relation_name = relation_name or 'certificates' bundles = [] for rid in relation_ids(relation_name): + sent = relation_get(rid=rid, unit=local_unit()) + legacy_keys = ['certificate_name', 'common_name'] + is_legacy_request = set(sent).intersection(legacy_keys) for unit in related_units(rid): data = relation_get(rid=rid, unit=unit) - if data.get(raw_certs_key): - bundles.append({ - 'ca': data['ca'], - 'chain': data.get('chain'), - 'certs': json.loads(data[raw_certs_key])}) + # Note: Bug#2028683 - data may not be available if the certificates + # relation hasn't been populated by the providing charm. If no 'ca' + # in the data then don't attempt the bundle at all. + if data.get('ca'): + if data.get(raw_certs_key): + bundles.append({ + 'ca': data['ca'], + 'chain': data.get('chain'), + 'certs': json.loads(data[raw_certs_key]) + }) + elif is_legacy_request: + bundles.append({ + 'ca': data['ca'], + 'chain': data.get('chain'), + 'certs': { + sent['common_name']: { + 'cert': data.get(local_name + '.server.cert'), + 'key': data.get(local_name + '.server.key') + } + } + }) + return bundles diff --git a/charmhelpers/contrib/openstack/context.py b/charmhelpers/contrib/openstack/context.py index 32c69ff..a0e2ae8 100644 --- a/charmhelpers/contrib/openstack/context.py +++ b/charmhelpers/contrib/openstack/context.py @@ -198,6 +198,21 @@ def get_related(self): return self.related +class KeystoneAuditMiddleware(OSContextGenerator): + def __init__(self, service: str) -> None: + self.service_name = service + + def __call__(self): + """Return context dictionary containing configuration status of + audit-middleware and the charm service name. + """ + ctxt = { + 'audit_middleware': config('audit-middleware') or False, + 'service_name': self.service_name + } + return ctxt + + class SharedDBContext(OSContextGenerator): interfaces = ['shared-db'] @@ -450,6 +465,7 @@ def __call__(self): int_host = format_ipv6_addr(int_host) or int_host svc_protocol = rdata.get('service_protocol') or 'http' auth_protocol = rdata.get('auth_protocol') or 'http' + admin_role = rdata.get('admin_role') or 'Admin' int_protocol = rdata.get('internal_protocol') or 'http' api_version = rdata.get('api_version') or '2.0' ctxt.update({'service_port': rdata.get('service_port'), @@ -461,6 +477,7 @@ def __call__(self): 'admin_tenant_name': rdata.get('service_tenant'), 'admin_user': rdata.get('service_username'), 'admin_password': rdata.get('service_password'), + 'admin_role': admin_role, 'service_protocol': svc_protocol, 'auth_protocol': auth_protocol, 'internal_protocol': int_protocol, @@ -475,7 +492,7 @@ def __call__(self): 'service_project_id': rdata.get('service_tenant_id'), 'service_domain_id': rdata.get('service_domain_id')}) - # we keep all veriables in ctxt for compatibility and + # we keep all variables in ctxt for compatibility and # add nested dictionary for keystone_authtoken generic # templating if keystonemiddleware_os_release: @@ -487,6 +504,7 @@ def __call__(self): # NOTE(jamespage) this is required for >= icehouse # so a missing value just indicates keystone needs # upgrading + ctxt['admin_user_id'] = rdata.get('service_user_id') ctxt['admin_tenant_id'] = rdata.get('service_tenant_id') ctxt['admin_domain_id'] = rdata.get('service_domain_id') return ctxt @@ -1663,6 +1681,9 @@ def __init__(self, name=None, script=None, admin_script=None, def __call__(self): total_processes = _calculate_workers() + enable_wsgi_socket_rotation = config('wsgi-socket-rotation') + if enable_wsgi_socket_rotation is None: + enable_wsgi_socket_rotation = True ctxt = { "service_name": self.service_name, "user": self.user, @@ -1676,6 +1697,7 @@ def __call__(self): "public_processes": int(math.ceil(self.public_process_weight * total_processes)), "threads": 1, + "wsgi_socket_rotation": enable_wsgi_socket_rotation, } return ctxt @@ -2560,14 +2582,18 @@ def _parse_cpu_list(cpulist): :rtype: List[int] """ cores = [] - ranges = cpulist.split(',') - for cpu_range in ranges: - if "-" in cpu_range: - cpu_min_max = cpu_range.split('-') - cores += range(int(cpu_min_max[0]), - int(cpu_min_max[1]) + 1) - else: - cores.append(int(cpu_range)) + if cpulist and re.match(r"^[0-9,\-^]*$", cpulist): + ranges = cpulist.split(',') + for cpu_range in ranges: + if "-" in cpu_range: + cpu_min_max = cpu_range.split('-') + cores += range(int(cpu_min_max[0]), + int(cpu_min_max[1]) + 1) + elif "^" in cpu_range: + cpu_rm = cpu_range.split('^') + cores.remove(int(cpu_rm[1])) + else: + cores.append(int(cpu_range)) return cores def _numa_node_cores(self): @@ -2586,36 +2612,32 @@ def _numa_node_cores(self): def cpu_mask(self): """Get hex formatted CPU mask - The mask is based on using the first config:dpdk-socket-cores cores of each NUMA node in the unit. :returns: hex formatted CPU mask :rtype: str """ - return self.cpu_masks()['dpdk_lcore_mask'] - - def cpu_masks(self): - """Get hex formatted CPU masks + num_cores = config('dpdk-socket-cores') + mask = 0 + for cores in self._numa_node_cores().values(): + for core in cores[:num_cores]: + mask = mask | 1 << core + return format(mask, '#04x') - The mask is based on using the first config:dpdk-socket-cores - cores of each NUMA node in the unit, followed by the - next config:pmd-socket-cores + @classmethod + def pmd_cpu_mask(cls): + """Get hex formatted pmd CPU mask - :returns: Dict of hex formatted CPU masks - :rtype: Dict[str, str] + The mask is based on config:pmd-cpu-set. + :returns: hex formatted CPU mask + :rtype: str """ - num_lcores = config('dpdk-socket-cores') - pmd_cores = config('pmd-socket-cores') - lcore_mask = 0 - pmd_mask = 0 - for cores in self._numa_node_cores().values(): - for core in cores[:num_lcores]: - lcore_mask = lcore_mask | 1 << core - for core in cores[num_lcores:][:pmd_cores]: - pmd_mask = pmd_mask | 1 << core - return { - 'pmd_cpu_mask': format(pmd_mask, '#04x'), - 'dpdk_lcore_mask': format(lcore_mask, '#04x')} + mask = 0 + cpu_list = cls._parse_cpu_list(config('pmd-cpu-set')) + if cpu_list: + for core in cpu_list: + mask = mask | 1 << core + return format(mask, '#x') def socket_memory(self): """Formatted list of socket memory configuration per socket. @@ -2694,6 +2716,7 @@ def __call__(self): ctxt['device_whitelist'] = self.device_whitelist() ctxt['socket_memory'] = self.socket_memory() ctxt['cpu_mask'] = self.cpu_mask() + ctxt['pmd_cpu_mask'] = self.pmd_cpu_mask() return ctxt diff --git a/charmhelpers/contrib/openstack/ssh_migrations.py b/charmhelpers/contrib/openstack/ssh_migrations.py index 96b9f71..0512e3a 100644 --- a/charmhelpers/contrib/openstack/ssh_migrations.py +++ b/charmhelpers/contrib/openstack/ssh_migrations.py @@ -310,7 +310,7 @@ def ssh_known_hosts_lines(application_name, user=None): for hosts_line in hosts: if hosts_line.rstrip(): known_hosts_list.append(hosts_line.rstrip()) - return(known_hosts_list) + return known_hosts_list def ssh_authorized_keys_lines(application_name, user=None): @@ -327,7 +327,7 @@ def ssh_authorized_keys_lines(application_name, user=None): for authkey_line in keys: if authkey_line.rstrip(): authorized_keys_list.append(authkey_line.rstrip()) - return(authorized_keys_list) + return authorized_keys_list def ssh_compute_remove(public_key, application_name, user=None): diff --git a/charmhelpers/contrib/openstack/templates/section-audit-middleware-notifications b/charmhelpers/contrib/openstack/templates/section-audit-middleware-notifications new file mode 100644 index 0000000..1f88014 --- /dev/null +++ b/charmhelpers/contrib/openstack/templates/section-audit-middleware-notifications @@ -0,0 +1,4 @@ +{% if audit_middleware -%} +[audit_middleware_notifications] +driver = log +{% endif -%} \ No newline at end of file diff --git a/charmhelpers/contrib/openstack/templates/section-filter-audit b/charmhelpers/contrib/openstack/templates/section-filter-audit new file mode 100644 index 0000000..11512ae --- /dev/null +++ b/charmhelpers/contrib/openstack/templates/section-filter-audit @@ -0,0 +1,6 @@ +{% if audit_middleware and service_name -%} +[filter:audit] +paste.filter_factory = keystonemiddleware.audit:filter_factory +audit_map_file = /etc/{{ service_name }}/api_audit_map.conf +service_name = {{ service_name }} +{% endif -%} \ No newline at end of file diff --git a/charmhelpers/contrib/openstack/templates/section-keystone-authtoken b/charmhelpers/contrib/openstack/templates/section-keystone-authtoken index c9b0152..dbad506 100644 --- a/charmhelpers/contrib/openstack/templates/section-keystone-authtoken +++ b/charmhelpers/contrib/openstack/templates/section-keystone-authtoken @@ -12,4 +12,6 @@ signing_dir = {{ signing_dir }} {% if service_type -%} service_type = {{ service_type }} {% endif -%} +service_token_roles = {{ admin_role }} +service_token_roles_required = True {% endif -%} diff --git a/charmhelpers/contrib/openstack/templates/section-keystone-authtoken-mitaka b/charmhelpers/contrib/openstack/templates/section-keystone-authtoken-mitaka index 14c25b4..139a051 100644 --- a/charmhelpers/contrib/openstack/templates/section-keystone-authtoken-mitaka +++ b/charmhelpers/contrib/openstack/templates/section-keystone-authtoken-mitaka @@ -22,4 +22,6 @@ signing_dir = {{ signing_dir }} {% if use_memcache == true %} memcached_servers = {{ memcache_url }} {% endif -%} +service_token_roles = {{ admin_role }} +service_token_roles_required = True {% endif -%} diff --git a/charmhelpers/contrib/openstack/templates/section-service-user b/charmhelpers/contrib/openstack/templates/section-service-user new file mode 100644 index 0000000..ff45408 --- /dev/null +++ b/charmhelpers/contrib/openstack/templates/section-service-user @@ -0,0 +1,11 @@ +{% if auth_host -%} +[service_user] +send_service_user_token = true +auth_type = password +auth_url = {{ auth_protocol }}://{{ auth_host }}:{{ auth_port }} +project_domain_name = service_domain +user_domain_name = service_domain +project_name = {{ admin_tenant_name }} +username = {{ admin_user }} +password = {{ admin_password }} +{% endif -%} diff --git a/charmhelpers/contrib/openstack/templates/wsgi-openstack-api.conf b/charmhelpers/contrib/openstack/templates/wsgi-openstack-api.conf index 6c4e37e..de5f603 100644 --- a/charmhelpers/contrib/openstack/templates/wsgi-openstack-api.conf +++ b/charmhelpers/contrib/openstack/templates/wsgi-openstack-api.conf @@ -12,6 +12,12 @@ Listen {{ admin_port }} Listen {{ public_port }} {% endif -%} +{% if wsgi_socket_rotation -%} +WSGISocketRotation On +{% else -%} +WSGISocketRotation Off +{% endif -%} + {% if port -%} WSGIDaemonProcess {{ service_name }} processes={{ processes }} threads={{ threads }} user={{ user }} group={{ group }} \ diff --git a/charmhelpers/contrib/openstack/templates/wsgi-openstack-metadata.conf b/charmhelpers/contrib/openstack/templates/wsgi-openstack-metadata.conf index 6c4e37e..de5f603 100644 --- a/charmhelpers/contrib/openstack/templates/wsgi-openstack-metadata.conf +++ b/charmhelpers/contrib/openstack/templates/wsgi-openstack-metadata.conf @@ -12,6 +12,12 @@ Listen {{ admin_port }} Listen {{ public_port }} {% endif -%} +{% if wsgi_socket_rotation -%} +WSGISocketRotation On +{% else -%} +WSGISocketRotation Off +{% endif -%} + {% if port -%} WSGIDaemonProcess {{ service_name }} processes={{ processes }} threads={{ threads }} user={{ user }} group={{ group }} \ diff --git a/charmhelpers/contrib/openstack/utils.py b/charmhelpers/contrib/openstack/utils.py index c8747c1..bea99cf 100644 --- a/charmhelpers/contrib/openstack/utils.py +++ b/charmhelpers/contrib/openstack/utils.py @@ -410,17 +410,6 @@ def get_os_version_codename(codename, version_map=OPENSTACK_CODENAMES): error_out(e) -def get_os_version_codename_swift(codename): - '''Determine OpenStack version number of swift from codename.''' - # for k, v in six.iteritems(SWIFT_CODENAMES): - for k, v in SWIFT_CODENAMES.items(): - if k == codename: - return v[-1] - e = 'Could not derive swift version for '\ - 'codename: %s' % codename - error_out(e) - - def get_swift_codename(version): '''Determine OpenStack codename that corresponds to swift version.''' codenames = [k for k, v in SWIFT_CODENAMES.items() if version in v] @@ -579,7 +568,6 @@ def _do_install(): return openstack_release().get('OPENSTACK_CODENAME') -@cached def openstack_release(): """Return /etc/os-release in a dict.""" d = {} @@ -841,14 +829,10 @@ def openstack_upgrade_available(package): if not cur_vers: # The package has not been installed yet do not attempt upgrade return False - if "swift" in package: - codename = get_os_codename_install_source(src) - avail_vers = get_os_version_codename_swift(codename) - else: - try: - avail_vers = get_os_version_install_source(src) - except Exception: - avail_vers = cur_vers + try: + avail_vers = get_os_version_install_source(src) + except Exception: + avail_vers = cur_vers apt.init() return apt.version_compare(avail_vers, cur_vers) >= 1 @@ -1216,12 +1200,14 @@ def _ows_check_services_running(services, ports): return ows_check_services_running(services, ports) -def ows_check_services_running(services, ports): +def ows_check_services_running(services, ports, ssl_check_info=None): """Check that the services that should be running are actually running and that any ports specified are being listened to. @param services: list of strings OR dictionary specifying services/ports @param ports: list of ports + @param ssl_check_info: SSLPortCheckInfo object. If provided, port checks + will be done using an SSL connection. @returns state, message: strings or None, None """ messages = [] @@ -1237,7 +1223,7 @@ def ows_check_services_running(services, ports): # also verify that the ports that should be open are open # NB, that ServiceManager objects only OPTIONALLY have ports map_not_open, ports_open = ( - _check_listening_on_services_ports(services)) + _check_listening_on_services_ports(services, ssl_check_info)) if not all(ports_open): # find which service has missing ports. They are in service # order which makes it a bit easier. @@ -1252,7 +1238,8 @@ def ows_check_services_running(services, ports): if ports is not None: # and we can also check ports which we don't know the service for - ports_open, ports_open_bools = _check_listening_on_ports_list(ports) + ports_open, ports_open_bools = \ + _check_listening_on_ports_list(ports, ssl_check_info) if not all(ports_open_bools): messages.append( "Ports which should be open, but are not: {}" @@ -1311,7 +1298,8 @@ def _check_running_services(services): return list(zip(services, services_running)), services_running -def _check_listening_on_services_ports(services, test=False): +def _check_listening_on_services_ports(services, test=False, + ssl_check_info=None): """Check that the unit is actually listening (has the port open) on the ports that the service specifies are open. If test is True then the function returns the services with ports that are open rather than @@ -1321,11 +1309,14 @@ def _check_listening_on_services_ports(services, test=False): @param services: OrderedDict(service: [port, ...], ...) @param test: default=False, if False, test for closed, otherwise open. + @param ssl_check_info: SSLPortCheckInfo object. If provided, port checks + will be done using an SSL connection. @returns OrderedDict(service: [port-not-open, ...]...), [boolean] """ - test = not(not(test)) # ensure test is True or False + test = not (not (test)) # ensure test is True or False all_ports = list(itertools.chain(*services.values())) - ports_states = [port_has_listener('0.0.0.0', p) for p in all_ports] + ports_states = [port_has_listener('0.0.0.0', p, ssl_check_info) + for p in all_ports] map_ports = OrderedDict() matched_ports = [p for p, opened in zip(all_ports, ports_states) if opened == test] # essentially opened xor test @@ -1336,16 +1327,19 @@ def _check_listening_on_services_ports(services, test=False): return map_ports, ports_states -def _check_listening_on_ports_list(ports): +def _check_listening_on_ports_list(ports, ssl_check_info=None): """Check that the ports list given are being listened to Returns a list of ports being listened to and a list of the booleans. + @param ssl_check_info: SSLPortCheckInfo object. If provided, port checks + will be done using an SSL connection. @param ports: LIST of port numbers. @returns [(port_num, boolean), ...], [boolean] """ - ports_open = [port_has_listener('0.0.0.0', p) for p in ports] + ports_open = [port_has_listener('0.0.0.0', p, ssl_check_info) + for p in ports] return zip(ports, ports_open), ports_open @@ -1579,7 +1573,7 @@ def is_unit_paused_set(): with unitdata.HookData()() as t: kv = t[0] # transform something truth-y into a Boolean. - return not(not(kv.get('unit-paused'))) + return not (not (kv.get('unit-paused'))) except Exception: return False @@ -2177,7 +2171,7 @@ def is_unit_upgrading_set(): with unitdata.HookData()() as t: kv = t[0] # transform something truth-y into a Boolean. - return not(not(kv.get('unit-upgrading'))) + return not (not (kv.get('unit-upgrading'))) except Exception: return False diff --git a/charmhelpers/contrib/storage/linux/ceph.py b/charmhelpers/contrib/storage/linux/ceph.py index 1b20b8f..2ff7a88 100644 --- a/charmhelpers/contrib/storage/linux/ceph.py +++ b/charmhelpers/contrib/storage/linux/ceph.py @@ -159,15 +159,19 @@ def get_osd_settings(relation_name): return _order_dict_by_key(osd_settings) -def send_application_name(relid=None): +def send_application_name(relid=None, app_name=None): """Send the application name down the relation. :param relid: Relation id to set application name in. :type relid: str + :param app_name: Application name to send in the relation. + :type app_name: str """ + if app_name is None: + app_name = application_name() relation_set( relation_id=relid, - relation_settings={'application-name': application_name()}) + relation_settings={'application-name': app_name}) def send_osd_settings(): diff --git a/charmhelpers/core/host.py b/charmhelpers/core/host.py index ad2cab4..2c9132f 100644 --- a/charmhelpers/core/host.py +++ b/charmhelpers/core/host.py @@ -256,8 +256,11 @@ def service_resume(service_name, init_dir="/etc/init", upstart_file = os.path.join(init_dir, "{}.conf".format(service_name)) sysv_file = os.path.join(initd_dir, service_name) if init_is_systemd(service_name=service_name): - service('unmask', service_name) - service('enable', service_name) + if service('is-enabled', service_name): + log('service {} already enabled'.format(service_name), level=DEBUG) + else: + service('unmask', service_name) + service('enable', service_name) elif os.path.exists(upstart_file): override_path = os.path.join( init_dir, '{}.override'.format(service_name)) @@ -952,7 +955,7 @@ def pwgen(length=None): random_generator = random.SystemRandom() random_chars = [ random_generator.choice(alphanumeric_chars) for _ in range(length)] - return(''.join(random_chars)) + return ''.join(random_chars) def is_phy_iface(interface): diff --git a/charmhelpers/fetch/snap.py b/charmhelpers/fetch/snap.py index 36d6bce..231e4fd 100644 --- a/charmhelpers/fetch/snap.py +++ b/charmhelpers/fetch/snap.py @@ -52,7 +52,7 @@ def _snap_exec(commands): :param commands: List commands :return: Integer exit code """ - assert type(commands) == list + assert type(commands) is list retry_count = 0 return_code = None diff --git a/charmhelpers/fetch/ubuntu.py b/charmhelpers/fetch/ubuntu.py index e6f8a0a..e972511 100644 --- a/charmhelpers/fetch/ubuntu.py +++ b/charmhelpers/fetch/ubuntu.py @@ -222,6 +222,10 @@ 'yoga/proposed': 'focal-proposed/yoga', 'focal-yoga/proposed': 'focal-proposed/yoga', 'focal-proposed/yoga': 'focal-proposed/yoga', + + # OVN + 'focal-ovn-22.03': 'focal-updates/ovn-22.03', + 'focal-ovn-22.03/proposed': 'focal-proposed/ovn-22.03', } @@ -677,6 +681,7 @@ def add_source(source, key=None, fail_invalid=False): (r"^cloud-archive:(.*)$", _add_apt_repository), (r"^((?:deb |http:|https:|ppa:).*)$", _add_apt_repository), (r"^cloud:(.*)-(.*)\/staging$", _add_cloud_staging), + (r"^cloud:(.*)-(ovn-.*)$", _add_cloud_distro_check), (r"^cloud:(.*)-(.*)$", _add_cloud_distro_check), (r"^cloud:(.*)$", _add_cloud_pocket), (r"^snap:.*-(.*)-(.*)$", _add_cloud_distro_check), @@ -740,6 +745,11 @@ def _add_apt_repository(spec): ) +def __write_sources_list_d_actual_pocket(file, actual_pocket): + with open('/etc/apt/sources.list.d/{}'.format(file), 'w') as apt: + apt.write(CLOUD_ARCHIVE.format(actual_pocket)) + + def _add_cloud_pocket(pocket): """Add a cloud pocket as /etc/apt/sources.d/cloud-archive.list @@ -759,8 +769,9 @@ def _add_cloud_pocket(pocket): 'Unsupported cloud: source option %s' % pocket) actual_pocket = CLOUD_ARCHIVE_POCKETS[pocket] - with open('/etc/apt/sources.list.d/cloud-archive.list', 'w') as apt: - apt.write(CLOUD_ARCHIVE.format(actual_pocket)) + __write_sources_list_d_actual_pocket( + 'cloud-archive{}.list'.format('' if 'ovn' not in pocket else '-ovn'), + actual_pocket) def _add_cloud_staging(cloud_archive_release, openstack_release): diff --git a/config.yaml b/config.yaml index 8d5c1bb..7f2555a 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: yoga @@ -552,8 +557,20 @@ options: type: boolean default: False description: | - Enable conversion of all images to raw format during image upload, - only supported on stein or newer. + Enable conversion of all images to raw format during image import, + only supported on stein or newer. This only works on imported images + (for example using 'openstack image create --import') Does not work + on regular image uploads (like 'openstack image create') + custom-import-properties: + type: string + default: + description: | + Set custom image properties when images are imported using the + interoperable image import process. Properties and their value are + delimited by commas, and values with colons and spaces are possible. + The properties specified here will be added to the glance-api + configuration file without being validated. + Example: 'prop1:val1,prop2:val-2,prop3:val:with:colons' s3-store-host: type: string default: diff --git a/hooks/glance_contexts.py b/hooks/glance_contexts.py index 5a857e2..5d2f9ae 100644 --- a/hooks/glance_contexts.py +++ b/hooks/glance_contexts.py @@ -33,6 +33,7 @@ ApacheSSLContext as SSLContext, BindHostContext, VolumeAPIContext, + IdentityServiceContext, ) from charmhelpers.contrib.hahelpers.cluster import ( @@ -106,10 +107,44 @@ class GlanceImageImportContext(OSContextGenerator): def __call__(self): ctxt = {} + ctxt['image_import_plugins'] = [] if config('image-conversion'): - ctxt['image_conversion'] = config('image-conversion') + ctxt['image_import_plugins'].append('image_conversion') + + if config('custom-import-properties'): + try: + self.validate_custom_import_properties() + ctxt['image_import_plugins'].append('inject_image_metadata') + ctxt['custom_import_properties'] = ( + config('custom-import-properties') + ) + except (ValueError): + juju_log('Unable to validate custom-import-properties ({}), ' + 'see config.yaml for information about valid ' + 'formatting' + .format(config('custom-import-properties')), + level=ERROR) + raise return ctxt + def validate_custom_import_properties(self): + """Check the format of 'custom-import-properties' config parameter, + it should be a string of comma delimited key:value pairs + """ + props = config('custom-import-properties') + if not isinstance(props, str): + raise ValueError('not a string') + # Empty string is valid + if props == '': + return + # Check key value pairs + props_list = props.split(',') + for prop in props_list: + if ":" not in prop: + raise ValueError('value not found for property: {}' + .format(prop)) + return + class CephGlanceContext(OSContextGenerator): interfaces = ['ceph-glance'] @@ -332,6 +367,7 @@ def __call__(self): # backend cinder_volume_type should be left blank so that glance # creates volume in cinder without specifying any volume type. if cinder_volume_types: + keystone_ctx = IdentityServiceContext()() for volume_type in volume_types: ctxt['enabled_backend_configs'][volume_type] = { 'cinder_volume_type': volume_type, @@ -339,6 +375,17 @@ def __call__(self): 'cinder_state_transition_timeout': config( 'cinder-state-transition-timeout'), } + if keystone_ctx: + ctxt['enabled_backend_configs'][volume_type].update({ + 'cinder_store_user_name': keystone_ctx.get( + 'admin_user'), + 'cinder_store_password': keystone_ctx.get( + 'admin_password'), + 'cinder_store_project_name': keystone_ctx.get( + 'admin_tenant_name'), + 'cinder_store_auth_address': keystone_ctx.get( + 'keystone_authtoken').get('auth_url'), + }) else: # default cinder volume type cinder ctxt['enabled_backend_configs']['cinder'] = { @@ -352,7 +399,7 @@ def __call__(self): vol_api_ctxt = VolumeAPIContext('glance-common')() volume_catalog_info = vol_api_ctxt['volume_catalog_info'] for volume_type in volume_types: - if 'volume_type' not in ctxt['enabled_backend_configs']: + if volume_type not in ctxt['enabled_backend_configs']: ctxt['enabled_backend_configs'][volume_type] = {} ctxt['enabled_backend_configs'][volume_type].update( {'cinder_catalog_info': volume_catalog_info}) diff --git a/hooks/glance_relations.py b/hooks/glance_relations.py index ff67ed9..d181a67 100755 --- a/hooks/glance_relations.py +++ b/hooks/glance_relations.py @@ -482,6 +482,7 @@ def cluster_changed(): @harden() def upgrade_charm(): resolve_CONFIGS() + apt_update() apt_install(filter_installed_packages(determine_packages()), fatal=True) packages_removed = remove_old_packages() reinstall_paste_ini(force_reinstall=packages_removed) diff --git a/hooks/glance_utils.py b/hooks/glance_utils.py index ef663e5..9ed2812 100644 --- a/hooks/glance_utils.py +++ b/hooks/glance_utils.py @@ -121,13 +121,13 @@ CHARM = "glance" GLANCE_CONF_DIR = "/etc/glance" +GLANCE_AUDIT_MAP = "%s/api_audit_map.conf" % GLANCE_CONF_DIR GLANCE_REGISTRY_CONF = "%s/glance-registry.conf" % GLANCE_CONF_DIR GLANCE_API_CONF = "%s/glance-api.conf" % GLANCE_CONF_DIR GLANCE_SWIFT_CONF = "%s/glance-swift.conf" % GLANCE_CONF_DIR GLANCE_REGISTRY_PASTE = os.path.join(GLANCE_CONF_DIR, 'glance-registry-paste.ini') -GLANCE_API_PASTE = os.path.join(GLANCE_CONF_DIR, - 'glance-api-paste.ini') +GLANCE_API_PASTE = os.path.join(GLANCE_CONF_DIR, 'api-paste.ini') GLANCE_POLICY_FILE = os.path.join(GLANCE_CONF_DIR, "policy.json") # NOTE(ajkavanagh): from Ussuri, glance switched to policy-in-code; this is the # policy.yaml file (as there is not packaged policy.json or .yaml) that is used @@ -204,6 +204,7 @@ def ceph_config_file(): config_file=GLANCE_API_CONF), context.MemcacheContext(), glance_contexts.GlanceImageImportContext(), + context.KeystoneAuditMiddleware(service=CHARM), glance_contexts.ExternalS3Context()], 'services': ['glance-api'] }), @@ -218,6 +219,14 @@ def ceph_config_file(): 'hook_contexts': [], 'services': ['glance-api', 'glance-registry'] }), + (GLANCE_AUDIT_MAP, { + 'hook_contexts': [context.KeystoneAuditMiddleware(service=CHARM)], + 'services': ['glance-api'] + }), + (GLANCE_API_PASTE, { + 'hook_contexts': [context.KeystoneAuditMiddleware(service=CHARM)], + 'services': ['glance-api'] + }), (ceph_config_file(), { 'hook_contexts': [context.CephContext()], 'services': ['glance-api', 'glance-registry'] @@ -259,6 +268,9 @@ def register_configs(): GLANCE_API_CONF, HAPROXY_CONF] + if cmp_release >= 'yoga': + confs.extend([GLANCE_API_PASTE, GLANCE_AUDIT_MAP]) + if relation_ids('ceph'): mkdir(os.path.dirname(ceph_config_file())) mkdir(os.path.dirname(CEPH_CONF)) @@ -403,6 +415,8 @@ def restart_map(): cmp_release = CompareOpenStackReleases(os_release('glance-common')) for f, ctxt in CONFIG_FILES.items(): + if f == GLANCE_AUDIT_MAP and cmp_release < 'yoga': + continue svcs = [] for svc in ctxt['services']: if cmp_release >= 'stein' and svc == 'glance-registry': @@ -636,7 +650,7 @@ def reinstall_paste_ini(force_reinstall=False): for paste_file in [GLANCE_REGISTRY_PASTE, GLANCE_API_PASTE]: if os.path.exists(paste_file): - os.remove(paste_file) + os.replace(paste_file, f'{paste_file}.charm.upgrade') # glance-registry is deprecated at queens but still # installed. if cmp_release < 'rocky': @@ -647,6 +661,15 @@ def reinstall_paste_ini(force_reinstall=False): apt_install(packages=pkg_list, options=REINSTALL_OPTIONS, fatal=True) + # LP: #2042792 Restore original paste files + for paste_file in [GLANCE_REGISTRY_PASTE, + GLANCE_API_PASTE]: + if not os.path.exists(paste_file): + if os.path.exists(f'{paste_file}.charm.upgrade'): + os.replace(f'{paste_file}.charm.upgrade', paste_file) + else: + if os.path.exists(f'{paste_file}.charm.upgrade'): + os.remove(f'{paste_file}.charm.upgrade') db.set(PASTE_INI_MARKER, True) db.flush() diff --git a/merged-requirements-py310.txt b/merged-requirements-py310.txt new file mode 100644 index 0000000..f4d5ec8 --- /dev/null +++ b/merged-requirements-py310.txt @@ -0,0 +1,719 @@ +# +# This file is autogenerated by pip-compile with Python 3.10 +# by the following command: +# +# pip-compile --output-file=merged-requirements-py310.txt --unsafe-package=juju --unsafe-package=kubernetes requirements.in test-requirements.in +# +aiohappyeyeballs==2.6.2 + # via aiohttp +aiohttp==3.14.0 + # via + # python-libmaas + # zaza +aiosignal==1.4.0 + # via aiohttp +aodhclient==1.3.0 + # via zaza-openstack +argcomplete==3.6.3 + # via python-libmaas +async-generator==1.10 + # via + # zaza + # zaza-openstack +async-timeout==5.0.1 + # via aiohttp +attrs==26.1.0 + # via + # aiohttp + # jsonschema +babel==2.18.0 + # via + # python-cinderclient + # python-heatclient + # python-manilaclient + # python-neutronclient + # python-novaclient + # python-octaviaclient + # python-openstackclient +backports-datetime-fromisoformat==2.0.3 + # via juju +backports-strenum==1.3.1 + # via juju +bcrypt==5.0.0 + # via paramiko +blessings==1.7 + # via charm-tools +boto3==1.43.22 + # via zaza-openstack +botocore==1.43.22 + # via + # boto3 + # s3transfer +certifi==2026.5.20 + # via + # kubernetes + # requests +cffi==2.0.0 + # via + # cryptography + # pynacl +charm-tools @ git+https://github.com/juju/charm-tools.git + # via -r test-requirements.in +charset-normalizer==3.4.7 + # via requests +cheetah3==3.2.6.post1 + # via charm-tools +cliff==2.18.0 + # via + # -r test-requirements.in + # aodhclient + # gnocchiclient + # osc-lib + # python-barbicanclient + # python-designateclient + # python-heatclient + # python-ironicclient + # python-neutronclient + # python-octaviaclient + # python-openstackclient + # stestr +cmd2==0.8.9 + # via cliff +colander==1.8.3 + # via charm-tools +colorclass==2.2.2 + # via python-libmaas +coverage==7.14.1 + # via -r test-requirements.in +croniter==6.2.2 + # via -r test-requirements.in +cryptography==3.3.2 + # via + # -r requirements.in + # openstacksdk + # paramiko + # pyopenssl + # secretstorage + # zaza + # zaza-openstack +debtcollector==3.1.0 + # via + # gnocchiclient + # oslo-concurrency + # oslo-config + # oslo-log + # oslo-utils + # python-designateclient + # python-keystoneclient + # python-manilaclient + # python-neutronclient +decorator==5.3.1 + # via + # dogpile-cache + # openstacksdk +dict2colander==0.2 + # via charm-tools +distlib==0.4.1 + # via virtualenv +dnspython==2.8.0 ; python_version >= "3.6" + # via + # -r requirements.in + # pymongo + # zaza-openstack +dogpile-cache==1.5.0 + # via + # openstacksdk + # python-ironicclient +fasteners==0.20 + # via oslo-concurrency +filelock==3.29.1 + # via virtualenv +fixtures==4.3.2 + # via stestr +flake8==7.1.1 + # via -r test-requirements.in +frozenlist==1.8.0 + # via + # aiohttp + # aiosignal +futurist==1.10.0 + # via + # gnocchiclient + # zaza-openstack +gnocchiclient==7.2.0 + # via zaza-openstack +google-auth==2.47.0 + # via kubernetes +hvac==0.6.4 + # via + # juju + # zaza + # zaza-openstack +idna==3.18 + # via + # requests + # yarl +importlib-metadata==9.0.0 + # via keyring +invoke==3.0.3 + # via paramiko +iso8601==2.1.0 + # via + # colander + # gnocchiclient + # keystoneauth1 + # openstacksdk + # oslo-utils + # python-ceilometerclient + # python-heatclient + # python-neutronclient + # python-novaclient + # python-subunit +jaraco-classes==3.4.0 + # via keyring +jeepney==0.9.0 + # via + # keyring + # secretstorage +jinja2==3.1.6 + # via + # -r requirements.in + # zaza + # zaza-openstack +jmespath==1.1.0 + # via + # boto3 + # botocore + # openstacksdk +jsonpatch==1.33 + # via + # openstacksdk + # warlock +jsonpointer==3.1.1 + # via jsonpatch +jsonschema==3.2.0 + # via + # charm-tools + # python-designateclient + # python-ironicclient + # warlock +juju-wait==2.8.4 + # via + # zaza + # zaza-openstack +jujubundlelib==0.5.7 + # via charm-tools +keyring==23.13.1 + # via charm-tools +keystoneauth1==5.14.0 + # via + # aodhclient + # gnocchiclient + # openstacksdk + # osc-lib + # python-barbicanclient + # python-ceilometerclient + # python-cinderclient + # python-designateclient + # python-glanceclient + # python-heatclient + # python-ironicclient + # python-keystoneclient + # python-neutronclient + # python-novaclient + # python-octaviaclient + # python-openstackclient +lxml==6.1.1 + # via zaza-openstack +macaroonbakery==1.3.4 + # via + # juju + # python-libmaas + # zaza +markupsafe==3.0.3 + # via jinja2 +mccabe==0.7.0 + # via flake8 +more-itertools==11.1.0 + # via jaraco-classes +msgpack==1.1.2 + # via oslo-serialization +multidict==6.7.1 + # via + # aiohttp + # yarl +mypy-extensions==1.1.0 + # via typing-inspect +netaddr==0.7.20 + # via + # -r requirements.in + # oslo-config + # oslo-utils + # osprofiler + # python-neutronclient +netifaces==0.11.0 + # via + # -r requirements.in + # openstacksdk + # oslo-utils + # python-octaviaclient +oauthlib==3.3.1 + # via + # kubernetes + # python-libmaas + # requests-oauthlib +openstacksdk==4.1.0 + # via + # os-client-config + # osc-lib + # python-ironicclient + # python-openstackclient +os-client-config==2.3.0 + # via python-neutronclient +os-service-types==1.8.2 + # via + # keystoneauth1 + # openstacksdk +osc-lib==2.1.0 + # via + # aodhclient + # python-designateclient + # python-heatclient + # python-ironicclient + # python-neutronclient + # python-octaviaclient + # python-openstackclient + # zaza-openstack +oslo-concurrency==7.5.0 + # via osprofiler +oslo-config==6.11.3 + # via + # oslo-concurrency + # oslo-log + # osprofiler + # python-keystoneclient + # python-manilaclient + # zaza + # zaza-openstack +oslo-context==6.4.0 + # via oslo-log +oslo-i18n==6.8.0 + # via + # aodhclient + # osc-lib + # oslo-concurrency + # oslo-config + # oslo-log + # oslo-utils + # python-barbicanclient + # python-ceilometerclient + # python-cinderclient + # python-glanceclient + # python-heatclient + # python-keystoneclient + # python-neutronclient + # python-novaclient + # python-openstackclient +oslo-log==8.2.0 + # via + # python-manilaclient + # python-neutronclient +oslo-serialization==5.10.0 + # via + # aodhclient + # oslo-log + # osprofiler + # python-barbicanclient + # python-ceilometerclient + # python-designateclient + # python-heatclient + # python-keystoneclient + # python-manilaclient + # python-neutronclient + # python-novaclient + # python-octaviaclient +oslo-utils==7.1.0 + # via + # aodhclient + # osc-lib + # oslo-concurrency + # oslo-log + # oslo-serialization + # osprofiler + # python-barbicanclient + # python-ceilometerclient + # python-cinderclient + # python-designateclient + # python-glanceclient + # python-heatclient + # python-ironicclient + # python-keystoneclient + # python-manilaclient + # python-neutronclient + # python-novaclient + # python-octaviaclient + # python-openstackclient +osprofiler==4.4.0 + # via aodhclient +otherstuf==1.1.0 + # via charm-tools +packaging==26.2 + # via + # juju + # oslo-utils +paramiko==5.0.0 + # via juju +parse==1.22.1 + # via stuf +path==16.16.0 + # via charm-tools +pathspec==0.10.3 + # via charm-tools +pbr==5.6.0 + # via + # -r requirements.in + # aodhclient + # cliff + # futurist + # keystoneauth1 + # openstacksdk + # os-client-config + # os-service-types + # osc-lib + # oslo-concurrency + # oslo-context + # oslo-i18n + # oslo-log + # python-barbicanclient + # python-ceilometerclient + # python-cinderclient + # python-designateclient + # python-glanceclient + # python-heatclient + # python-ironicclient + # python-keystoneclient + # python-manilaclient + # python-neutronclient + # python-novaclient + # python-octaviaclient + # python-openstackclient +pika==1.4.1 + # via zaza-openstack +pip==26.1.2 + # via charm-tools +platformdirs==4.10.0 + # via + # openstacksdk + # python-ironicclient + # virtualenv +prettytable==0.7.2 + # via + # cliff + # futurist + # osprofiler + # python-ceilometerclient + # python-cinderclient + # python-glanceclient + # python-heatclient + # python-manilaclient + # python-novaclient +propcache==0.5.2 + # via + # aiohttp + # yarl +protobuf==7.35.0 + # via macaroonbakery +psutil==1.2.1 + # via -r requirements.in +pyasn1==0.6.3 + # via + # juju + # pyasn1-modules + # rsa +pyasn1-modules==0.4.2 + # via google-auth +pycodestyle==2.12.1 + # via flake8 +pycparser==3.0 + # via cffi +pyflakes==3.2.0 + # via flake8 +pymacaroons==0.13.0 + # via macaroonbakery +pymongo==4.17.0 + # via python-libmaas +pynacl==1.6.2 + # via + # macaroonbakery + # paramiko + # pymacaroons +pyopenssl==21.0.0 + # via + # python-glanceclient + # zaza-openstack +pyparsing==2.4.7 + # via + # -r test-requirements.in + # aodhclient + # cliff + # cmd2 + # oslo-utils + # zaza-openstack +pyperclip==1.11.0 + # via cmd2 +pyrfc3339==1.1 + # via macaroonbakery +pyrsistent==0.20.0 + # via jsonschema +python-barbicanclient==4.10.0 + # via zaza-openstack +python-ceilometerclient==2.9.0 + # via zaza-openstack +python-cinderclient==5.0.2 + # via + # python-openstackclient + # zaza-openstack +python-dateutil==2.9.0.post0 + # via + # botocore + # croniter + # gnocchiclient + # kubernetes + # oslo-log +python-designateclient==2.12.0 + # via zaza-openstack +python-glanceclient==2.17.1 + # via + # python-openstackclient + # zaza-openstack +python-heatclient==1.18.1 + # via zaza-openstack +python-ironicclient==5.8.1 + # via zaza-openstack +python-keystoneclient==3.21.0 + # via + # python-manilaclient + # python-neutronclient + # python-openstackclient + # zaza-openstack +python-libmaas==0.6.8 + # via zaza +python-manilaclient==1.29.0 + # via zaza-openstack +python-neutronclient==6.14.1 + # via + # python-octaviaclient + # zaza-openstack +python-novaclient==15.1.1 + # via + # python-openstackclient + # zaza-openstack +python-octaviaclient==1.10.1 + # via zaza-openstack +python-openstackclient==4.0.2 + # via python-octaviaclient +python-subunit==1.4.6 + # via stestr +python-swiftclient==3.8.1 + # via + # python-heatclient + # zaza-openstack +pytz==2026.2 + # via + # pyrfc3339 + # python-libmaas +pyudev==0.24.4 + # via -r test-requirements.in +pyyaml==6.0.3 + # via + # charm-tools + # cliff + # juju + # juju-wait + # jujubundlelib + # kubernetes + # openstacksdk + # oslo-config + # oslo-utils + # python-heatclient + # python-ironicclient + # python-libmaas + # python-subunit + # stestr + # zaza + # zaza-openstack +requests==2.34.2 + # via + # -r test-requirements.in + # charm-tools + # hvac + # keystoneauth1 + # kubernetes + # macaroonbakery + # oslo-config + # osprofiler + # python-barbicanclient + # python-ceilometerclient + # python-cinderclient + # python-designateclient + # python-glanceclient + # python-heatclient + # python-ironicclient + # python-keystoneclient + # python-manilaclient + # python-neutronclient + # python-octaviaclient + # python-swiftclient + # requests-oauthlib +requests-oauthlib==2.0.0 + # via kubernetes +requestsexceptions==1.4.0 + # via openstacksdk +requirements-parser==0.5.0 + # via charm-tools +rfc3986==2.0.0 + # via oslo-config +rsa==4.9.1 + # via google-auth +ruamel-yaml==0.17.40 + # via charm-tools +ruamel-yaml-clib==0.2.15 + # via ruamel-yaml +s3transfer==0.18.0 + # via boto3 +secretstorage==3.3.3 + # via + # charm-tools + # keyring +setuptools==81.0.0 + # via + # -r test-requirements.in + # charm-tools + # colander + # jsonschema +simplejson==4.1.1 + # via + # -r requirements.in + # osc-lib + # python-cinderclient + # python-manilaclient + # python-neutronclient + # python-novaclient +six==1.17.0 + # via + # -r requirements.in + # aodhclient + # blessings + # cliff + # cmd2 + # cryptography + # futurist + # jsonschema + # kubernetes + # macaroonbakery + # oslo-config + # pymacaroons + # pyopenssl + # python-barbicanclient + # python-ceilometerclient + # python-cinderclient + # python-dateutil + # python-designateclient + # python-glanceclient + # python-heatclient + # python-keystoneclient + # python-manilaclient + # python-neutronclient + # python-novaclient + # python-octaviaclient + # python-openstackclient + # python-swiftclient + # warlock +stestr==4.2.1 + # via -r test-requirements.in +stevedore==5.8.0 + # via + # cliff + # dogpile-cache + # keystoneauth1 + # osc-lib + # oslo-config + # python-ceilometerclient + # python-designateclient + # python-ironicclient + # python-keystoneclient +stuf==0.9.16 + # via otherstuf +tenacity==9.1.4 + # via + # zaza + # zaza-openstack +terminaltables==3.1.10 + # via python-libmaas +testtools==2.9.1 + # via + # python-subunit + # stestr +tomlkit==0.15.0 + # via stestr +toposort==1.10 + # via juju +translationstring==1.4 + # via colander +types-setuptools==82.0.0.20260518 + # via requirements-parser +typing-extensions==4.15.0 + # via + # aiohttp + # aiosignal + # dogpile-cache + # juju + # keystoneauth1 + # multidict + # os-service-types + # oslo-context + # typing-inspect + # virtualenv +typing-inspect==0.9.0 + # via juju +tzdata==2026.2 + # via oslo-utils +ujson==5.12.1 + # via gnocchiclient +urllib3==2.7.0 + # via + # botocore + # kubernetes + # requests +virtualenv==20.39.1 + # via charm-tools +voluptuous==0.16.0 + # via stestr +warlock==1.3.3 + # via python-glanceclient +wcwidth==0.7.0 + # via cmd2 +webob==1.8.10 + # via osprofiler +websocket-client==1.9.0 + # via kubernetes +websockets==16.0 + # via juju +wrapt==2.2.1 + # via + # debtcollector + # python-glanceclient +yarl==1.24.2 + # via aiohttp +zaza==2024.1.1 + # via + # -r test-requirements.in + # zaza-openstack +zaza-openstack @ git+https://github.com/openstack-charmers/zaza-openstack-tests.git@stable/yoga + # via -r test-requirements.in +zipp==4.1.0 + # via importlib-metadata + +# The following packages are considered to be unsafe in a requirements file: +# juju +# kubernetes diff --git a/merged-requirements-py38.txt b/merged-requirements-py38.txt new file mode 100644 index 0000000..6e77a10 --- /dev/null +++ b/merged-requirements-py38.txt @@ -0,0 +1,717 @@ +# +# This file is autogenerated by pip-compile with Python 3.8 +# by the following command: +# +# pip-compile --output-file=merged-requirements-py38.txt --unsafe-package=juju --unsafe-package=kubernetes requirements.in test-requirements.in +# +aiohappyeyeballs==2.4.4 + # via aiohttp +aiohttp==3.10.11 + # via + # python-libmaas + # zaza +aiosignal==1.3.1 + # via aiohttp +aodhclient==1.3.0 + # via zaza-openstack +argcomplete==3.6.3 + # via python-libmaas +async-generator==1.10 + # via + # zaza + # zaza-openstack +async-timeout==5.0.1 + # via aiohttp +attrs==25.3.0 + # via + # aiohttp + # jsonschema +babel==2.18.0 + # via + # python-cinderclient + # python-heatclient + # python-manilaclient + # python-neutronclient + # python-novaclient + # python-octaviaclient + # python-openstackclient +backports-datetime-fromisoformat==2.0.3 + # via juju +backports-strenum==1.3.1 + # via juju +bcrypt==5.0.0 + # via paramiko +blessings==1.7 + # via charm-tools +boto3==1.37.38 + # via zaza-openstack +botocore==1.37.38 + # via + # boto3 + # s3transfer +certifi==2026.5.20 + # via + # kubernetes + # requests +cffi==1.17.1 + # via + # cryptography + # pynacl +charm-tools @ git+https://github.com/juju/charm-tools.git + # via -r test-requirements.in +charset-normalizer==3.4.7 + # via requests +cheetah3==3.2.6.post1 + # via charm-tools +cliff==2.18.0 + # via + # -r test-requirements.in + # aodhclient + # gnocchiclient + # osc-lib + # python-barbicanclient + # python-designateclient + # python-heatclient + # python-ironicclient + # python-neutronclient + # python-octaviaclient + # python-openstackclient + # stestr +cmd2==0.8.9 + # via cliff +colander==1.8.3 + # via charm-tools +colorclass==2.2.2 + # via python-libmaas +coverage==7.6.1 + # via -r test-requirements.in +croniter==6.0.0 + # via -r test-requirements.in +cryptography==3.3.2 + # via + # -r requirements.in + # openstacksdk + # paramiko + # pyopenssl + # secretstorage + # zaza + # zaza-openstack +debtcollector==3.0.0 + # via + # gnocchiclient + # oslo-config + # oslo-context + # oslo-utils + # python-designateclient + # python-keystoneclient + # python-manilaclient + # python-neutronclient +decorator==5.3.1 + # via + # dogpile-cache + # openstacksdk +dict2colander==0.2 + # via charm-tools +distlib==0.4.1 + # via virtualenv +dnspython==2.6.1 ; python_version >= "3.6" + # via + # -r requirements.in + # pymongo + # zaza-openstack +dogpile-cache==1.3.4 + # via + # openstacksdk + # python-ironicclient +fasteners==0.20 + # via oslo-concurrency +filelock==3.16.1 + # via virtualenv +fixtures==4.2.5 + # via stestr +flake8==7.1.1 + # via -r test-requirements.in +frozenlist==1.5.0 + # via + # aiohttp + # aiosignal +futurist==1.10.0 + # via + # gnocchiclient + # zaza-openstack +gnocchiclient==7.2.0 + # via zaza-openstack +google-auth==2.47.0 + # via kubernetes +hvac==0.6.4 + # via + # juju + # zaza + # zaza-openstack +idna==3.15 + # via + # requests + # yarl +importlib-metadata==8.5.0 + # via keyring +importlib-resources==6.4.5 + # via + # jsonschema + # keyring +iso8601==2.1.0 + # via + # colander + # gnocchiclient + # keystoneauth1 + # openstacksdk + # oslo-utils + # python-ceilometerclient + # python-heatclient + # python-neutronclient + # python-novaclient + # python-subunit +jaraco-classes==3.4.0 + # via keyring +jeepney==0.9.0 + # via + # keyring + # secretstorage +jinja2==3.1.6 + # via + # -r requirements.in + # zaza + # zaza-openstack +jmespath==1.0.1 + # via + # boto3 + # botocore + # openstacksdk +jsonpatch==1.33 + # via + # openstacksdk + # warlock +jsonpointer==3.0.0 + # via jsonpatch +jsonschema==3.2.0 + # via + # charm-tools + # python-designateclient + # python-ironicclient + # warlock +juju-wait==2.8.4 + # via + # zaza + # zaza-openstack +jujubundlelib==0.5.7 + # via charm-tools +keyring==23.13.1 + # via charm-tools +keystoneauth1==5.8.1 + # via + # aodhclient + # gnocchiclient + # openstacksdk + # osc-lib + # python-barbicanclient + # python-ceilometerclient + # python-cinderclient + # python-designateclient + # python-glanceclient + # python-heatclient + # python-ironicclient + # python-keystoneclient + # python-neutronclient + # python-novaclient + # python-octaviaclient + # python-openstackclient +lxml==6.1.1 + # via zaza-openstack +macaroonbakery==1.3.4 + # via + # juju + # python-libmaas + # zaza +markupsafe==2.1.5 + # via jinja2 +mccabe==0.7.0 + # via flake8 +more-itertools==10.5.0 + # via jaraco-classes +msgpack==1.1.1 + # via oslo-serialization +multidict==6.1.0 + # via + # aiohttp + # yarl +mypy-extensions==1.1.0 + # via typing-inspect +netaddr==0.7.20 + # via + # -r requirements.in + # oslo-config + # oslo-utils + # osprofiler + # python-neutronclient +netifaces==0.11.0 + # via + # -r requirements.in + # openstacksdk + # oslo-utils + # python-octaviaclient +oauthlib==3.3.1 + # via + # kubernetes + # python-libmaas + # requests-oauthlib +openstacksdk==4.0.1 + # via + # os-client-config + # osc-lib + # python-ironicclient + # python-openstackclient +os-client-config==2.1.0 + # via python-neutronclient +os-service-types==1.7.0 + # via + # keystoneauth1 + # openstacksdk +osc-lib==2.1.0 + # via + # aodhclient + # python-designateclient + # python-heatclient + # python-ironicclient + # python-neutronclient + # python-octaviaclient + # python-openstackclient + # zaza-openstack +oslo-concurrency==6.1.0 + # via osprofiler +oslo-config==6.11.3 + # via + # oslo-concurrency + # oslo-log + # osprofiler + # python-keystoneclient + # python-manilaclient + # zaza + # zaza-openstack +oslo-context==5.6.0 + # via oslo-log +oslo-i18n==6.4.0 + # via + # aodhclient + # osc-lib + # oslo-concurrency + # oslo-config + # oslo-log + # oslo-utils + # python-barbicanclient + # python-ceilometerclient + # python-cinderclient + # python-glanceclient + # python-heatclient + # python-keystoneclient + # python-neutronclient + # python-novaclient + # python-openstackclient +oslo-log==6.1.2 + # via + # python-manilaclient + # python-neutronclient +oslo-serialization==5.5.0 + # via + # aodhclient + # oslo-log + # osprofiler + # python-barbicanclient + # python-ceilometerclient + # python-designateclient + # python-heatclient + # python-keystoneclient + # python-manilaclient + # python-neutronclient + # python-novaclient + # python-octaviaclient +oslo-utils==7.1.0 + # via + # aodhclient + # osc-lib + # oslo-concurrency + # oslo-log + # oslo-serialization + # osprofiler + # python-barbicanclient + # python-ceilometerclient + # python-cinderclient + # python-designateclient + # python-glanceclient + # python-heatclient + # python-ironicclient + # python-keystoneclient + # python-manilaclient + # python-neutronclient + # python-novaclient + # python-octaviaclient + # python-openstackclient +osprofiler==4.2.0 + # via aodhclient +otherstuf==1.1.0 + # via charm-tools +packaging==26.2 + # via + # juju + # oslo-utils +paramiko==3.5.1 + # via juju +parse==1.22.1 + # via stuf +path==16.16.0 + # via charm-tools +pathspec==0.10.3 + # via charm-tools +pbr==5.6.0 + # via + # -r requirements.in + # aodhclient + # cliff + # futurist + # keystoneauth1 + # openstacksdk + # os-service-types + # osc-lib + # oslo-concurrency + # oslo-context + # oslo-i18n + # oslo-log + # oslo-serialization + # python-barbicanclient + # python-ceilometerclient + # python-cinderclient + # python-designateclient + # python-glanceclient + # python-heatclient + # python-ironicclient + # python-keystoneclient + # python-manilaclient + # python-neutronclient + # python-novaclient + # python-octaviaclient + # python-openstackclient + # stevedore +pika==1.4.1 + # via zaza-openstack +pip==25.0.1 + # via charm-tools +platformdirs==4.3.6 + # via + # openstacksdk + # python-ironicclient + # virtualenv +prettytable==0.7.2 + # via + # cliff + # futurist + # osprofiler + # python-ceilometerclient + # python-cinderclient + # python-glanceclient + # python-heatclient + # python-manilaclient + # python-novaclient +propcache==0.2.0 + # via yarl +protobuf==5.29.6 + # via macaroonbakery +psutil==1.2.1 + # via -r requirements.in +pyasn1==0.6.3 + # via + # juju + # pyasn1-modules + # rsa +pyasn1-modules==0.4.2 + # via google-auth +pycodestyle==2.12.1 + # via flake8 +pycparser==2.23 + # via cffi +pyflakes==3.2.0 + # via flake8 +pymacaroons==0.13.0 + # via macaroonbakery +pymongo==4.10.1 + # via python-libmaas +pynacl==1.6.2 + # via + # macaroonbakery + # paramiko + # pymacaroons +pyopenssl==21.0.0 + # via + # python-glanceclient + # zaza-openstack +pyparsing==2.4.7 + # via + # -r test-requirements.in + # aodhclient + # cliff + # cmd2 + # oslo-utils + # zaza-openstack +pyperclip==1.11.0 + # via cmd2 +pyrfc3339==1.1 + # via macaroonbakery +pyrsistent==0.20.0 + # via jsonschema +python-barbicanclient==4.10.0 + # via zaza-openstack +python-ceilometerclient==2.9.0 + # via zaza-openstack +python-cinderclient==5.0.2 + # via + # python-openstackclient + # zaza-openstack +python-dateutil==2.9.0.post0 + # via + # botocore + # croniter + # gnocchiclient + # kubernetes + # oslo-log +python-designateclient==2.12.0 + # via zaza-openstack +python-glanceclient==2.17.1 + # via + # python-openstackclient + # zaza-openstack +python-heatclient==1.18.1 + # via zaza-openstack +python-ironicclient==5.8.1 + # via zaza-openstack +python-keystoneclient==3.21.0 + # via + # python-manilaclient + # python-neutronclient + # python-openstackclient + # zaza-openstack +python-libmaas==0.6.8 + # via zaza +python-manilaclient==1.29.0 + # via zaza-openstack +python-neutronclient==6.14.1 + # via + # python-octaviaclient + # zaza-openstack +python-novaclient==15.1.1 + # via + # python-openstackclient + # zaza-openstack +python-octaviaclient==1.10.1 + # via zaza-openstack +python-openstackclient==4.0.2 + # via python-octaviaclient +python-subunit==1.4.4 + # via stestr +python-swiftclient==3.8.1 + # via + # python-heatclient + # zaza-openstack +pytz==2026.2 + # via + # babel + # croniter + # oslo-serialization + # oslo-utils + # pyrfc3339 + # python-libmaas +pyudev==0.24.3 + # via -r test-requirements.in +pyyaml==6.0.3 + # via + # charm-tools + # cliff + # juju + # juju-wait + # jujubundlelib + # kubernetes + # openstacksdk + # oslo-config + # oslo-utils + # python-heatclient + # python-ironicclient + # python-libmaas + # stestr + # zaza + # zaza-openstack +requests==2.32.4 + # via + # -r test-requirements.in + # charm-tools + # hvac + # keystoneauth1 + # kubernetes + # macaroonbakery + # oslo-config + # osprofiler + # python-barbicanclient + # python-ceilometerclient + # python-cinderclient + # python-designateclient + # python-glanceclient + # python-heatclient + # python-ironicclient + # python-keystoneclient + # python-manilaclient + # python-neutronclient + # python-octaviaclient + # python-swiftclient + # requests-oauthlib +requests-oauthlib==2.0.0 + # via kubernetes +requestsexceptions==1.4.0 + # via openstacksdk +requirements-parser==0.5.0 + # via charm-tools +rfc3986==2.0.0 + # via oslo-config +rsa==4.9.1 + # via google-auth +ruamel-yaml==0.17.40 + # via charm-tools +ruamel-yaml-clib==0.2.8 + # via ruamel-yaml +s3transfer==0.11.5 + # via boto3 +secretstorage==3.3.3 + # via + # charm-tools + # keyring +setuptools==75.3.4 + # via + # -r test-requirements.in + # charm-tools + # colander + # jsonschema +simplejson==4.1.1 + # via + # -r requirements.in + # osc-lib + # python-cinderclient + # python-manilaclient + # python-neutronclient + # python-novaclient +six==1.17.0 + # via + # -r requirements.in + # aodhclient + # blessings + # cliff + # cmd2 + # cryptography + # futurist + # jsonschema + # kubernetes + # macaroonbakery + # oslo-config + # pymacaroons + # pyopenssl + # python-barbicanclient + # python-ceilometerclient + # python-cinderclient + # python-dateutil + # python-designateclient + # python-glanceclient + # python-heatclient + # python-keystoneclient + # python-manilaclient + # python-neutronclient + # python-novaclient + # python-octaviaclient + # python-openstackclient + # python-swiftclient + # warlock +stestr==4.2.1 + # via -r test-requirements.in +stevedore==5.3.0 + # via + # cliff + # dogpile-cache + # keystoneauth1 + # osc-lib + # oslo-config + # python-ceilometerclient + # python-designateclient + # python-ironicclient + # python-keystoneclient +stuf==0.9.16 + # via otherstuf +tenacity==9.0.0 + # via + # zaza + # zaza-openstack +terminaltables==3.1.10 + # via python-libmaas +testtools==2.7.2 + # via + # python-subunit + # stestr +tomlkit==0.13.3 + # via stestr +toposort==1.10 + # via juju +translationstring==1.4 + # via colander +types-setuptools==75.8.0.20250110 + # via requirements-parser +typing-extensions==4.13.2 + # via + # dogpile-cache + # juju + # multidict + # typing-inspect + # virtualenv +typing-inspect==0.9.0 + # via juju +ujson==5.10.0 + # via gnocchiclient +urllib3==1.26.20 + # via + # botocore + # kubernetes + # requests +virtualenv==20.39.1 + # via charm-tools +voluptuous==0.14.2 + # via stestr +warlock==1.3.3 + # via python-glanceclient +wcwidth==0.7.0 + # via cmd2 +webob==1.8.10 + # via osprofiler +websocket-client==1.8.0 + # via kubernetes +websockets==13.1 + # via juju +wrapt==2.0.1 + # via + # debtcollector + # python-glanceclient +yarl==1.15.2 + # via aiohttp +zaza==2024.1.1 + # via + # -r test-requirements.in + # zaza-openstack +zaza-openstack @ git+https://github.com/openstack-charmers/zaza-openstack-tests.git@stable/yoga + # via -r test-requirements.in +zipp==3.20.2 + # via + # importlib-metadata + # importlib-resources + +# The following packages are considered to be unsafe in a requirements file: +# juju +# kubernetes diff --git a/metadata.yaml b/metadata.yaml index 4ca2b41..0fc6ee2 100644 --- a/metadata.yaml +++ b/metadata.yaml @@ -6,6 +6,7 @@ description: | and an image delivery service. These services are used in conjunction by Nova to deliver images from object stores, such as OpenStack's Swift service, to Nova's compute nodes. +docs: https://discourse.charmhub.io/t/glance-docs-index/10553 tags: - openstack - storage diff --git a/pip.sh b/pip.sh deleted file mode 100755 index 9a7e6b0..0000000 --- a/pip.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env bash -# -# This file is managed centrally by release-tools and should not be modified -# within individual charm repos. See the 'global' dir contents for available -# choices of tox.ini for OpenStack Charms: -# https://github.com/openstack-charmers/release-tools -# -# setuptools 58.0 dropped the support for use_2to3=true which is needed to -# install blessings (an indirect dependency of charm-tools). -# -# More details on the beahvior of tox and virtualenv creation can be found at -# https://github.com/tox-dev/tox/issues/448 -# -# This script is wrapper to force the use of the pinned versions early in the -# process when the virtualenv was created and upgraded before installing the -# depedencies declared in the target. -pip install 'pip<20.3' 'setuptools<50.0.0' -pip "$@" diff --git a/rename.sh b/rename.sh deleted file mode 100755 index d0c35c9..0000000 --- a/rename.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash -charm=$(grep "charm_build_name" osci.yaml | awk '{print $2}') -echo "renaming ${charm}_*.charm to ${charm}.charm" -echo -n "pwd: " -pwd -ls -al -echo "Removing bad downloaded charm maybe?" -if [[ -e "${charm}.charm" ]]; -then - rm "${charm}.charm" -fi -echo "Renaming charm here." -mv ${charm}_*.charm ${charm}.charm diff --git a/requirements.txt b/requirements.in similarity index 100% rename from requirements.txt rename to requirements.in diff --git a/templates/parts/section-image-import b/templates/parts/section-image-import index afd1cbd..a9e3075 100644 --- a/templates/parts/section-image-import +++ b/templates/parts/section-image-import @@ -1,7 +1,13 @@ -{% if image_conversion -%} +{% if image_import_plugins|length > 0 -%} [image_import_opts] -image_import_plugins = ['image_conversion'] - +image_import_plugins = {{ image_import_plugins }} +{% if 'image_conversion' in image_import_plugins %} [image_conversion] output_format = raw +{% endif %} +{% if 'inject_image_metadata' in image_import_plugins -%} +[inject_metadata_properties] +ignore_user_roles = "" +inject = {{ custom_import_properties }} {% endif -%} +{% endif %} diff --git a/templates/yoga/api-paste.ini b/templates/yoga/api-paste.ini new file mode 100644 index 0000000..dc8687b --- /dev/null +++ b/templates/yoga/api-paste.ini @@ -0,0 +1,86 @@ +# Use this pipeline for no auth or image caching - DEFAULT +[pipeline:glance-api] +pipeline = cors healthcheck http_proxy_to_wsgi versionnegotiation osprofiler unauthenticated-context rootapp + +# Use this pipeline for image caching and no auth +[pipeline:glance-api-caching] +pipeline = cors healthcheck http_proxy_to_wsgi versionnegotiation osprofiler unauthenticated-context cache rootapp + +# Use this pipeline for caching w/ management interface but no auth +[pipeline:glance-api-cachemanagement] +pipeline = cors healthcheck http_proxy_to_wsgi versionnegotiation osprofiler unauthenticated-context cache cachemanage rootapp + +# Use this pipeline for keystone auth +[pipeline:glance-api-keystone] +{% if audit_middleware and service_name -%} +pipeline = cors healthcheck http_proxy_to_wsgi versionnegotiation osprofiler authtoken audit context rootapp +{% else %} +pipeline = cors healthcheck http_proxy_to_wsgi versionnegotiation osprofiler authtoken context rootapp +{% endif %} + +# Use this pipeline for keystone auth with image caching +[pipeline:glance-api-keystone+caching] +{% if audit_middleware and service_name -%} +pipeline = cors healthcheck http_proxy_to_wsgi versionnegotiation osprofiler authtoken audit context cache rootapp +{% else %} +pipeline = cors healthcheck http_proxy_to_wsgi versionnegotiation osprofiler authtoken context cache rootapp +{% endif %} + +# Use this pipeline for keystone auth with caching and cache management +[pipeline:glance-api-keystone+cachemanagement] +{% if audit_middleware and service_name -%} +pipeline = cors healthcheck http_proxy_to_wsgi versionnegotiation osprofiler authtoken audit context cache cachemanage rootapp +{% else %} +pipeline = cors healthcheck http_proxy_to_wsgi versionnegotiation osprofiler authtoken context cache cachemanage rootapp +{% endif %} + +[composite:rootapp] +paste.composite_factory = glance.api:root_app_factory +/: apiversions +/v2: apiv2app + +[app:apiversions] +paste.app_factory = glance.api.versions:create_resource + +[app:apiv2app] +paste.app_factory = glance.api.v2.router:API.factory + +[filter:healthcheck] +paste.filter_factory = oslo_middleware:Healthcheck.factory +backends = disable_by_file +disable_by_file_path = /etc/glance/healthcheck_disable + +[filter:versionnegotiation] +paste.filter_factory = glance.api.middleware.version_negotiation:VersionNegotiationFilter.factory + +[filter:cache] +paste.filter_factory = glance.api.middleware.cache:CacheFilter.factory + +[filter:cachemanage] +paste.filter_factory = glance.api.middleware.cache_manage:CacheManageFilter.factory + +[filter:context] +paste.filter_factory = glance.api.middleware.context:ContextMiddleware.factory + +[filter:unauthenticated-context] +paste.filter_factory = glance.api.middleware.context:UnauthenticatedContextMiddleware.factory + +[filter:authtoken] +paste.filter_factory = keystonemiddleware.auth_token:filter_factory +delay_auth_decision = true + +[filter:gzip] +paste.filter_factory = glance.api.middleware.gzip:GzipMiddleware.factory + +[filter:osprofiler] +paste.filter_factory = osprofiler.web:WsgiMiddleware.factory + +[filter:cors] +paste.filter_factory = oslo_middleware.cors:filter_factory +oslo_config_project = glance +oslo_config_program = glance-api + +[filter:http_proxy_to_wsgi] +paste.filter_factory = oslo_middleware:HTTPProxyToWSGI.factory + +{% include "section-filter-audit" %} \ No newline at end of file diff --git a/templates/yoga/api_audit_map.conf b/templates/yoga/api_audit_map.conf new file mode 100644 index 0000000..fc9e461 --- /dev/null +++ b/templates/yoga/api_audit_map.conf @@ -0,0 +1,16 @@ +[DEFAULT] +# default target endpoint type +# should match the endpoint type defined in service catalog +target_endpoint_type = None + +# possible end path of api requests +[path_keywords] +detail = None +file = None +images = image +members = member +tags = tag + +# map endpoint type defined in service catalog to CADF typeURI +[service_endpoints] +image = service/storage/image \ No newline at end of file diff --git a/templates/yoga/glance-api.conf b/templates/yoga/glance-api.conf new file mode 100644 index 0000000..6aed2b4 --- /dev/null +++ b/templates/yoga/glance-api.conf @@ -0,0 +1,89 @@ +[DEFAULT] +verbose = {{ verbose }} +use_syslog = {{ use_syslog }} +debug = {{ debug }} +workers = {{ workers }} +bind_host = {{ bind_host }} + +{% if ext -%} +bind_port = {{ ext }} +{% elif bind_port -%} +bind_port = {{ bind_port }} +{% else -%} +bind_port = 9292 +{% endif -%} + +{% if transport_url %} +transport_url = {{ transport_url }} +{% endif %} + +log_file = /var/log/glance/api.log +backlog = 4096 + +{% if expose_image_locations -%} +show_multiple_locations = {{ expose_image_locations }} +show_image_direct_url = {{ expose_image_locations }} +{% endif -%} + +{% if api_config_flags -%} +{% for key, value in api_config_flags.items() -%} +{{ key }} = {{ value }} +{% endfor -%} +{% endif -%} + +delayed_delete = False +scrub_time = 43200 +scrubber_datadir = /var/lib/glance/scrubber +image_cache_dir = /var/lib/glance/image-cache/ +db_enforce_mysql_charset = False + +{% if image_size_cap -%} +image_size_cap = {{ image_size_cap }} +{% endif -%} + +{% if enabled_backends %} +enabled_backends = {{ enabled_backends }} +{% endif %} + +[glance_store] +{% if default_store_backend %} +default_backend = {{ default_store_backend }} +{% endif %} + +[image_format] +disk_formats = {{ disk_formats }} +{% if container_formats -%} +container_formats = {{ container_formats }} +{% endif -%} + +{% include "section-keystone-authtoken-v3only" %} + +{% if auth_host -%} +[paste_deploy] +flavor = keystone +config_file = /etc/glance/api-paste.ini +{% endif %} + +[barbican] +auth_endpoint = {{ service_protocol }}://{{ service_host }}:{{ service_port }}/v3 + +{% include "parts/section-database" %} + +{% include "section-oslo-messaging-rabbit" %} + +{% include "section-oslo-notifications" %} + +{% include "section-oslo-middleware" %} + +{% include "parts/section-storage" %} + +{% for name, cfg in enabled_backend_configs.items() %} +[{{name}}] +{% for key, val in cfg.items() -%} +{{ key }} = {{ val }} +{% endfor -%} +{% endfor%} + +{% include "parts/section-image-import" %} + +{% include "section-audit-middleware-notifications" %} \ No newline at end of file diff --git a/test-requirements-py310.txt b/test-requirements-py310.txt new file mode 100644 index 0000000..f54b088 --- /dev/null +++ b/test-requirements-py310.txt @@ -0,0 +1,708 @@ +# +# This file is autogenerated by pip-compile with Python 3.10 +# by the following command: +# +# pip-compile --output-file=test-requirements-py310.txt --unsafe-package=juju --unsafe-package=kubernetes test-requirements.in +# +aiohappyeyeballs==2.6.2 + # via aiohttp +aiohttp==3.14.0 + # via + # python-libmaas + # zaza +aiosignal==1.4.0 + # via aiohttp +aodhclient==1.3.0 + # via zaza-openstack +argcomplete==3.6.3 + # via python-libmaas +async-generator==1.10 + # via + # zaza + # zaza-openstack +async-timeout==5.0.1 + # via aiohttp +attrs==26.1.0 + # via + # aiohttp + # jsonschema +babel==2.18.0 + # via + # python-cinderclient + # python-heatclient + # python-manilaclient + # python-neutronclient + # python-novaclient + # python-octaviaclient + # python-openstackclient +backports-datetime-fromisoformat==2.0.3 + # via juju +backports-strenum==1.3.1 + # via juju +bcrypt==5.0.0 + # via paramiko +blessings==1.7 + # via charm-tools +boto3==1.43.22 + # via zaza-openstack +botocore==1.43.22 + # via + # boto3 + # s3transfer +certifi==2026.5.20 + # via + # kubernetes + # requests +cffi==2.0.0 + # via + # cryptography + # pynacl +charm-tools @ git+https://github.com/juju/charm-tools.git + # via -r test-requirements.in +charset-normalizer==3.4.7 + # via requests +cheetah3==3.2.6.post1 + # via charm-tools +cliff==2.18.0 + # via + # -r test-requirements.in + # aodhclient + # gnocchiclient + # osc-lib + # python-barbicanclient + # python-designateclient + # python-heatclient + # python-ironicclient + # python-neutronclient + # python-octaviaclient + # python-openstackclient + # stestr +cmd2==0.8.9 + # via cliff +colander==1.8.3 + # via charm-tools +colorclass==2.2.2 + # via python-libmaas +coverage==7.14.1 + # via -r test-requirements.in +croniter==6.2.2 + # via -r test-requirements.in +cryptography==3.3.2 + # via + # openstacksdk + # paramiko + # pyopenssl + # secretstorage + # zaza + # zaza-openstack +debtcollector==3.1.0 + # via + # gnocchiclient + # oslo-concurrency + # oslo-config + # oslo-log + # python-designateclient + # python-keystoneclient + # python-manilaclient + # python-neutronclient +decorator==5.3.1 + # via + # dogpile-cache + # openstacksdk +dict2colander==0.2 + # via charm-tools +distlib==0.4.1 + # via virtualenv +dnspython==2.8.0 + # via + # pymongo + # zaza-openstack +dogpile-cache==1.5.0 + # via + # openstacksdk + # python-ironicclient +fasteners==0.20 + # via oslo-concurrency +filelock==3.29.1 + # via virtualenv +fixtures==4.3.2 + # via stestr +flake8==7.1.1 + # via -r test-requirements.in +frozenlist==1.8.0 + # via + # aiohttp + # aiosignal +futurist==1.10.0 + # via + # gnocchiclient + # zaza-openstack +gnocchiclient==7.2.0 + # via zaza-openstack +google-auth==2.47.0 + # via kubernetes +hvac==0.6.4 + # via + # juju + # zaza + # zaza-openstack +idna==3.18 + # via + # requests + # yarl +importlib-metadata==9.0.0 + # via keyring +invoke==3.0.3 + # via paramiko +iso8601==2.1.0 + # via + # colander + # gnocchiclient + # keystoneauth1 + # openstacksdk + # oslo-utils + # python-ceilometerclient + # python-heatclient + # python-neutronclient + # python-novaclient + # python-subunit +jaraco-classes==3.4.0 + # via keyring +jeepney==0.9.0 + # via + # keyring + # secretstorage +jinja2==3.1.6 + # via + # zaza + # zaza-openstack +jmespath==1.1.0 + # via + # boto3 + # botocore + # openstacksdk +jsonpatch==1.33 + # via + # openstacksdk + # warlock +jsonpointer==3.1.1 + # via jsonpatch +jsonschema==3.2.0 + # via + # charm-tools + # python-designateclient + # python-ironicclient + # warlock +juju-wait==2.8.4 + # via + # zaza + # zaza-openstack +jujubundlelib==0.5.7 + # via charm-tools +keyring==23.13.1 + # via charm-tools +keystoneauth1==5.14.0 + # via + # aodhclient + # gnocchiclient + # openstacksdk + # osc-lib + # python-barbicanclient + # python-ceilometerclient + # python-cinderclient + # python-designateclient + # python-glanceclient + # python-heatclient + # python-ironicclient + # python-keystoneclient + # python-neutronclient + # python-novaclient + # python-octaviaclient + # python-openstackclient +lxml==6.1.1 + # via zaza-openstack +macaroonbakery==1.3.4 + # via + # juju + # python-libmaas + # zaza +markupsafe==3.0.3 + # via jinja2 +mccabe==0.7.0 + # via flake8 +more-itertools==11.1.0 + # via jaraco-classes +msgpack==1.1.2 + # via oslo-serialization +multidict==6.7.1 + # via + # aiohttp + # yarl +mypy-extensions==1.1.0 + # via typing-inspect +netaddr==1.3.0 + # via + # oslo-config + # oslo-utils + # osprofiler + # python-neutronclient +netifaces==0.11.0 + # via python-octaviaclient +oauthlib==3.3.1 + # via + # kubernetes + # python-libmaas + # requests-oauthlib +openstacksdk==4.13.0 + # via + # os-client-config + # osc-lib + # python-ironicclient + # python-openstackclient +os-client-config==2.3.0 + # via python-neutronclient +os-service-types==1.8.2 + # via + # keystoneauth1 + # openstacksdk +osc-lib==2.1.0 + # via + # aodhclient + # python-designateclient + # python-heatclient + # python-ironicclient + # python-neutronclient + # python-octaviaclient + # python-openstackclient + # zaza-openstack +oslo-concurrency==7.5.0 + # via osprofiler +oslo-config==6.11.3 + # via + # oslo-concurrency + # oslo-log + # osprofiler + # python-keystoneclient + # python-manilaclient + # zaza + # zaza-openstack +oslo-context==6.4.0 + # via oslo-log +oslo-i18n==6.8.0 + # via + # aodhclient + # osc-lib + # oslo-concurrency + # oslo-config + # oslo-log + # oslo-utils + # python-barbicanclient + # python-ceilometerclient + # python-cinderclient + # python-glanceclient + # python-heatclient + # python-keystoneclient + # python-neutronclient + # python-novaclient + # python-openstackclient +oslo-log==8.2.0 + # via + # python-manilaclient + # python-neutronclient +oslo-serialization==5.10.0 + # via + # aodhclient + # oslo-log + # osprofiler + # python-barbicanclient + # python-ceilometerclient + # python-designateclient + # python-heatclient + # python-keystoneclient + # python-manilaclient + # python-neutronclient + # python-novaclient + # python-octaviaclient +oslo-utils==10.1.0 + # via + # aodhclient + # osc-lib + # oslo-concurrency + # oslo-log + # oslo-serialization + # osprofiler + # python-barbicanclient + # python-ceilometerclient + # python-cinderclient + # python-designateclient + # python-glanceclient + # python-heatclient + # python-ironicclient + # python-keystoneclient + # python-manilaclient + # python-neutronclient + # python-novaclient + # python-octaviaclient + # python-openstackclient +osprofiler==4.4.0 + # via aodhclient +otherstuf==1.1.0 + # via charm-tools +packaging==26.2 + # via + # juju + # oslo-utils +paramiko==5.0.0 + # via juju +parse==1.22.1 + # via stuf +path==16.16.0 + # via charm-tools +pathspec==0.10.3 + # via charm-tools +pbr==7.0.3 + # via + # aodhclient + # cliff + # futurist + # keystoneauth1 + # openstacksdk + # os-client-config + # os-service-types + # osc-lib + # oslo-concurrency + # oslo-context + # oslo-i18n + # oslo-log + # oslo-utils + # python-barbicanclient + # python-ceilometerclient + # python-cinderclient + # python-designateclient + # python-glanceclient + # python-heatclient + # python-ironicclient + # python-keystoneclient + # python-manilaclient + # python-neutronclient + # python-novaclient + # python-octaviaclient + # python-openstackclient +pika==1.4.1 + # via zaza-openstack +pip==26.1.2 + # via charm-tools +platformdirs==4.10.0 + # via + # openstacksdk + # python-ironicclient + # virtualenv +prettytable==0.7.2 + # via + # cliff + # futurist + # osprofiler + # python-ceilometerclient + # python-cinderclient + # python-glanceclient + # python-heatclient + # python-manilaclient + # python-novaclient +propcache==0.5.2 + # via + # aiohttp + # yarl +protobuf==7.35.0 + # via macaroonbakery +psutil==7.2.2 + # via + # openstacksdk + # oslo-utils +pyasn1==0.6.3 + # via + # juju + # pyasn1-modules + # rsa +pyasn1-modules==0.4.2 + # via google-auth +pycodestyle==2.12.1 + # via flake8 +pycparser==3.0 + # via cffi +pyflakes==3.2.0 + # via flake8 +pymacaroons==0.13.0 + # via macaroonbakery +pymongo==4.17.0 + # via python-libmaas +pynacl==1.6.2 + # via + # macaroonbakery + # paramiko + # pymacaroons +pyopenssl==21.0.0 + # via + # python-glanceclient + # zaza-openstack +pyparsing==2.4.7 + # via + # -r test-requirements.in + # aodhclient + # cliff + # cmd2 + # oslo-utils + # zaza-openstack +pyperclip==1.11.0 + # via cmd2 +pyrfc3339==1.1 + # via macaroonbakery +pyrsistent==0.20.0 + # via jsonschema +python-barbicanclient==4.10.0 + # via zaza-openstack +python-ceilometerclient==2.9.0 + # via zaza-openstack +python-cinderclient==5.0.2 + # via + # python-openstackclient + # zaza-openstack +python-dateutil==2.9.0.post0 + # via + # botocore + # croniter + # gnocchiclient + # kubernetes + # oslo-log +python-designateclient==2.12.0 + # via zaza-openstack +python-glanceclient==2.17.1 + # via + # python-openstackclient + # zaza-openstack +python-heatclient==1.18.1 + # via zaza-openstack +python-ironicclient==6.1.0 + # via zaza-openstack +python-keystoneclient==3.21.0 + # via + # python-manilaclient + # python-neutronclient + # python-openstackclient + # zaza-openstack +python-libmaas==0.6.8 + # via zaza +python-manilaclient==1.29.0 + # via zaza-openstack +python-neutronclient==6.14.1 + # via + # python-octaviaclient + # zaza-openstack +python-novaclient==15.1.1 + # via + # python-openstackclient + # zaza-openstack +python-octaviaclient==1.10.1 + # via zaza-openstack +python-openstackclient==4.0.2 + # via python-octaviaclient +python-subunit==1.4.6 + # via stestr +python-swiftclient==3.8.1 + # via + # python-heatclient + # zaza-openstack +pytz==2026.2 + # via + # pyrfc3339 + # python-libmaas +pyudev==0.24.4 + # via -r test-requirements.in +pyyaml==6.0.3 + # via + # charm-tools + # cliff + # juju + # juju-wait + # jujubundlelib + # kubernetes + # openstacksdk + # oslo-config + # oslo-utils + # python-heatclient + # python-ironicclient + # python-libmaas + # python-subunit + # stestr + # zaza + # zaza-openstack +requests==2.34.2 + # via + # -r test-requirements.in + # charm-tools + # hvac + # keystoneauth1 + # kubernetes + # macaroonbakery + # oslo-config + # osprofiler + # python-barbicanclient + # python-ceilometerclient + # python-cinderclient + # python-designateclient + # python-glanceclient + # python-heatclient + # python-ironicclient + # python-keystoneclient + # python-manilaclient + # python-neutronclient + # python-octaviaclient + # python-swiftclient + # requests-oauthlib +requests-oauthlib==2.0.0 + # via kubernetes +requirements-parser==0.5.0 + # via charm-tools +rfc3986==2.0.0 + # via oslo-config +rsa==4.9.1 + # via google-auth +ruamel-yaml==0.17.40 + # via charm-tools +ruamel-yaml-clib==0.2.15 + # via ruamel-yaml +s3transfer==0.18.0 + # via boto3 +secretstorage==3.3.3 + # via + # charm-tools + # keyring +setuptools==81.0.0 + # via + # -r test-requirements.in + # charm-tools + # colander + # jsonschema + # pbr +simplejson==4.1.1 + # via + # osc-lib + # python-cinderclient + # python-manilaclient + # python-neutronclient + # python-novaclient +six==1.17.0 + # via + # aodhclient + # blessings + # cliff + # cmd2 + # cryptography + # futurist + # jsonschema + # kubernetes + # macaroonbakery + # oslo-config + # pymacaroons + # pyopenssl + # python-barbicanclient + # python-ceilometerclient + # python-cinderclient + # python-dateutil + # python-designateclient + # python-glanceclient + # python-heatclient + # python-keystoneclient + # python-manilaclient + # python-neutronclient + # python-novaclient + # python-octaviaclient + # python-openstackclient + # python-swiftclient + # warlock +stestr==4.2.1 + # via -r test-requirements.in +stevedore==5.8.0 + # via + # cliff + # dogpile-cache + # keystoneauth1 + # osc-lib + # oslo-config + # python-ceilometerclient + # python-designateclient + # python-ironicclient + # python-keystoneclient +stuf==0.9.16 + # via otherstuf +tenacity==9.1.4 + # via + # zaza + # zaza-openstack +terminaltables==3.1.10 + # via python-libmaas +testtools==2.9.1 + # via + # python-subunit + # stestr +tomlkit==0.15.0 + # via stestr +toposort==1.10 + # via juju +translationstring==1.4 + # via colander +types-setuptools==82.0.0.20260518 + # via requirements-parser +typing-extensions==4.15.0 + # via + # aiohttp + # aiosignal + # dogpile-cache + # juju + # keystoneauth1 + # multidict + # openstacksdk + # os-service-types + # oslo-context + # typing-inspect + # virtualenv +typing-inspect==0.9.0 + # via juju +ujson==5.12.1 + # via gnocchiclient +urllib3==2.7.0 + # via + # botocore + # kubernetes + # requests +virtualenv==20.39.1 + # via charm-tools +voluptuous==0.16.0 + # via stestr +warlock==1.3.3 + # via python-glanceclient +wcwidth==0.7.0 + # via cmd2 +webob==1.8.10 + # via osprofiler +websocket-client==1.9.0 + # via kubernetes +websockets==16.0 + # via juju +wrapt==2.2.1 + # via + # debtcollector + # python-glanceclient +yarl==1.24.2 + # via aiohttp +zaza==2024.1.1 + # via + # -r test-requirements.in + # zaza-openstack +zaza-openstack @ git+https://github.com/openstack-charmers/zaza-openstack-tests.git@stable/yoga + # via -r test-requirements.in +zipp==4.1.0 + # via importlib-metadata + +# The following packages are considered to be unsafe in a requirements file: +# juju +# kubernetes diff --git a/test-requirements.txt b/test-requirements.in similarity index 82% rename from test-requirements.txt rename to test-requirements.in index 0aabe17..27b1a48 100644 --- a/test-requirements.txt +++ b/test-requirements.in @@ -9,7 +9,7 @@ # pyparsing<3.0.0 # aodhclient is pinned in zaza and needs pyparsing < 3.0.0, but cffi also needs it, so pin here. cffi==1.14.6; python_version < '3.6' # cffi 1.15.0 drops support for py35. -setuptools<50.0.0 # https://github.com/pypa/setuptools/commit/04e3df22df840c6bb244e9b27bc56750c44b7c85 +setuptools<82.0.0 requests>=2.18.4 @@ -33,11 +33,9 @@ oslo.utils<=3.41.0;python_version<'3.6' coverage>=4.5.2 pyudev # for ceph-* charm unit tests (need to fix the ceph-* charm unit tests/mocking) -git+https://github.com/openstack-charmers/zaza.git#egg=zaza -git+https://github.com/openstack-charmers/zaza-openstack-tests.git#egg=zaza.openstack - -# Needed for charm-glance: -git+https://opendev.org/openstack/tempest.git#egg=tempest;python_version>='3.6' -tempest<24.0.0;python_version<'3.6' +zaza==2024.* +git+https://github.com/openstack-charmers/zaza-openstack-tests.git@stable/yoga#egg=zaza.openstack croniter # needed for charm-rabbitmq-server unit tests +git+https://github.com/juju/charm-tools.git +flake8==7.1.1 diff --git a/tests/bundles/focal-xena.yaml b/tests/bundles/focal-xena.yaml index 796c773..891b11f 100644 --- a/tests/bundles/focal-xena.yaml +++ b/tests/bundles/focal-xena.yaml @@ -1,3 +1,5 @@ +local_overlay_enabled: False + variables: openstack-origin: &openstack-origin cloud:focal-xena @@ -31,13 +33,13 @@ applications: keystone-mysql-router: charm: ch:mysql-router - channel: latest/edge + channel: 8.0/edge glance-mysql-router: charm: ch:mysql-router - channel: latest/edge + channel: 8.0/edge cinder-mysql-router: charm: ch:mysql-router - channel: latest/edge + channel: 8.0/edge mysql-innodb-cluster: charm: ch:mysql-innodb-cluster @@ -48,7 +50,7 @@ applications: - '0' - '1' - '2' - channel: latest/edge + channel: 8.0/edge ceph-osd: charm: ch:ceph-osd @@ -64,7 +66,7 @@ applications: - '6' - '7' - '8' - channel: latest/edge + channel: quincy/edge ceph-radosgw: charm: ch:ceph-radosgw @@ -73,7 +75,7 @@ applications: source: *openstack-origin to: - '15' - channel: latest/edge + channel: quincy/edge ceph-mon: charm: ch:ceph-mon @@ -85,7 +87,7 @@ applications: - '9' - '10' - '11' - channel: latest/edge + channel: quincy/edge rabbitmq-server: charm: ch:rabbitmq-server @@ -94,7 +96,7 @@ applications: source: *openstack-origin to: - '12' - channel: latest/edge + channel: 3.9/edge keystone: expose: True @@ -104,11 +106,11 @@ applications: openstack-origin: *openstack-origin to: - '13' - channel: latest/edge + channel: yoga/edge glance: expose: True - charm: ../../glance.charm + charm: ../../glance_ubuntu@20.04-amd64.charm num_units: 1 options: openstack-origin: *openstack-origin @@ -131,7 +133,7 @@ applications: block-device: None to: - '16' - channel: latest/edge + channel: yoga/edge cinder-lvm: charm: ch:/cinder-lvm diff --git a/tests/bundles/focal-yoga.yaml b/tests/bundles/focal-yoga.yaml index a999e63..23685ba 100644 --- a/tests/bundles/focal-yoga.yaml +++ b/tests/bundles/focal-yoga.yaml @@ -1,3 +1,5 @@ +local_overlay_enabled: False + variables: openstack-origin: &openstack-origin cloud:focal-yoga @@ -31,13 +33,13 @@ applications: keystone-mysql-router: charm: ch:mysql-router - channel: latest/edge + channel: 8.0/edge glance-mysql-router: charm: ch:mysql-router - channel: latest/edge + channel: 8.0/edge cinder-mysql-router: charm: ch:mysql-router - channel: latest/edge + channel: 8.0/edge mysql-innodb-cluster: charm: ch:mysql-innodb-cluster @@ -48,7 +50,7 @@ applications: - '0' - '1' - '2' - channel: latest/edge + channel: 8.0/edge ceph-osd: charm: ch:ceph-osd @@ -64,7 +66,7 @@ applications: - '6' - '7' - '8' - channel: latest/edge + channel: quincy/edge ceph-radosgw: charm: ch:ceph-radosgw @@ -73,7 +75,7 @@ applications: source: *openstack-origin to: - '15' - channel: latest/edge + channel: quincy/edge ceph-mon: charm: ch:ceph-mon @@ -85,7 +87,7 @@ applications: - '9' - '10' - '11' - channel: latest/edge + channel: quincy/edge rabbitmq-server: charm: ch:rabbitmq-server @@ -94,7 +96,7 @@ applications: source: *openstack-origin to: - '12' - channel: latest/edge + channel: 3.9/edge keystone: expose: True @@ -104,11 +106,11 @@ applications: openstack-origin: *openstack-origin to: - '13' - channel: latest/edge + channel: yoga/edge glance: expose: True - charm: ../../glance.charm + charm: ../../glance_ubuntu@20.04-amd64.charm num_units: 1 options: openstack-origin: *openstack-origin @@ -131,7 +133,7 @@ applications: block-device: None to: - '16' - channel: latest/edge + channel: yoga/edge cinder-lvm: charm: ch:cinder-lvm @@ -142,7 +144,7 @@ applications: ephemeral-unmount: /mnt allocation-type: auto config-flags: target_helper=lioadm - channel: latest/edge + channel: yoga/edge relations: - - 'keystone:shared-db' diff --git a/tests/bundles/impish-xena.yaml b/tests/bundles/impish-xena.yaml deleted file mode 100644 index 0147699..0000000 --- a/tests/bundles/impish-xena.yaml +++ /dev/null @@ -1,193 +0,0 @@ -variables: - openstack-origin: &openstack-origin distro - -series: impish - -comment: -- 'machines section to decide order of deployment. database sooner = faster' -machines: - '0': - constraints: mem=3072M - '1': - constraints: mem=3072M - '2': - constraints: mem=3072M - '3': - '4': - '5': - '6': - '7': - '8': - '9': - '10': - '11': - '12': - '13': - '14': - '15': - '16': - -applications: - - keystone-mysql-router: - charm: ch:mysql-router - channel: latest/edge - glance-mysql-router: - charm: ch:mysql-router - channel: latest/edge - cinder-mysql-router: - charm: ch:mysql-router - channel: latest/edge - - mysql-innodb-cluster: - charm: ch:mysql-innodb-cluster - num_units: 3 - options: - source: *openstack-origin - to: - - '0' - - '1' - - '2' - channel: latest/edge - - ceph-osd: - charm: ch:ceph-osd - num_units: 6 - storage: - osd-devices: '10G' - options: - source: *openstack-origin - to: - - '3' - - '4' - - '5' - - '6' - - '7' - - '8' - channel: latest/edge - - ceph-radosgw: - charm: ch:ceph-radosgw - num_units: 1 - options: - source: *openstack-origin - to: - - '15' - channel: latest/edge - - ceph-mon: - charm: ch:ceph-mon - num_units: 3 - options: - source: *openstack-origin - monitor-count: '3' - to: - - '9' - - '10' - - '11' - channel: latest/edge - - rabbitmq-server: - charm: ch:rabbitmq-server - num_units: 1 - options: - source: *openstack-origin - to: - - '12' - channel: latest/edge - - keystone: - expose: True - charm: ch:keystone - num_units: 1 - options: - openstack-origin: *openstack-origin - to: - - '13' - channel: latest/edge - - glance: - expose: True - charm: ../../glance.charm - num_units: 1 - options: - openstack-origin: *openstack-origin - pool-type: erasure-coded - ec-profile-k: 4 - ec-profile-m: 2 - ec-profile-plugin: jerasure - to: - - '14' - - cinder: - expose: True - charm: ch:cinder - num_units: 1 - storage: - block-devices: '10G' - options: - openstack-origin: *openstack-origin - glance-api-version: 2 - block-device: None - to: - - '16' - channel: latest/edge - - cinder-lvm: - charm: ch:cinder-lvm - options: - block-device: '/tmp/vol1|4G' - alias: zaza-lvm - overwrite: "true" - ephemeral-unmount: /mnt - allocation-type: auto - config-flags: target_helper=lioadm - channel: latest/edge - -relations: - - - 'keystone:shared-db' - - 'keystone-mysql-router:shared-db' - - - 'keystone-mysql-router:db-router' - - 'mysql-innodb-cluster:db-router' - - - - 'glance:shared-db' - - 'glance-mysql-router:shared-db' - - - 'glance-mysql-router:db-router' - - 'mysql-innodb-cluster:db-router' - - - - 'glance:identity-service' - - 'keystone:identity-service' - - - - 'glance:amqp' - - 'rabbitmq-server:amqp' - - - - 'glance:ceph' - - 'ceph-mon:client' - - - - 'ceph-osd:mon' - - 'ceph-mon:osd' - - - - 'ceph-radosgw:mon' - - 'ceph-mon:radosgw' - - - 'ceph-radosgw:identity-service' - - 'keystone:identity-service' - - - 'ceph-radosgw:object-store' - - 'glance:object-store' - - - - 'cinder:shared-db' - - 'cinder-mysql-router:shared-db' - - - 'cinder-mysql-router:db-router' - - 'mysql-innodb-cluster:db-router' - - - - 'cinder:identity-service' - - 'keystone:identity-service' - - - - 'cinder:amqp' - - 'rabbitmq-server:amqp' - - - - 'cinder:image-service' - - 'glance:image-service' - - - 'cinder:cinder-volume-service' - - 'glance:cinder-volume-service' - - - 'cinder-lvm:storage-backend' - - 'cinder:storage-backend' diff --git a/tests/bundles/jammy-yoga.yaml b/tests/bundles/jammy-yoga.yaml index 94e0516..cf9af37 100644 --- a/tests/bundles/jammy-yoga.yaml +++ b/tests/bundles/jammy-yoga.yaml @@ -1,3 +1,5 @@ +local_overlay_enabled: False + variables: openstack-origin: &openstack-origin distro @@ -31,13 +33,13 @@ applications: keystone-mysql-router: charm: ch:mysql-router - channel: latest/edge + channel: 8.0/edge glance-mysql-router: charm: ch:mysql-router - channel: latest/edge + channel: 8.0/edge cinder-mysql-router: charm: ch:mysql-router - channel: latest/edge + channel: 8.0/edge mysql-innodb-cluster: charm: ch:mysql-innodb-cluster @@ -48,7 +50,7 @@ applications: - '0' - '1' - '2' - channel: latest/edge + channel: 8.0/edge ceph-osd: charm: ch:ceph-osd @@ -64,7 +66,7 @@ applications: - '6' - '7' - '8' - channel: latest/edge + channel: quincy/edge ceph-radosgw: charm: ch:ceph-radosgw @@ -73,7 +75,7 @@ applications: source: *openstack-origin to: - '15' - channel: latest/edge + channel: quincy/edge ceph-mon: charm: ch:ceph-mon @@ -85,7 +87,7 @@ applications: - '9' - '10' - '11' - channel: latest/edge + channel: quincy/edge rabbitmq-server: charm: ch:rabbitmq-server @@ -94,7 +96,7 @@ applications: source: *openstack-origin to: - '12' - channel: latest/edge + channel: 3.9/edge keystone: expose: True @@ -104,11 +106,11 @@ applications: openstack-origin: *openstack-origin to: - '13' - channel: latest/edge + channel: yoga/edge glance: expose: True - charm: ../../glance.charm + charm: ../../glance_ubuntu@22.04-amd64.charm num_units: 1 options: openstack-origin: *openstack-origin @@ -131,7 +133,7 @@ applications: block-device: None to: - '16' - channel: latest/edge + channel: yoga/edge cinder-lvm: charm: ch:cinder-lvm @@ -142,7 +144,7 @@ applications: ephemeral-unmount: /mnt allocation-type: auto config-flags: target_helper=lioadm - channel: latest/edge + channel: yoga/edge relations: - - 'keystone:shared-db' diff --git a/tests/tests.yaml b/tests/tests.yaml index e1a424d..9625917 100644 --- a/tests/tests.yaml +++ b/tests/tests.yaml @@ -8,7 +8,6 @@ smoke_bundles: gate_bundles: - focal-xena - - impish-xena dev_bundles: - focal-yoga @@ -29,6 +28,7 @@ tests: - zaza.openstack.charm_tests.glance.tests.GlanceCephRGWBackendTest - zaza.openstack.charm_tests.glance.tests.GlanceExternalS3Test - zaza.openstack.charm_tests.glance.tests.GlanceCinderBackendTest + - zaza.openstack.charm_tests.audit.tests.KeystoneAuditMiddlewareTest - zaza.openstack.charm_tests.policyd.tests.GlanceTests - zaza.openstack.charm_tests.ceph.tests.CheckPoolTypes - zaza.openstack.charm_tests.ceph.tests.BlueStoreCompressionCharmOperation @@ -38,11 +38,12 @@ tests: - zaza.openstack.charm_tests.policyd.tests.GlanceTests tests_options: + audit-middleware: + service: glance tempest: full_run: smoke: True policyd: service: glance force_deploy: - - impish-xena - jammy-yoga diff --git a/tox.ini b/tox.ini index c41077d..93a22a2 100644 --- a/tox.ini +++ b/tox.ini @@ -3,96 +3,78 @@ # within individual charm repos. See the 'global' dir contents for available # choices of tox.ini for OpenStack Charms: # https://github.com/openstack-charmers/release-tools -# -# TODO: Distill the func test requirements from the lint/unit test -# requirements. They are intertwined. Also, Zaza itself should specify -# all of its own requirements and if it doesn't, fix it there. + [tox] envlist = pep8,py3 -skipsdist = True # NOTE: Avoid build/test env pollution by not enabling sitepackages. sitepackages = False # NOTE: Avoid false positives by not skipping missing interpreters. skip_missing_interpreters = False -# NOTES: -# * We avoid the new dependency resolver by pinning pip < 20.3, see -# https://github.com/pypa/pip/issues/9187 -# * Pinning dependencies requires tox >= 3.2.0, see -# https://tox.readthedocs.io/en/latest/config.html#conf-requires -# * It is also necessary to pin virtualenv as a newer virtualenv would still -# lead to fetching the latest pip in the func* tox targets, see -# https://stackoverflow.com/a/38133283 -requires = - pip < 20.3 - virtualenv < 20.0 - setuptools < 50.0.0 - -# NOTE: https://wiki.canonical.com/engineering/OpenStack/InstallLatestToxOnOsci -minversion = 3.18.0 [testenv] +basepython = python3.10 +# We use tox mainly for virtual environment management for test requirements +# and do not install the charm code as a Python package into that environment. +# Ref: https://tox.wiki/en/latest/config.html#skip_install +skip_install = True setenv = VIRTUAL_ENV={envdir} PYTHONHASHSEED=0 - CHARM_DIR={envdir} -install_command = - {toxinidir}/pip.sh install {opts} {packages} -commands = stestr run --slowest {posargs} +passenv = + CS_* + OS_* + TEST_* + # The following are needed for building in environments that use a proxy + # for external access. + http_proxy + https_proxy + no_proxy allowlist_externals = charmcraft - rename.sh -passenv = HOME TERM CS_* OS_* TEST_* -deps = -r{toxinidir}/test-requirements.txt +deps = + -c {env:TEST_CONSTRAINTS_FILE:https://raw.githubusercontent.com/openstack-charmers/zaza/master/constraints-juju36.txt} + -r{toxinidir}/merged-requirements-py310.txt + +[testenv:update-requirements] +basepython = python3.10 +deps = pip-tools +commands = + pip-compile --unsafe-package juju --unsafe-package kubernetes --output-file=test-requirements-py310.txt test-requirements.in + pip-compile --unsafe-package juju --unsafe-package kubernetes --output-file=merged-requirements-py310.txt requirements.in test-requirements.in + +[testenv:update-requirements-focal] +basepython = python3.8 +deps = pip-tools +commands = + pip-compile --unsafe-package juju --unsafe-package kubernetes --output-file=merged-requirements-py38.txt requirements.in test-requirements.in [testenv:build] -basepython = python3 -deps = -r{toxinidir}/build-requirements.txt +deps = +# charmcraft clean is done to ensure that +# `tox -e build` always performs a clean, repeatable build. +# For faster rebuilds during development, +# directly run `charmcraft -v pack commands = charmcraft clean - charmcraft -v build - {toxinidir}/rename.sh + charmcraft -v pack + charmcraft clean [testenv:py3] -basepython = python3 -deps = -r{toxinidir}/requirements.txt - -r{toxinidir}/test-requirements.txt - -[testenv:py36] -basepython = python3.6 -deps = -r{toxinidir}/requirements.txt - -r{toxinidir}/test-requirements.txt commands = stestr run --slowest {posargs} -[testenv:py38] -basepython = python3.8 -deps = -r{toxinidir}/requirements.txt - -r{toxinidir}/test-requirements.txt -commands = stestr run --slowest {posargs} - -[testenv:py39] -basepython = python3.9 -deps = -r{toxinidir}/requirements.txt - -r{toxinidir}/test-requirements.txt +[testenv:py310] commands = stestr run --slowest {posargs} -[testenv:py310] -basepython = python3.10 -deps = -r{toxinidir}/requirements.txt - -r{toxinidir}/test-requirements.txt +[testenv:py38] +basepython = python3.8 +deps = -r {toxinidir}/merged-requirements-py38.txt commands = stestr run --slowest {posargs} [testenv:pep8] -basepython = python3 -deps = flake8==3.9.2 - charm-tools==2.8.3 +deps = -r {toxinidir}/test-requirements-py310.txt commands = flake8 {posargs} hooks unit_tests tests actions lib files charm-proof [testenv:cover] -# Technique based heavily upon -# https://github.com/openstack/nova/blob/master/tox.ini -basepython = python3 -deps = -r{toxinidir}/requirements.txt - -r{toxinidir}/test-requirements.txt setenv = {[testenv]setenv} PYTHON=coverage run @@ -116,31 +98,25 @@ omit = unit_tests/* [testenv:venv] -basepython = python3 commands = {posargs} [testenv:func-noop] -basepython = python3 commands = functest-run-suite --help [testenv:func] -basepython = python3 commands = functest-run-suite --keep-model [testenv:func-smoke] -basepython = python3 commands = functest-run-suite --keep-model --smoke [testenv:func-dev] -basepython = python3 commands = functest-run-suite --keep-model --dev [testenv:func-target] -basepython = python3 commands = functest-run-suite --keep-model --bundle {posargs} diff --git a/unit_tests/test_glance_contexts.py b/unit_tests/test_glance_contexts.py index 983577c..2643279 100644 --- a/unit_tests/test_glance_contexts.py +++ b/unit_tests/test_glance_contexts.py @@ -77,6 +77,34 @@ def test_glance_context_image_size_cap(self): "/var/lib/glance/images/", 'image_size_cap': 1099511627776}) + def test_glance_image_import_context(self): + config = { + 'image-conversion': True, + 'custom-import-properties': 'p1:v1,prop2:rng1:rng2'} + self.config.side_effect = lambda x: config[x] + self.assertEqual(contexts.GlanceImageImportContext()(), + {'image_import_plugins': [ + 'image_conversion', + 'inject_image_metadata'], + 'custom_import_properties': + 'p1:v1,prop2:rng1:rng2'}) + config = { + 'image-conversion': False, + 'custom-import-properties': ''} + self.config.side_effect = lambda x: config[x] + self.assertEqual(contexts.GlanceImageImportContext()(), + {'image_import_plugins': []}) + + config = { + 'image-conversion': False, + 'custom-import-properties': 'prop-noval'} + self.assertRaises(ValueError) + + config = { + 'image-conversion': False, + 'custom-import-properties': 10} + self.assertRaises(ValueError) + def test_swift_not_related(self): self.relation_ids.return_value = [] self.assertEqual(contexts.ObjectStoreContext()(), {}) @@ -363,7 +391,9 @@ def _relation_ids(*args, **kwargs): 'default_store_backend': 'cinder', }) - def test_multi_backend_with_cinder_volume_types_defined(self): + @patch('charmhelpers.contrib.openstack.' + 'context.IdentityServiceContext.__call__') + def test_multi_backend_with_cinder_volume_types_defined(self, keystone): # return relation_ids only for cinder but not for swift def _relation_ids(*args, **kwargs): if args[0] == 'cinder-volume-service': @@ -373,6 +403,12 @@ def _relation_ids(*args, **kwargs): self.maxDiff = None self.relation_ids.side_effect = _relation_ids + keystone.return_value = { + 'admin_user': 'user', + 'admin_password': 'password', + 'admin_tenant_name': 'services', + 'keystone_authtoken': {'auth_url': 'http://10.0.0.1:35357/v3'}, + } self.is_relation_made.return_value = False data_dir = '/some/data/dir' conf_dict = { @@ -394,6 +430,11 @@ def _relation_ids(*args, **kwargs): 'cinder_volume_type': 'volume-type-test', 'cinder_http_retries': 3, 'cinder_state_transition_timeout': 30, + 'cinder_store_user_name': 'user', + 'cinder_store_password': 'password', + 'cinder_store_project_name': 'services', + 'cinder_store_auth_address': + 'http://10.0.0.1:35357/v3', } }, 'enabled_backends': 'local:file, volume-type-test:cinder', diff --git a/unit_tests/test_glance_utils.py b/unit_tests/test_glance_utils.py index 8eb5654..95adeed 100644 --- a/unit_tests/test_glance_utils.py +++ b/unit_tests/test_glance_utils.py @@ -153,6 +153,7 @@ def test_restart_map_rocky(self): (utils.GLANCE_API_CONF, ['glance-api']), (utils.GLANCE_SWIFT_CONF, ['glance-api']), (utils.GLANCE_POLICY_FILE, ['glance-api', 'glance-registry']), + (utils.GLANCE_API_PASTE, ['glance-api']), (utils.ceph_config_file(), ['glance-api', 'glance-registry']), (utils.HAPROXY_CONF, ['haproxy']), (utils.HTTPS_APACHE_CONF, ['apache2']), @@ -175,6 +176,31 @@ def test_restart_map_stein(self): (utils.GLANCE_API_CONF, ['glance-api']), (utils.GLANCE_SWIFT_CONF, ['glance-api']), (utils.GLANCE_POLICY_FILE, ['glance-api']), + (utils.GLANCE_API_PASTE, ['glance-api']), + (utils.ceph_config_file(), ['glance-api']), + (utils.HAPROXY_CONF, ['haproxy']), + (utils.HTTPS_APACHE_CONF, ['apache2']), + (utils.HTTPS_APACHE_24_CONF, ['apache2']), + (utils.APACHE_PORTS_CONF, ['apache2']), + (utils.MEMCACHED_CONF, ['memcached']), + ]) + self.assertEqual(ex_map, utils.restart_map()) + self.enable_memcache.return_value = False + del ex_map[utils.MEMCACHED_CONF] + self.assertEqual(ex_map, utils.restart_map()) + + def test_restart_map_yoga(self): + self.enable_memcache.return_value = True + self.config.side_effect = None + self.service_name.return_value = 'glance' + self.os_release.return_value = 'yoga' + + ex_map = OrderedDict([ + (utils.GLANCE_API_CONF, ['glance-api']), + (utils.GLANCE_SWIFT_CONF, ['glance-api']), + (utils.GLANCE_POLICY_FILE, ['glance-api']), + (utils.GLANCE_AUDIT_MAP, ['glance-api']), + (utils.GLANCE_API_PASTE, ['glance-api']), (utils.ceph_config_file(), ['glance-api']), (utils.HAPROXY_CONF, ['haproxy']), (utils.HTTPS_APACHE_CONF, ['apache2']), @@ -199,6 +225,7 @@ def test_restart_map_stein_ssl(self, isdir): (utils.GLANCE_API_CONF, ['glance-api']), (utils.GLANCE_SWIFT_CONF, ['glance-api']), (utils.GLANCE_POLICY_FILE, ['glance-api']), + (utils.GLANCE_API_PASTE, ['glance-api']), (utils.ceph_config_file(), ['glance-api']), (utils.HAPROXY_CONF, ['haproxy']), (utils.HTTPS_APACHE_CONF, ['apache2']), @@ -353,10 +380,6 @@ def test_reinstall_paste_ini(self, kv, _os, mock_os_release): call(utils.GLANCE_REGISTRY_PASTE), call(utils.GLANCE_API_PASTE), ]) - _os.remove.assert_has_calls([ - call(utils.GLANCE_REGISTRY_PASTE), - call(utils.GLANCE_API_PASTE), - ]) self.assertTrue(test_kv.get(utils.PASTE_INI_MARKER)) self.assertTrue(test_kv.flushed) @@ -423,6 +446,32 @@ def test_reinstall_paste_ini_fix_upgrade(self, kv, mock_os_release, utils.reinstall_paste_ini() self.assertTrue(self.apt_install.called) + @patch.object(utils, 'os_release') + @patch.object(utils, 'os') + @patch.object(utils, 'kv') + def test_reinstall_paste_ini_rocky_restore(self, kv, _os, mock_os_release): + """Ensure that paste.ini files are restored""" + mock_os_release.return_value = 'rocky' + _os.path.exists.return_value = True + test_kv = SimpleKV() + test_kv.set(utils.PASTE_INI_MARKER, False) + kv.return_value = test_kv + + self.apt_install.reset_mock() + utils.reinstall_paste_ini() + + self.apt_install.assert_called_with( + packages=['glance-common'], + options=utils.REINSTALL_OPTIONS, + fatal=True + ) + reg_paste_backup = f'{utils.GLANCE_REGISTRY_PASTE}.charm.upgrade' + api_paste_backup = f'{utils.GLANCE_API_PASTE}.charm.upgrade' + _os.remove.assert_has_calls([ + call(reg_paste_backup), + call(api_paste_backup), + ]) + def _test_is_api_ready(self, tgt): fake_config = MagicMock() with patch.object(utils, 'incomplete_relation_data') as ird: