From 7bddcb9c83eb06a5d49080422f6a98c76c460c78 Mon Sep 17 00:00:00 2001 From: John Garbutt Date: Thu, 18 Jan 2024 11:21:30 +0000 Subject: [PATCH 01/13] Initial plumbing for create_manila_project_share --- api/azimuth/cluster_api/openstack.py | 2 ++ api/azimuth/provider/openstack/provider.py | 30 ++++++++++++++++++- .../files/api/settings/04-cloud-provider.yaml | 1 + chart/values.yaml | 2 ++ 4 files changed, 34 insertions(+), 1 deletion(-) diff --git a/api/azimuth/cluster_api/openstack.py b/api/azimuth/cluster_api/openstack.py index 61880e15..f6750f5d 100644 --- a/api/azimuth/cluster_api/openstack.py +++ b/api/azimuth/cluster_api/openstack.py @@ -30,3 +30,5 @@ def _ensure_shared_resources(self): # Just make sure that the shared tenant network exists # This allows templates to target it via a tag filter if they want self._cloud_session._tenant_network(True) + # For consistency, ensure the project share is created + self._cloud_session._project_share(True) diff --git a/api/azimuth/provider/openstack/provider.py b/api/azimuth/provider/openstack/provider.py index e3e410de..e3aacd8e 100644 --- a/api/azimuth/provider/openstack/provider.py +++ b/api/azimuth/provider/openstack/provider.py @@ -140,6 +140,8 @@ class Provider(base.Provider): fragment ``{tenant_name}``. create_internal_net: If ``True`` (the default), then the internal network is auto-created when a tagged network or templated network cannot be found. + create_manila_project_share: If ``True`` (the default is False), then + manila project share is auto created when cannot be found. internal_net_cidr: The CIDR for the internal network when it is auto-created (default ``192.168.3.0/24``). internal_net_dns_nameservers: The DNS nameservers for the internal network when it is @@ -163,6 +165,7 @@ def __init__(self, auth_url, internal_net_template = None, external_net_template = None, create_internal_net = True, + create_manila_project_share = False, internal_net_cidr = "192.168.3.0/24", internal_net_dns_nameservers = None, az_backdoor_net_map = None, @@ -176,6 +179,7 @@ def __init__(self, auth_url, self._internal_net_template = internal_net_template self._external_net_template = external_net_template self._create_internal_net = create_internal_net + self._create_manila_project_share = create_manila_project_share self._internal_net_cidr = internal_net_cidr self._internal_net_dns_nameservers = internal_net_dns_nameservers self._az_backdoor_net_map = az_backdoor_net_map or dict() @@ -202,6 +206,7 @@ def from_token(self, token): internal_net_template = self._internal_net_template, external_net_template = self._external_net_template, create_internal_net = self._create_internal_net, + create_manila_project_share = self._create_manila_project_share, internal_net_cidr = self._internal_net_cidr, internal_net_dns_nameservers = self._internal_net_dns_nameservers, az_backdoor_net_map = self._az_backdoor_net_map, @@ -220,6 +225,7 @@ def __init__(self, connection, internal_net_template = None, external_net_template = None, create_internal_net = True, + create_manila_project_share = False, internal_net_cidr = "192.168.3.0/24", internal_net_dns_nameservers = None, az_backdoor_net_map = None, @@ -229,6 +235,7 @@ def __init__(self, connection, self._internal_net_template = internal_net_template self._external_net_template = external_net_template self._create_internal_net = create_internal_net + self._create_manila_project_share = create_manila_project_share self._internal_net_cidr = internal_net_cidr self._internal_net_dns_nameservers = internal_net_dns_nameservers self._az_backdoor_net_map = az_backdoor_net_map or dict() @@ -366,6 +373,7 @@ def scoped_session(self, tenancy): internal_net_template = self._internal_net_template, external_net_template = self._external_net_template, create_internal_net = self._create_internal_net, + create_manila_project_share = self._create_manila_project_share, internal_net_cidr = self._internal_net_cidr, internal_net_dns_nameservers = self._internal_net_dns_nameservers, az_backdoor_net_map = self._az_backdoor_net_map, @@ -397,6 +405,7 @@ def __init__(self, username, internal_net_template = None, external_net_template = None, create_internal_net = True, + create_manila_project_share = False, internal_net_cidr = "192.168.3.0/24", internal_net_dns_nameservers = None, az_backdoor_net_map = None, @@ -408,11 +417,18 @@ def __init__(self, username, self._internal_net_template = internal_net_template self._external_net_template = external_net_template self._create_internal_net = create_internal_net + self._create_manila_project_share = create_manila_project_share self._internal_net_cidr = internal_net_cidr self._internal_net_dns_nameservers = internal_net_dns_nameservers self._az_backdoor_net_map = az_backdoor_net_map or dict() self._backdoor_vnic_type = backdoor_vnic_type + # TODO(johngarbutt): consider moving some of this to config + self._project_share_name = "azimuth-project-share" + prefix = "proj" + project_id_safe = self._connection.project_id.replace("-", "") + self._project_share_user = prefix + project_id_safe + def _log(self, message, *args, level = logging.INFO, **kwargs): logger.log( level, @@ -690,6 +706,9 @@ def _tenant_network(self, create_network = False): else: raise errors.InvalidOperationError("Could not find internal network.") + def project_share(self, create_share=True): + pass + def _external_network(self): """ Returns the external network that connects the tenant router to the outside world. @@ -1383,7 +1402,7 @@ def cluster_parameters(self): """ # Inject information about the networks to use external_network = self._external_network().name - return dict( + params = dict( # Legacy name cluster_floating_network = external_network, # New name @@ -1391,6 +1410,15 @@ def cluster_parameters(self): cluster_network = self._tenant_network(True).name ) + # Optionally inject name of the project specific manila share + project_share = self._project_share(True) + if project_share: + params["cluster_project_manila_share_name"] = project_share.name + user = self._project_share_user + params["cluster_project_manila_share_user"] = user + + return params + def cluster_modify(self, cluster): """ See :py:meth:`.base.ScopedSession.cluster_modify`. diff --git a/chart/files/api/settings/04-cloud-provider.yaml b/chart/files/api/settings/04-cloud-provider.yaml index 0c147dc4..4985f6e0 100644 --- a/chart/files/api/settings/04-cloud-provider.yaml +++ b/chart/files/api/settings/04-cloud-provider.yaml @@ -16,6 +16,7 @@ AZIMUTH: EXTERNAL_NET_TEMPLATE: {{ . | quote }} {{- end }} CREATE_INTERNAL_NET: {{ .Values.provider.openstack.createInternalNet }} + CREATE_MANILA_PROJECT_SHARE: {{ .Values.provider.openstack.createManilaProjectShare }} {{- with .Values.provider.openstack.internalNetCidr }} INTERNAL_NET_CIDR: {{ . | quote }} {{- end }} diff --git a/chart/values.yaml b/chart/values.yaml index 8dbaa4d6..1bf7e7f8 100644 --- a/chart/values.yaml +++ b/chart/values.yaml @@ -267,6 +267,8 @@ provider: externalNetTemplate: # Indicates whether tenant internal networks should be auto-created if not present createInternalNet: true + # Indicates whether project specific manila share should be auto-created if not present + createManilaProjectShare: false # The CIDR to use for auto-created tenant internal networks # Defaults to 192.168.3.0/24 if not given, which should be OK for most circumstances internalNetCidr: From 1d29c801bea507caf629f1950ed755dcbffe17eb Mon Sep 17 00:00:00 2001 From: John Garbutt Date: Mon, 22 Jan 2024 17:46:17 +0000 Subject: [PATCH 02/13] Adding initial calls to Manila --- .../provider/openstack/api/__init__.py | 2 +- api/azimuth/provider/openstack/api/core.py | 9 ++ api/azimuth/provider/openstack/api/shares.py | 86 +++++++++++++++++++ api/azimuth/provider/openstack/provider.py | 78 ++++++++++++++++- 4 files changed, 172 insertions(+), 3 deletions(-) create mode 100644 api/azimuth/provider/openstack/api/shares.py diff --git a/api/azimuth/provider/openstack/api/__init__.py b/api/azimuth/provider/openstack/api/__init__.py index 34c667bb..42d7151f 100644 --- a/api/azimuth/provider/openstack/api/__init__.py +++ b/api/azimuth/provider/openstack/api/__init__.py @@ -1,3 +1,3 @@ from .core import Connection, ServiceNotSupported # Import the modules for each of the services -from . import block_store, coe, compute, identity, image, network, orchestration +from . import block_store, coe, compute, identity, image, network, orchestration, shares diff --git a/api/azimuth/provider/openstack/api/core.py b/api/azimuth/provider/openstack/api/core.py index e71c142b..b431244e 100644 --- a/api/azimuth/provider/openstack/api/core.py +++ b/api/azimuth/provider/openstack/api/core.py @@ -13,6 +13,11 @@ logger = logging.getLogger(__name__) +# get some debug logs +logger2 = logging.getLogger('rackit.connection') +logger2.setLevel(logging.DEBUG) +logger2.addHandler(logging.StreamHandler()) + class UnmanagedResourceOptions(rackit.resource.Options): def __init__(self, options = None): @@ -59,6 +64,8 @@ def extract_list(self, response): # OpenStack responses have the list under a named key # If there is a next page, that is provided under a links attribute data = response.json() + if self.resource_cls._opts.resource_list_key not in data: + raise Exception(f"{data}") list_data = data[self.resource_cls._opts.resource_list_key] next_url = self.extract_next_url(data) return list_data, next_url, {} @@ -357,6 +364,8 @@ def prepare_request(self, request): # If a specific microversion is requested, add the appropriate header if self.microversion: request.headers["OpenStack-API-Version"] = f"{self.catalog_type} {self.microversion}" + if self.catalog_type == "sharev2": + request.headers["X-OpenStack-Manila-API-Version"] = f"{self.microversion}" return super().prepare_request(request) def extract_error_message(self, response): diff --git a/api/azimuth/provider/openstack/api/shares.py b/api/azimuth/provider/openstack/api/shares.py new file mode 100644 index 00000000..0e7ceaec --- /dev/null +++ b/api/azimuth/provider/openstack/api/shares.py @@ -0,0 +1,86 @@ +from rackit import RootResource, EmbeddedResource, Endpoint + +from .core import ( + Service, + UnmanagedResource, + Resource, + ResourceWithDetail +) + + +class ShareType(Resource): + """ + Resource for accessing share types. + """ + class Meta: + endpoint = '/types' + resource_key = "share_type" + resource_list_key = "share_types" + + +class Share(ResourceWithDetail): + """ + Resource for accessing shares. + """ + class Meta: + endpoint = '/shares' + + def grant_rw_access(self, username): + self._action('action', { + 'allow_access': { + "access_level": "rw", + "access_type": "cephx", + "access_to": username, + } + }) + + +class ShareAccess(Resource): + """ + Resource for share access lists. + """ + class Meta: + endpoint = '/share-access-rules' + resource_key = "access" + resource_list_key = "access_list" + + +class AbsoluteLimits(UnmanagedResource): + """ + Represents the absolute limits for a project. + """ + class Meta: + aliases = dict( + # TODO(johngarbutt): fill out the rest? + total_shares_gb='maxTotalShareGigabytes', + total_shares_gb_used='totalShareGigabytesUsed', + total_shares='maxTotalShares', + total_shares_used='totalSharesUsed', + ) + + +class ShareLimits(UnmanagedResource): + """ + Represents the limits for a project. + + This is not a REST-ful resource, so is unmanaged. + """ + class Meta: + endpoint = "/limits" + + absolute = EmbeddedResource(AbsoluteLimits) + + +class ShareService(Service): + """ + OpenStack service class for the compute service. + """ + name = "share" + catalog_type = "sharev2" + microversion = '2.51' + path_prefix = '/v2/{project_id}' + + limits = Endpoint(ShareLimits) + shares = RootResource(Share) + types = RootResource(ShareType) + access = RootResource(ShareAccess) diff --git a/api/azimuth/provider/openstack/provider.py b/api/azimuth/provider/openstack/provider.py index e3aacd8e..d436de96 100644 --- a/api/azimuth/provider/openstack/provider.py +++ b/api/azimuth/provider/openstack/provider.py @@ -706,8 +706,82 @@ def _tenant_network(self, create_network = False): else: raise errors.InvalidOperationError("Could not find internal network.") - def project_share(self, create_share=True): - pass + def _project_share(self, create_share=True): + # By default we don't try to touch Manila + # TODO add me back! + # TODO remove me!! + self._create_manila_project_share = True + self._project_share_name = "johng-dev" + + if not self._create_manila_project_share: + return + + # find if project share exists + project_share = None + current_shares = self._connection.share.shares.all() + for share in current_shares: + if share.name == self._project_share_name: + self._log(f"Found project share for: {self._connection.project_id}") + project_share = share + + # no share found, create if required + if not project_share and self._create_manila_project_share: + self._log(f"Creating share for: {self._connection.project_id}") + + # find correct type + default_share_type = None + all_types = list(self._connection.share.types.all()) + if len(all_types) == 1: + default_share_type = all_types[0] + else: + for share_type in all_types: + if share_type.is_default: + default_share_type = share_type + break + + if not default_share_type: + raise Exception("Unable to find valid share type!") + self._log(f"Found share type: {default_share_type.name}") + + # TODO(johngarbutt) need to support non-ceph types + # and rework the access system based on that. + project_share = self._connection.share.shares.create( + share_proto="CephFS", + size=10, + name=self._project_share_name, + description="Project share auto-created by Azimuth.", + share_type=default_share_type.id) + # TODO: correctly poll for async actions being complete + import time + time.sleep(0.5) + project_share.grant_rw_access(self._project_share_user) + time.sleep(0.5) + self._log(f"Create new project share: {project_share}") + + # Ensure expected access user is present + if project_share: + # double check share has the correct protocol and is available + share_details = self._connection.share.shares.get(project_share.id) + self._log(f"Got share details f{share_details}") + if share_details.share_proto.upper() != "CEPHFS": + raise Exception("Currently only support CephFS shares!") + if share_details.status.lower() != "available": + raise Exception("Share is not available!") + if share_details.access_rules_status.lower() != "active": + raise Exception("Share has a problem with its access rules!") + + access_list = list(self._connection.share.access.all( + share_id=project_share.id)) + self._log(f"Share access list found: {access_list}") + found_expected_access = False + for access in access_list: + if access.access_to == self._project_share_user: + found_expected_access = True + break + if not found_expected_access: + raise Exception("can't find the expected access rule!") + + raise Exception("FIXME!") def _external_network(self): """ From af16d7e6e38a41edf41369a5eb88d31d46fd9e92 Mon Sep 17 00:00:00 2001 From: John Garbutt Date: Tue, 23 Jan 2024 18:33:02 +0000 Subject: [PATCH 03/13] Remove bits of debugging code --- api/azimuth/provider/openstack/api/core.py | 7 ------- api/azimuth/provider/openstack/provider.py | 8 -------- 2 files changed, 15 deletions(-) diff --git a/api/azimuth/provider/openstack/api/core.py b/api/azimuth/provider/openstack/api/core.py index b431244e..4d7324d0 100644 --- a/api/azimuth/provider/openstack/api/core.py +++ b/api/azimuth/provider/openstack/api/core.py @@ -13,11 +13,6 @@ logger = logging.getLogger(__name__) -# get some debug logs -logger2 = logging.getLogger('rackit.connection') -logger2.setLevel(logging.DEBUG) -logger2.addHandler(logging.StreamHandler()) - class UnmanagedResourceOptions(rackit.resource.Options): def __init__(self, options = None): @@ -64,8 +59,6 @@ def extract_list(self, response): # OpenStack responses have the list under a named key # If there is a next page, that is provided under a links attribute data = response.json() - if self.resource_cls._opts.resource_list_key not in data: - raise Exception(f"{data}") list_data = data[self.resource_cls._opts.resource_list_key] next_url = self.extract_next_url(data) return list_data, next_url, {} diff --git a/api/azimuth/provider/openstack/provider.py b/api/azimuth/provider/openstack/provider.py index d436de96..63074068 100644 --- a/api/azimuth/provider/openstack/provider.py +++ b/api/azimuth/provider/openstack/provider.py @@ -707,12 +707,6 @@ def _tenant_network(self, create_network = False): raise errors.InvalidOperationError("Could not find internal network.") def _project_share(self, create_share=True): - # By default we don't try to touch Manila - # TODO add me back! - # TODO remove me!! - self._create_manila_project_share = True - self._project_share_name = "johng-dev" - if not self._create_manila_project_share: return @@ -781,8 +775,6 @@ def _project_share(self, create_share=True): if not found_expected_access: raise Exception("can't find the expected access rule!") - raise Exception("FIXME!") - def _external_network(self): """ Returns the external network that connects the tenant router to the outside world. From 61ec5a8593e79d622abde16205727e81ddbe79d0 Mon Sep 17 00:00:00 2001 From: John Garbutt Date: Tue, 23 Jan 2024 18:59:27 +0000 Subject: [PATCH 04/13] Some tidy ups --- api/azimuth/provider/openstack/provider.py | 75 ++++++++++++---------- 1 file changed, 40 insertions(+), 35 deletions(-) diff --git a/api/azimuth/provider/openstack/provider.py b/api/azimuth/provider/openstack/provider.py index 63074068..1bd2487b 100644 --- a/api/azimuth/provider/openstack/provider.py +++ b/api/azimuth/provider/openstack/provider.py @@ -10,6 +10,7 @@ import logging import random import re +import time import dateutil.parser @@ -707,6 +708,9 @@ def _tenant_network(self, create_network = False): raise errors.InvalidOperationError("Could not find internal network.") def _project_share(self, create_share=True): + self._create_manila_project_share = True + self._project_share_name = "johng-dev" + if not self._create_manila_project_share: return @@ -715,14 +719,34 @@ def _project_share(self, create_share=True): current_shares = self._connection.share.shares.all() for share in current_shares: if share.name == self._project_share_name: - self._log(f"Found project share for: {self._connection.project_id}") project_share = share + if project_share: + # double check share has the correct protocol and is available + share_details = self._connection.share.shares.get(project_share.id) + self._log(f"Got share details f{share_details}") + if share_details.share_proto.upper() != "CEPHFS": + raise Exception("Currently only support CephFS shares!") + if share_details.status.lower() != "available": + raise Exception("Share is not available!") + if share_details.access_rules_status.lower() != "active": + raise Exception("Share has a problem with its access rules!") + + access_list = list(self._connection.share.access.all( + share_id=project_share.id)) + found_expected_access = False + for access in access_list: + if access.access_to == self._project_share_user: + found_expected_access = True + break + if not found_expected_access: + raise Exception("can't find the expected access rule!") + self._log(f"Found project share for: {self._connection.project_id}") # no share found, create if required - if not project_share and self._create_manila_project_share: - self._log(f"Creating share for: {self._connection.project_id}") + if not project_share and create_share: + self._log(f"Creating project share for: {self._connection.project_id}") - # find correct type + # Find share type default_share_type = None all_types = list(self._connection.share.types.all()) if len(all_types) == 1: @@ -732,48 +756,29 @@ def _project_share(self, create_share=True): if share_type.is_default: default_share_type = share_type break - if not default_share_type: raise Exception("Unable to find valid share type!") - self._log(f"Found share type: {default_share_type.name}") - # TODO(johngarbutt) need to support non-ceph types - # and rework the access system based on that. + # TODO(johngarbutt) need to support non-ceph types eventually project_share = self._connection.share.shares.create( share_proto="CephFS", size=10, name=self._project_share_name, description="Project share auto-created by Azimuth.", share_type=default_share_type.id) - # TODO: correctly poll for async actions being complete - import time - time.sleep(0.5) - project_share.grant_rw_access(self._project_share_user) - time.sleep(0.5) - self._log(f"Create new project share: {project_share}") - # Ensure expected access user is present - if project_share: - # double check share has the correct protocol and is available - share_details = self._connection.share.shares.get(project_share.id) - self._log(f"Got share details f{share_details}") - if share_details.share_proto.upper() != "CEPHFS": - raise Exception("Currently only support CephFS shares!") - if share_details.status.lower() != "available": - raise Exception("Share is not available!") - if share_details.access_rules_status.lower() != "active": - raise Exception("Share has a problem with its access rules!") - - access_list = list(self._connection.share.access.all( - share_id=project_share.id)) - self._log(f"Share access list found: {access_list}") - found_expected_access = False - for access in access_list: - if access.access_to == self._project_share_user: - found_expected_access = True + # wait for share to be available before trying to grant access + for _ in range(10): + latest = self._connection.share.shares.get(project_share.id) + if latest.status.lower() == "available": break - if not found_expected_access: - raise Exception("can't find the expected access rule!") + time.sleep(0.1) + + project_share.grant_rw_access(self._project_share_user) + # TODO(johngarbutt) should we wait for access to be granted? + self._log(f"Created new project share: {project_share.id}") + + return project_share def _external_network(self): """ From c39ddc72c3695c35e17dc71ca5beecd819b93a7b Mon Sep 17 00:00:00 2001 From: John Garbutt Date: Wed, 24 Jan 2024 12:02:06 +0000 Subject: [PATCH 05/13] Rename shares to share --- api/azimuth/provider/openstack/api/__init__.py | 2 +- api/azimuth/provider/openstack/api/{shares.py => share.py} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename api/azimuth/provider/openstack/api/{shares.py => share.py} (100%) diff --git a/api/azimuth/provider/openstack/api/__init__.py b/api/azimuth/provider/openstack/api/__init__.py index 42d7151f..95b179e6 100644 --- a/api/azimuth/provider/openstack/api/__init__.py +++ b/api/azimuth/provider/openstack/api/__init__.py @@ -1,3 +1,3 @@ from .core import Connection, ServiceNotSupported # Import the modules for each of the services -from . import block_store, coe, compute, identity, image, network, orchestration, shares +from . import block_store, coe, compute, identity, image, network, orchestration, share diff --git a/api/azimuth/provider/openstack/api/shares.py b/api/azimuth/provider/openstack/api/share.py similarity index 100% rename from api/azimuth/provider/openstack/api/shares.py rename to api/azimuth/provider/openstack/api/share.py From 6246a5c736bba51be3461983928d4c11c5e1e485 Mon Sep 17 00:00:00 2001 From: John Garbutt Date: Wed, 24 Jan 2024 12:05:32 +0000 Subject: [PATCH 06/13] Fix up manila specific code --- api/azimuth/provider/openstack/api/core.py | 2 -- api/azimuth/provider/openstack/api/share.py | 4 +++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/api/azimuth/provider/openstack/api/core.py b/api/azimuth/provider/openstack/api/core.py index 4d7324d0..e71c142b 100644 --- a/api/azimuth/provider/openstack/api/core.py +++ b/api/azimuth/provider/openstack/api/core.py @@ -357,8 +357,6 @@ def prepare_request(self, request): # If a specific microversion is requested, add the appropriate header if self.microversion: request.headers["OpenStack-API-Version"] = f"{self.catalog_type} {self.microversion}" - if self.catalog_type == "sharev2": - request.headers["X-OpenStack-Manila-API-Version"] = f"{self.microversion}" return super().prepare_request(request) def extract_error_message(self, response): diff --git a/api/azimuth/provider/openstack/api/share.py b/api/azimuth/provider/openstack/api/share.py index 0e7ceaec..f684688b 100644 --- a/api/azimuth/provider/openstack/api/share.py +++ b/api/azimuth/provider/openstack/api/share.py @@ -77,10 +77,12 @@ class ShareService(Service): """ name = "share" catalog_type = "sharev2" - microversion = '2.51' path_prefix = '/v2/{project_id}' limits = Endpoint(ShareLimits) shares = RootResource(Share) types = RootResource(ShareType) access = RootResource(ShareAccess) + + def prepare_request(self, request): + request.headers["X-OpenStack-Manila-API-Version"] = "2.51" From be4d4634abca467f491c19f5288bf0c289e8c3a3 Mon Sep 17 00:00:00 2001 From: John Garbutt Date: Wed, 24 Jan 2024 12:06:55 +0000 Subject: [PATCH 07/13] Remove debug code --- api/azimuth/provider/openstack/provider.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/api/azimuth/provider/openstack/provider.py b/api/azimuth/provider/openstack/provider.py index 1bd2487b..a33ffabb 100644 --- a/api/azimuth/provider/openstack/provider.py +++ b/api/azimuth/provider/openstack/provider.py @@ -708,9 +708,6 @@ def _tenant_network(self, create_network = False): raise errors.InvalidOperationError("Could not find internal network.") def _project_share(self, create_share=True): - self._create_manila_project_share = True - self._project_share_name = "johng-dev" - if not self._create_manila_project_share: return From 7087084aad87bec9e7085d8a09e40f12dcefa968 Mon Sep 17 00:00:00 2001 From: John Garbutt Date: Wed, 24 Jan 2024 12:11:41 +0000 Subject: [PATCH 08/13] Make missing share type a slient error --- api/azimuth/provider/openstack/provider.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/api/azimuth/provider/openstack/provider.py b/api/azimuth/provider/openstack/provider.py index a33ffabb..f5bdd185 100644 --- a/api/azimuth/provider/openstack/provider.py +++ b/api/azimuth/provider/openstack/provider.py @@ -722,11 +722,11 @@ def _project_share(self, create_share=True): share_details = self._connection.share.shares.get(project_share.id) self._log(f"Got share details f{share_details}") if share_details.share_proto.upper() != "CEPHFS": - raise Exception("Currently only support CephFS shares!") + raise errors.ImproperlyConfiguredError("Currently only support CephFS shares!") if share_details.status.lower() != "available": - raise Exception("Share is not available!") + raise errors.Error("Share is not available!") if share_details.access_rules_status.lower() != "active": - raise Exception("Share has a problem with its access rules!") + raise errors.Error("Share has a problem with its access rules!") access_list = list(self._connection.share.access.all( share_id=project_share.id)) @@ -736,7 +736,7 @@ def _project_share(self, create_share=True): found_expected_access = True break if not found_expected_access: - raise Exception("can't find the expected access rule!") + raise errors.ImproperlyConfiguredError("can't find the expected access rule!") self._log(f"Found project share for: {self._connection.project_id}") # no share found, create if required @@ -754,7 +754,10 @@ def _project_share(self, create_share=True): default_share_type = share_type break if not default_share_type: - raise Exception("Unable to find valid share type!") + # Silent ignore here, as it usually means project + # has not been setup for manila + self._log("Unable to find valid share type!") + return # TODO(johngarbutt) need to support non-ceph types eventually project_share = self._connection.share.shares.create( @@ -769,6 +772,8 @@ def _project_share(self, create_share=True): latest = self._connection.share.shares.get(project_share.id) if latest.status.lower() == "available": break + if latest.status.lower() == "error": + raise error.Error("Unable to create project share.") time.sleep(0.1) project_share.grant_rw_access(self._project_share_user) From 6260954ab18dd63da64dcdef81bf70f4787f84e3 Mon Sep 17 00:00:00 2001 From: John Garbutt Date: Thu, 25 Jan 2024 09:48:15 +0000 Subject: [PATCH 09/13] Fixup from the review --- api/azimuth/provider/openstack/provider.py | 24 ++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/api/azimuth/provider/openstack/provider.py b/api/azimuth/provider/openstack/provider.py index f5bdd185..a5b8ea91 100644 --- a/api/azimuth/provider/openstack/provider.py +++ b/api/azimuth/provider/openstack/provider.py @@ -708,6 +708,23 @@ def _tenant_network(self, create_network = False): raise errors.InvalidOperationError("Could not find internal network.") def _project_share(self, create_share=True): + """ + Returns the project specific Manila share. + + If we are not configured to create the project share, + we do nothing here, and return None. + + If we are configured to create the project share, + we look to see if a valid share is already created. + If we find a valid share, we return that object. + + Finally, we look to create the share dynamically, + then return that share. + + If this project has not available share type in + Manila, we simply log that we can't create a share + for this project, and return None. + """ if not self._create_manila_project_share: return @@ -773,7 +790,7 @@ def _project_share(self, create_share=True): if latest.status.lower() == "available": break if latest.status.lower() == "error": - raise error.Error("Unable to create project share.") + raise errors.Error("Unable to create project share.") time.sleep(0.1) project_share.grant_rw_access(self._project_share_user) @@ -1483,12 +1500,15 @@ def cluster_parameters(self): cluster_network = self._tenant_network(True).name ) - # Optionally inject name of the project specific manila share + # If configured to, find if we can have a project share project_share = self._project_share(True) if project_share: + params["cluster_project_manila_share"] = True params["cluster_project_manila_share_name"] = project_share.name user = self._project_share_user params["cluster_project_manila_share_user"] = user + else: + params["cluster_project_manila_share"] = False return params From b23474ff050ff03ec7b8c75028f0f8f79152b2eb Mon Sep 17 00:00:00 2001 From: John Garbutt Date: Thu, 25 Jan 2024 09:55:28 +0000 Subject: [PATCH 10/13] Add comment about turning manila off --- api/azimuth/provider/openstack/provider.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/api/azimuth/provider/openstack/provider.py b/api/azimuth/provider/openstack/provider.py index a5b8ea91..7ebd90af 100644 --- a/api/azimuth/provider/openstack/provider.py +++ b/api/azimuth/provider/openstack/provider.py @@ -425,6 +425,9 @@ def __init__(self, username, self._backdoor_vnic_type = backdoor_vnic_type # TODO(johngarbutt): consider moving some of this to config + # and/or hopefully having this feature on by default + # and auto detecting when its available, which is not currently + # feasible. self._project_share_name = "azimuth-project-share" prefix = "proj" project_id_safe = self._connection.project_id.replace("-", "") From 5af5d4a4a35fed122af838b1890f57b292e81433 Mon Sep 17 00:00:00 2001 From: John Garbutt Date: Mon, 29 Jan 2024 14:26:44 +0000 Subject: [PATCH 11/13] Tweak exception class type --- api/azimuth/provider/openstack/provider.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/api/azimuth/provider/openstack/provider.py b/api/azimuth/provider/openstack/provider.py index 7ebd90af..ceb41cff 100644 --- a/api/azimuth/provider/openstack/provider.py +++ b/api/azimuth/provider/openstack/provider.py @@ -742,11 +742,14 @@ def _project_share(self, create_share=True): share_details = self._connection.share.shares.get(project_share.id) self._log(f"Got share details f{share_details}") if share_details.share_proto.upper() != "CEPHFS": - raise errors.ImproperlyConfiguredError("Currently only support CephFS shares!") + raise errors.ImproperlyConfiguredError( + "Currently only support CephFS shares!") if share_details.status.lower() != "available": - raise errors.Error("Share is not available!") + raise errors.ImproperlyConfiguredError( + "Project share is not available!") if share_details.access_rules_status.lower() != "active": - raise errors.Error("Share has a problem with its access rules!") + raise errors.ImproperlyConfiguredError( + "Project share has a problem with its access rules!") access_list = list(self._connection.share.access.all( share_id=project_share.id)) From cda13da82c9106798accd263cb885e297a6add06 Mon Sep 17 00:00:00 2001 From: John Garbutt Date: Tue, 30 Jan 2024 13:32:26 +0000 Subject: [PATCH 12/13] Update share.py to correctly call super --- api/azimuth/provider/openstack/api/share.py | 1 + 1 file changed, 1 insertion(+) diff --git a/api/azimuth/provider/openstack/api/share.py b/api/azimuth/provider/openstack/api/share.py index f684688b..b71351c1 100644 --- a/api/azimuth/provider/openstack/api/share.py +++ b/api/azimuth/provider/openstack/api/share.py @@ -86,3 +86,4 @@ class ShareService(Service): def prepare_request(self, request): request.headers["X-OpenStack-Manila-API-Version"] = "2.51" + return super().prepare_request(request) From 91182c26916a0e52410dc7148495b2bd8f642079 Mon Sep 17 00:00:00 2001 From: John Garbutt Date: Tue, 30 Jan 2024 18:12:47 +0000 Subject: [PATCH 13/13] Change helm config to manilaProjectShareGB --- api/azimuth/provider/openstack/provider.py | 26 ++++++++++--------- .../files/api/settings/04-cloud-provider.yaml | 2 +- chart/values.yaml | 4 +-- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/api/azimuth/provider/openstack/provider.py b/api/azimuth/provider/openstack/provider.py index 763cdf62..407f9a33 100644 --- a/api/azimuth/provider/openstack/provider.py +++ b/api/azimuth/provider/openstack/provider.py @@ -141,8 +141,8 @@ class Provider(base.Provider): fragment ``{tenant_name}``. create_internal_net: If ``True`` (the default), then the internal network is auto-created when a tagged network or templated network cannot be found. - create_manila_project_share: If ``True`` (the default is False), then - manila project share is auto created when cannot be found. + manila_project_share_gb: If >0 (the default is 0), then + manila project share is auto created with specified size. internal_net_cidr: The CIDR for the internal network when it is auto-created (default ``192.168.3.0/24``). internal_net_dns_nameservers: The DNS nameservers for the internal network when it is @@ -166,7 +166,7 @@ def __init__(self, auth_url, internal_net_template = None, external_net_template = None, create_internal_net = True, - create_manila_project_share = False, + manila_project_share_gb = 0, internal_net_cidr = "192.168.3.0/24", internal_net_dns_nameservers = None, az_backdoor_net_map = None, @@ -180,7 +180,9 @@ def __init__(self, auth_url, self._internal_net_template = internal_net_template self._external_net_template = external_net_template self._create_internal_net = create_internal_net - self._create_manila_project_share = create_manila_project_share + self._manila_project_share_gb = 0 + if manila_project_share_gb: + self._manila_project_share_gb = int(manila_project_share_gb) self._internal_net_cidr = internal_net_cidr self._internal_net_dns_nameservers = internal_net_dns_nameservers self._az_backdoor_net_map = az_backdoor_net_map or dict() @@ -207,7 +209,7 @@ def from_token(self, token): internal_net_template = self._internal_net_template, external_net_template = self._external_net_template, create_internal_net = self._create_internal_net, - create_manila_project_share = self._create_manila_project_share, + manila_project_share_gb = self._manila_project_share_gb, internal_net_cidr = self._internal_net_cidr, internal_net_dns_nameservers = self._internal_net_dns_nameservers, az_backdoor_net_map = self._az_backdoor_net_map, @@ -226,7 +228,7 @@ def __init__(self, connection, internal_net_template = None, external_net_template = None, create_internal_net = True, - create_manila_project_share = False, + manila_project_share_gb = 0, internal_net_cidr = "192.168.3.0/24", internal_net_dns_nameservers = None, az_backdoor_net_map = None, @@ -236,7 +238,7 @@ def __init__(self, connection, self._internal_net_template = internal_net_template self._external_net_template = external_net_template self._create_internal_net = create_internal_net - self._create_manila_project_share = create_manila_project_share + self._manila_project_share_gb = manila_project_share_gb self._internal_net_cidr = internal_net_cidr self._internal_net_dns_nameservers = internal_net_dns_nameservers self._az_backdoor_net_map = az_backdoor_net_map or dict() @@ -374,7 +376,7 @@ def scoped_session(self, tenancy): internal_net_template = self._internal_net_template, external_net_template = self._external_net_template, create_internal_net = self._create_internal_net, - create_manila_project_share = self._create_manila_project_share, + manila_project_share_gb = self._manila_project_share_gb, internal_net_cidr = self._internal_net_cidr, internal_net_dns_nameservers = self._internal_net_dns_nameservers, az_backdoor_net_map = self._az_backdoor_net_map, @@ -406,7 +408,7 @@ def __init__(self, username, internal_net_template = None, external_net_template = None, create_internal_net = True, - create_manila_project_share = False, + manila_project_share_gb = 0, internal_net_cidr = "192.168.3.0/24", internal_net_dns_nameservers = None, az_backdoor_net_map = None, @@ -418,7 +420,7 @@ def __init__(self, username, self._internal_net_template = internal_net_template self._external_net_template = external_net_template self._create_internal_net = create_internal_net - self._create_manila_project_share = create_manila_project_share + self._manila_project_share_gb = manila_project_share_gb self._internal_net_cidr = internal_net_cidr self._internal_net_dns_nameservers = internal_net_dns_nameservers self._az_backdoor_net_map = az_backdoor_net_map or dict() @@ -734,7 +736,7 @@ def _project_share(self, create_share=True): Manila, we simply log that we can't create a share for this project, and return None. """ - if not self._create_manila_project_share: + if not self._manila_project_share_gb: return # find if project share exists @@ -791,7 +793,7 @@ def _project_share(self, create_share=True): # TODO(johngarbutt) need to support non-ceph types eventually project_share = self._connection.share.shares.create( share_proto="CephFS", - size=10, + size=self._manila_project_share_gb, name=self._project_share_name, description="Project share auto-created by Azimuth.", share_type=default_share_type.id) diff --git a/chart/files/api/settings/04-cloud-provider.yaml b/chart/files/api/settings/04-cloud-provider.yaml index 4985f6e0..80ecd659 100644 --- a/chart/files/api/settings/04-cloud-provider.yaml +++ b/chart/files/api/settings/04-cloud-provider.yaml @@ -16,7 +16,7 @@ AZIMUTH: EXTERNAL_NET_TEMPLATE: {{ . | quote }} {{- end }} CREATE_INTERNAL_NET: {{ .Values.provider.openstack.createInternalNet }} - CREATE_MANILA_PROJECT_SHARE: {{ .Values.provider.openstack.createManilaProjectShare }} + MANILA_PROJECT_SHARE_GB: {{ .Values.provider.openstack.manilaProjectShareGB }} {{- with .Values.provider.openstack.internalNetCidr }} INTERNAL_NET_CIDR: {{ . | quote }} {{- end }} diff --git a/chart/values.yaml b/chart/values.yaml index 1bf7e7f8..22b52064 100644 --- a/chart/values.yaml +++ b/chart/values.yaml @@ -267,8 +267,8 @@ provider: externalNetTemplate: # Indicates whether tenant internal networks should be auto-created if not present createInternalNet: true - # Indicates whether project specific manila share should be auto-created if not present - createManilaProjectShare: false + # If larger than zero, project specific manila share should be auto-created + manilaProjectShareGB: 0 # The CIDR to use for auto-created tenant internal networks # Defaults to 192.168.3.0/24 if not given, which should be OK for most circumstances internalNetCidr: