Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auto create project manila cephfs shares #148

Merged
merged 21 commits into from
Jan 31, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
7bddcb9
Initial plumbing for create_manila_project_share
JohnGarbutt Jan 18, 2024
1d29c80
Adding initial calls to Manila
JohnGarbutt Jan 22, 2024
af16d7e
Remove bits of debugging code
JohnGarbutt Jan 23, 2024
61ec5a8
Some tidy ups
JohnGarbutt Jan 23, 2024
e72772c
Merge branch 'master' into feature/create-manila-project-share
JohnGarbutt Jan 23, 2024
1223917
Merge remote-tracking branch 'origin/master' into feature/create-mani…
JohnGarbutt Jan 24, 2024
c39ddc7
Rename shares to share
JohnGarbutt Jan 24, 2024
6246a5c
Fix up manila specific code
JohnGarbutt Jan 24, 2024
be4d463
Remove debug code
JohnGarbutt Jan 24, 2024
7087084
Make missing share type a slient error
JohnGarbutt Jan 24, 2024
ac80424
Merge remote-tracking branch 'origin/master' into feature/create-mani…
JohnGarbutt Jan 25, 2024
6260954
Fixup from the review
JohnGarbutt Jan 25, 2024
b23474f
Add comment about turning manila off
JohnGarbutt Jan 25, 2024
5bfe04e
Merge branch 'master' into feature/create-manila-project-share
JohnGarbutt Jan 29, 2024
5af5d4a
Tweak exception class type
JohnGarbutt Jan 29, 2024
e9dc839
Merge branch 'master' into feature/create-manila-project-share
JohnGarbutt Jan 29, 2024
0683224
Merge branch 'master' into feature/create-manila-project-share
mkjpryor Jan 29, 2024
cda13da
Update share.py to correctly call super
JohnGarbutt Jan 30, 2024
b7db69f
Merge branch 'master' into feature/create-manila-project-share
JohnGarbutt Jan 30, 2024
fd673af
Merge branch 'master' into feature/create-manila-project-share
JohnGarbutt Jan 30, 2024
91182c2
Change helm config to manilaProjectShareGB
JohnGarbutt Jan 30, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions api/azimuth/cluster_api/openstack.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
2 changes: 1 addition & 1 deletion api/azimuth/provider/openstack/api/__init__.py
Original file line number Diff line number Diff line change
@@ -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, share
88 changes: 88 additions & 0 deletions api/azimuth/provider/openstack/api/share.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
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"
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"
mkjpryor marked this conversation as resolved.
Show resolved Hide resolved
129 changes: 128 additions & 1 deletion api/azimuth/provider/openstack/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import logging
import random
import re
import time

import dateutil.parser

Expand Down Expand Up @@ -140,6 +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.
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
Expand All @@ -163,6 +166,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,
Expand All @@ -176,6 +180,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()
Expand All @@ -202,6 +207,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,
Expand All @@ -220,6 +226,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,
Expand All @@ -229,6 +236,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()
Expand Down Expand Up @@ -366,6 +374,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,
Expand Down Expand Up @@ -397,6 +406,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,
Expand All @@ -408,11 +418,21 @@ 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
# 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"
mkjpryor marked this conversation as resolved.
Show resolved Hide resolved
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,
Expand Down Expand Up @@ -690,6 +710,101 @@ def _tenant_network(self, create_network = False):
else:
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

# 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:
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 errors.ImproperlyConfiguredError(
"Currently only support CephFS shares!")
if share_details.status.lower() != "available":
raise errors.ImproperlyConfiguredError(
"Project share is not available!")
if share_details.access_rules_status.lower() != "active":
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))
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 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
if not project_share and create_share:
self._log(f"Creating project share for: {self._connection.project_id}")

# Find share type
default_share_type = None
JohnGarbutt marked this conversation as resolved.
Show resolved Hide resolved
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:
# 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(
share_proto="CephFS",
size=10,
JohnGarbutt marked this conversation as resolved.
Show resolved Hide resolved
name=self._project_share_name,
description="Project share auto-created by Azimuth.",
share_type=default_share_type.id)

# 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 latest.status.lower() == "error":
raise errors.Error("Unable to create project share.")
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):
"""
Returns the external network that connects the tenant router to the outside world.
Expand Down Expand Up @@ -1383,14 +1498,26 @@ 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
cluster_external_network = external_network,
cluster_network = self._tenant_network(True).name
)

# 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

def cluster_modify(self, cluster):
"""
See :py:meth:`.base.ScopedSession.cluster_modify`.
Expand Down
1 change: 1 addition & 0 deletions chart/files/api/settings/04-cloud-provider.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}
Expand Down
2 changes: 2 additions & 0 deletions chart/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Loading