Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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: 1 addition & 1 deletion doc/source/sdk/guides/sfsturbo.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ This interface is used to create a share (scalable file system).
:class:`~otcextensions.sdk.sfsturbo.v1.share.Share`.

.. literalinclude:: ../examples/sfsturbo/create_share.py
:lines: 16-36
:lines: 16-43

Delete Share
^^^^^^^^^^^^
Expand Down
7 changes: 7 additions & 0 deletions examples/sfsturbo/create_share.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,18 @@
"name": "test_share_1",
"share_proto": "NFS",
"share_type": "STANDARD",
# For an HPC file system, size must be a multiple of 1.2 TiB (e.g. 3686)
# and hpc_bw one of "20M", "40M", "125M", "250M", "500M", "1000M".
"size": 100,
"availability_zone": "eu-de-01",
"vpc_id": "vpc_uuid",
"subnet_id": "subnet_uuid",
"security_group_id": "security_group_uuid",
# Optional metadata. crypt_key_id creates an encrypted file system. To
# deploy an HPC file system set expand_type to "hpc" and provide hpc_bw
# (size must then be a multiple of 1.2 TiB, e.g. 3686); use "bandwidth"
# for an enhanced file system.
"metadata": {"crypt_key_id": "kms_key_uuid"},
}

share = conn.sfsturbo.create_share(**attrs)
Expand Down
9 changes: 7 additions & 2 deletions otcextensions/sdk/cce/v3/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,11 +141,16 @@ def create(self, session, prepend_key=False, base_path=None):
# Overriden here to override prepend_key default value
return super(Resource, self).create(session, prepend_key, base_path)

def delete(self, session, error_message=None):
def delete(self, session, error_message=None, **params):
"""Delete the remote resource based on this instance.

:param session: The session to use for making this request.
:type session: :class:`~keystoneauth1.adapter.Adapter`
:param dict params: Additional arguments (e.g. ``params``,
``microversion``, ``base_path``) as passed down by the calling
:class:`~openstack.proxy.Proxy`. Their exact set differs between
openstacksdk releases, so they are accepted generically and
forwarded as-is to ``_raw_delete``.

:return: This :class:`Resource` instance.
:raises: :exc:`~openstack.exceptions.MethodNotSupported` if
Expand All @@ -154,7 +159,7 @@ def delete(self, session, error_message=None):
the resource was not found.
"""

response = self._raw_delete(session)
response = self._raw_delete(session, **params)
kwargs = {}
if error_message:
kwargs["error_message"] = error_message
Expand Down
14 changes: 12 additions & 2 deletions otcextensions/sdk/rds/v3/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,19 @@ def find(cls, session, name_or_id, ignore_missing=True, **params):
"No %s found for %s" % (cls.__name__, name_or_id)
)

def delete(self, session, error_message=None):
def delete(self, session, error_message=None, **params):
"""Delete the remote resource based on this instance.

response = self._raw_delete(session)
:param session: The session to use for making this request.
:type session: :class:`~keystoneauth1.adapter.Adapter`
:param dict params: Additional arguments (e.g. ``params``,
``microversion``, ``base_path``) as passed down by the calling
:class:`~openstack.proxy.Proxy`. Their exact set differs between
openstacksdk releases, so they are accepted generically and
forwarded as-is to ``_raw_delete``.
"""

response = self._raw_delete(session, **params)
kwargs = {}
if error_message:
kwargs["error_message"] = error_message
Expand Down
22 changes: 22 additions & 0 deletions otcextensions/sdk/sfsturbo/v1/share.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,24 @@
LOG = _log.setup_logging(__name__)


class Metadata(resource.Resource):

# Properties
#: The extension type. ``bandwidth`` creates an enhanced file system;
#: ``hpc`` creates an HPC file system (in which case ``hpc_bw`` is
#: mandatory).
#: *Type: str*
expand_type = resource.Body("expand_type")
#: The HPC file system bandwidth, e.g. ``250M``. Mandatory when
#: ``expand_type`` is ``hpc``.
#: *Type: str*
hpc_bw = resource.Body("hpc_bw")
#: The ID of a KMS professional key, used to create an encrypted file
#: system.
#: *Type: str*
crypt_key_id = resource.Body("crypt_key_id")


class Share(resource.Resource):

base_path = "/sfs-turbo/shares"
Expand Down Expand Up @@ -52,6 +70,10 @@ class Share(resource.Resource):
#: For an enhanced file system, bandwidth is returned for this field.
#: *Type: str*
expand_type = resource.Body("expand_type")
#: Specifies the metadata used to create the file system, carrying
#: ``expand_type``, ``hpc_bw`` and ``crypt_key_id``. Sent only on creation.
#: *Type: :class:`Metadata`*
metadata = resource.Body("metadata", type=Metadata)
#: Specifies the mount point of the SFS Turbo file system.
#: *Type: str*
export_location = resource.Body("export_location")
Expand Down
24 changes: 22 additions & 2 deletions otcextensions/tests/functional/privatenat.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,33 @@
# License for the specific language governing permissions and limitations
# under the License.

import time
import uuid

from openstack import exceptions as sdk_exceptions
from openstack import resource


class PrivateNatEnvironmentMixin(object):
def _wait_for_subnet_active(self, subnet, timeout=60, retries=3):
"""Wait for a VPC subnet to become ACTIVE.

The OTC VPC subnet API is occasionally read-after-write
inconsistent: a GET right after creation can 404 even though a
previous GET already returned the resource. Retry the wait a
few times before giving up so such transient 404s don't fail
the test.
"""
for attempt in range(retries):
try:
return resource.wait_for_status(
self.conn.vpc, subnet, "ACTIVE", None, 2, timeout
)
except sdk_exceptions.ResourceNotFound:
if attempt == retries - 1:
raise
time.sleep(2)

def _delete_private_transit_ip(self, transit_ip_id):
self.conn.natv3.delete_private_transit_ip(transit_ip_id, ignore_missing=True)

Expand All @@ -43,7 +63,7 @@ def _create_private_nat_subnet(self, vpc, prefix, suffix):
gateway_ip=gw[:-2] + ".1",
dns_list=["100.125.4.25", "100.125.129.199"],
)
resource.wait_for_status(self.conn.vpc, subnet, "ACTIVE", None, 2, 60)
self._wait_for_subnet_active(subnet)
return subnet

def _delete_private_nat_subnet(self, subnet):
Expand All @@ -52,7 +72,7 @@ def _delete_private_nat_subnet(self, subnet):
except sdk_exceptions.ResourceNotFound:
return

resource.wait_for_status(self.conn.vpc, subnet, "ACTIVE", None, 2, 60)
self._wait_for_subnet_active(subnet)
self.conn.vpc.delete_subnet(subnet, ignore_missing=True)
resource.wait_for_delete(self.conn.vpc, subnet, 2, 120)

Expand Down
19 changes: 19 additions & 0 deletions otcextensions/tests/functional/sdk/swr/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import time

from openstack import exceptions as sdk_exceptions
from otcextensions.tests.functional import base


Expand All @@ -17,3 +20,19 @@ class TestSwr(base.BaseFunctionalTest):
def setUp(self):
super(TestSwr, self).setUp()
self.client = self.conn.swr

def _create_organization(self, namespace, retries=3, delay=5):
"""Create an SWR organization, retrying on transient server errors.

The SWR "create namespace" API occasionally returns a transient
5xx (e.g. SVCSTG.SWR.5000059) under load; retry a few times
before giving up.
"""
for attempt in range(retries):
try:
return self.client.create_organization(namespace=namespace)
except sdk_exceptions.HttpException as e:
status = e.response.status_code if e.response is not None else None
if not status or status < 500 or attempt == retries - 1:
raise
time.sleep(delay)
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def setUp(self):
super(TestOrganization, self).setUp()

self.org_name = "sdk-swr-org-" + uuid.uuid4().hex
self.org = self.client.create_organization(namespace=self.org_name)
self.org = self._create_organization(self.org_name)
if os.getenv("OS_SWR_PERMISSIONS_RUN"):
self.permission = [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def setUp(self):

self.org_name = "sdk-swr-org-" + uuid.uuid4().hex
self.repo_name = "sdk-swr-repo-" + uuid.uuid4().hex
self.org = self.client.create_organization(namespace=self.org_name)
self.org = self._create_organization(self.org_name)
self.repo = self.client.create_repository(
namespace=self.org_name,
repository=self.repo_name,
Expand Down
37 changes: 37 additions & 0 deletions otcextensions/tests/unit/sdk/sfsturbo/v1/test_share.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,21 @@
"vpc_id": "vpc_id",
}

METADATA_EXAMPLE = {
"expand_type": "hpc",
"hpc_bw": "250M",
"crypt_key_id": "crypt_key_id",
}


class TestMetadata(base.TestCase):

def test_make_it(self):
sot = _share.Metadata(**METADATA_EXAMPLE)
self.assertEqual(METADATA_EXAMPLE["expand_type"], sot.expand_type)
self.assertEqual(METADATA_EXAMPLE["hpc_bw"], sot.hpc_bw)
self.assertEqual(METADATA_EXAMPLE["crypt_key_id"], sot.crypt_key_id)


class TestShare(base.TestCase):

Expand Down Expand Up @@ -114,3 +129,25 @@ def test_change_security_group(self):
"sfs-turbo/shares/%s/action" % EXAMPLE["id"],
json={"change_security_group": {"security_group_id": "secgroup-uuid"}},
)

def test_create_body_includes_metadata(self):
metadata = {
"expand_type": "hpc",
"hpc_bw": "250M",
"crypt_key_id": "kms-key-uuid",
}
sot = _share.Share(
name="hpc-test",
share_proto="NFS",
share_type="PERFORMANCE",
size=3686,
availability_zone="eu-de-01",
vpc_id="vpc-uuid",
subnet_id="subnet-uuid",
security_group_id="secgroup-uuid",
metadata=metadata,
)

request = sot._prepare_request(requires_id=False, prepend_key=True)

self.assertEqual(metadata, request.body["share"]["metadata"])