Browse Source

Merge "Remove in-tree tempest plugin"

changes/26/522126/12
Zuul 4 years ago
committed by Gerrit Code Review
parent
commit
11bf61f5ba
  1. 19
      .zuul.yaml
  2. 4
      contrib/ci/post_test_hook.sh
  3. 7
      devstack/settings
  4. 6
      manila_tempest_tests/README.rst
  5. 0
      manila_tempest_tests/__init__.py
  6. 32
      manila_tempest_tests/clients.py
  7. 0
      manila_tempest_tests/common/__init__.py
  8. 85
      manila_tempest_tests/common/constants.py
  9. 92
      manila_tempest_tests/common/remote_client.py
  10. 247
      manila_tempest_tests/config.py
  11. 75
      manila_tempest_tests/plugin.py
  12. 0
      manila_tempest_tests/services/__init__.py
  13. 18
      manila_tempest_tests/services/share/__init__.py
  14. 17
      manila_tempest_tests/services/share/json/__init__.py
  15. 760
      manila_tempest_tests/services/share/json/shares_client.py
  16. 18
      manila_tempest_tests/services/share/v2/__init__.py
  17. 0
      manila_tempest_tests/services/share/v2/json/__init__.py
  18. 1735
      manila_tempest_tests/services/share/v2/json/shares_client.py
  19. 77
      manila_tempest_tests/share_exceptions.py
  20. 0
      manila_tempest_tests/tests/__init__.py
  21. 0
      manila_tempest_tests/tests/api/__init__.py
  22. 0
      manila_tempest_tests/tests/api/admin/__init__.py
  23. 128
      manila_tempest_tests/tests/api/admin/test_admin_actions.py
  24. 210
      manila_tempest_tests/tests/api/admin/test_admin_actions_negative.py
  25. 201
      manila_tempest_tests/tests/api/admin/test_export_locations.py
  26. 97
      manila_tempest_tests/tests/api/admin/test_export_locations_negative.py
  27. 634
      manila_tempest_tests/tests/api/admin/test_migration.py
  28. 326
      manila_tempest_tests/tests/api/admin/test_migration_negative.py
  29. 91
      manila_tempest_tests/tests/api/admin/test_multi_backend.py
  30. 759
      manila_tempest_tests/tests/api/admin/test_quotas.py
  31. 357
      manila_tempest_tests/tests/api/admin/test_quotas_negative.py
  32. 183
      manila_tempest_tests/tests/api/admin/test_replication.py
  33. 179
      manila_tempest_tests/tests/api/admin/test_replication_actions.py
  34. 204
      manila_tempest_tests/tests/api/admin/test_scheduler_stats.py
  35. 64
      manila_tempest_tests/tests/api/admin/test_security_services.py
  36. 105
      manila_tempest_tests/tests/api/admin/test_services.py
  37. 100
      manila_tempest_tests/tests/api/admin/test_services_negative.py
  38. 245
      manila_tempest_tests/tests/api/admin/test_share_group_types.py
  39. 146
      manila_tempest_tests/tests/api/admin/test_share_group_types_negative.py
  40. 191
      manila_tempest_tests/tests/api/admin/test_share_groups.py
  41. 56
      manila_tempest_tests/tests/api/admin/test_share_groups_negative.py
  42. 114
      manila_tempest_tests/tests/api/admin/test_share_instances.py
  43. 54
      manila_tempest_tests/tests/api/admin/test_share_instances_negative.py
  44. 252
      manila_tempest_tests/tests/api/admin/test_share_manage.py
  45. 95
      manila_tempest_tests/tests/api/admin/test_share_networks.py
  46. 258
      manila_tempest_tests/tests/api/admin/test_share_servers.py
  47. 108
      manila_tempest_tests/tests/api/admin/test_share_servers_negative.py
  48. 121
      manila_tempest_tests/tests/api/admin/test_share_snapshot_instances.py
  49. 88
      manila_tempest_tests/tests/api/admin/test_share_snapshot_instances_negative.py
  50. 207
      manila_tempest_tests/tests/api/admin/test_share_types.py
  51. 132
      manila_tempest_tests/tests/api/admin/test_share_types_extra_specs.py
  52. 311
      manila_tempest_tests/tests/api/admin/test_share_types_extra_specs_negative.py
  53. 116
      manila_tempest_tests/tests/api/admin/test_share_types_negative.py
  54. 405
      manila_tempest_tests/tests/api/admin/test_shares_actions.py
  55. 140
      manila_tempest_tests/tests/api/admin/test_snapshot_export_locations.py
  56. 140
      manila_tempest_tests/tests/api/admin/test_snapshot_export_locations_negative.py
  57. 170
      manila_tempest_tests/tests/api/admin/test_snapshot_manage.py
  58. 115
      manila_tempest_tests/tests/api/admin/test_snapshot_manage_negative.py
  59. 108
      manila_tempest_tests/tests/api/admin/test_user_messages.py
  60. 58
      manila_tempest_tests/tests/api/admin/test_user_messages_negative.py
  61. 1077
      manila_tempest_tests/tests/api/base.py
  62. 51
      manila_tempest_tests/tests/api/test_availability_zones.py
  63. 43
      manila_tempest_tests/tests/api/test_availability_zones_negative.py
  64. 31
      manila_tempest_tests/tests/api/test_extensions.py
  65. 64
      manila_tempest_tests/tests/api/test_limits.py
  66. 163
      manila_tempest_tests/tests/api/test_metadata.py
  67. 100
      manila_tempest_tests/tests/api/test_metadata_negative.py
  68. 172
      manila_tempest_tests/tests/api/test_microversions.py
  69. 88
      manila_tempest_tests/tests/api/test_quotas.py
  70. 60
      manila_tempest_tests/tests/api/test_quotas_negative.py
  71. 406
      manila_tempest_tests/tests/api/test_replication.py
  72. 215
      manila_tempest_tests/tests/api/test_replication_negative.py
  73. 218
      manila_tempest_tests/tests/api/test_replication_snapshots.py
  74. 161
      manila_tempest_tests/tests/api/test_revert_to_snapshot.py
  75. 159
      manila_tempest_tests/tests/api/test_revert_to_snapshot_negative.py
  76. 631
      manila_tempest_tests/tests/api/test_rules.py
  77. 406
      manila_tempest_tests/tests/api/test_rules_negative.py
  78. 33
      manila_tempest_tests/tests/api/test_scheduler_stats_negative.py
  79. 210
      manila_tempest_tests/tests/api/test_security_services.py
  80. 70
      manila_tempest_tests/tests/api/test_security_services_mapping.py
  81. 173
      manila_tempest_tests/tests/api/test_security_services_mapping_negative.py
  82. 122
      manila_tempest_tests/tests/api/test_security_services_negative.py
  83. 401
      manila_tempest_tests/tests/api/test_share_group_actions.py
  84. 222
      manila_tempest_tests/tests/api/test_share_groups.py
  85. 273
      manila_tempest_tests/tests/api/test_share_groups_negative.py
  86. 298
      manila_tempest_tests/tests/api/test_share_networks.py
  87. 134
      manila_tempest_tests/tests/api/test_share_networks_negative.py
  88. 64
      manila_tempest_tests/tests/api/test_share_types_negative.py
  89. 236
      manila_tempest_tests/tests/api/test_shares.py
  90. 723
      manila_tempest_tests/tests/api/test_shares_actions.py
  91. 245
      manila_tempest_tests/tests/api/test_shares_actions_negative.py
  92. 283
      manila_tempest_tests/tests/api/test_shares_negative.py
  93. 101
      manila_tempest_tests/tests/api/test_snapshot_rules.py
  94. 90
      manila_tempest_tests/tests/api/test_snapshot_rules_negative.py
  95. 0
      manila_tempest_tests/tests/scenario/__init__.py
  96. 1241
      manila_tempest_tests/tests/scenario/manager.py
  97. 253
      manila_tempest_tests/tests/scenario/manager_share.py
  98. 594
      manila_tempest_tests/tests/scenario/test_share_basic_ops.py
  99. 168
      manila_tempest_tests/utils.py
  100. 1
      playbooks/legacy/manila-tempest-dsvm-container-scenario-custom-image/run.yaml

19
.zuul.yaml

@ -93,6 +93,7 @@
- openstack-infra/devstack-gate
- openstack/manila
- openstack/manila-image-elements
- openstack/manila-tempest-plugin
- openstack/python-manilaclient
- openstack/tempest
@ -106,6 +107,7 @@
- openstack-infra/devstack-gate
- openstack/manila
- openstack/manila-image-elements
- openstack/manila-tempest-plugin
- openstack/python-manilaclient
- openstack/tempest
@ -119,6 +121,7 @@
- openstack-infra/devstack-gate
- openstack/manila
- openstack/manila-image-elements
- openstack/manila-tempest-plugin
- openstack/python-manilaclient
- openstack/tempest
@ -132,6 +135,7 @@
- openstack-infra/devstack-gate
- openstack/devstack-plugin-glusterfs
- openstack/manila
- openstack/manila-tempest-plugin
- openstack/python-manilaclient
- openstack/tempest
@ -145,6 +149,7 @@
- openstack-infra/devstack-gate
- openstack/devstack-plugin-glusterfs
- openstack/manila
- openstack/manila-tempest-plugin
- openstack/python-manilaclient
- openstack/tempest
@ -158,6 +163,7 @@
- openstack-infra/devstack-gate
- openstack/devstack-plugin-glusterfs
- openstack/manila
- openstack/manila-tempest-plugin
- openstack/python-manilaclient
- openstack/tempest
@ -171,6 +177,7 @@
- openstack-infra/devstack-gate
- openstack/devstack-plugin-glusterfs
- openstack/manila
- openstack/manila-tempest-plugin
- openstack/python-manilaclient
- openstack/tempest
@ -184,6 +191,7 @@
- openstack-infra/devstack-gate
- openstack/devstack-plugin-hdfs
- openstack/manila
- openstack/manila-tempest-plugin
- openstack/python-manilaclient
- openstack/tempest
@ -197,6 +205,7 @@
- openstack-infra/devstack-gate
- openstack/manila
- openstack/manila-image-elements
- openstack/manila-tempest-plugin
- openstack/python-manilaclient
- openstack/tempest
@ -210,6 +219,7 @@
- openstack-infra/devstack-gate
- openstack/manila
- openstack/manila-image-elements
- openstack/manila-tempest-plugin
- openstack/python-manilaclient
- openstack/tempest
@ -223,6 +233,7 @@
- openstack-infra/devstack-gate
- openstack/manila
- openstack/manila-image-elements
- openstack/manila-tempest-plugin
- openstack/python-manilaclient
- openstack/tempest
@ -236,6 +247,7 @@
- openstack-infra/devstack-gate
- openstack/manila
- openstack/manila-image-elements
- openstack/manila-tempest-plugin
- openstack/python-manilaclient
- openstack/tempest
@ -249,6 +261,7 @@
- openstack-infra/devstack-gate
- openstack/manila
- openstack/manila-image-elements
- openstack/manila-tempest-plugin
- openstack/python-manilaclient
- openstack/tempest
@ -263,6 +276,7 @@
- openstack-infra/devstack-gate
- openstack/devstack-plugin-ceph
- openstack/manila
- openstack/manila-tempest-plugin
- openstack/python-manilaclient
- openstack/tempest
@ -277,6 +291,7 @@
- openstack-infra/devstack-gate
- openstack/devstack-plugin-ceph
- openstack/manila
- openstack/manila-tempest-plugin
- openstack/python-manilaclient
- openstack/tempest
@ -289,6 +304,7 @@
required-projects:
- openstack-infra/devstack-gate
- openstack/manila
- openstack/manila-tempest-plugin
- openstack/python-manilaclient
- openstack/tempest
@ -302,6 +318,7 @@
required-projects:
- openstack-infra/devstack-gate
- openstack/manila
- openstack/manila-tempest-plugin
- openstack/python-manilaclient
- openstack/tempest
@ -316,6 +333,7 @@
- openstack-infra/devstack-gate
- openstack/devstack-plugin-ceph
- openstack/manila
- openstack/manila-tempest-plugin
- openstack/python-manilaclient
- openstack/tempest
@ -330,6 +348,7 @@
- openstack-infra/devstack-gate
- openstack/devstack-plugin-ceph
- openstack/manila
- openstack/manila-tempest-plugin
- openstack/python-manilaclient
- openstack/tempest

4
contrib/ci/post_test_hook.sh

@ -329,7 +329,7 @@ for port in ${UDP_PORTS[*]}; do
done
echo "Running tempest manila test suites"
sudo -H -u $USER tox -eall-plugin $MANILA_TESTS -- --concurrency=$MANILA_TEMPEST_CONCURRENCY
sudo -H -u $USER tox -eall -- $MANILA_TESTS --concurrency=$MANILA_TEMPEST_CONCURRENCY
RETVAL=$?
@ -351,7 +351,7 @@ if [[ "$DRIVER" == "dummy" ]]; then
manila type-key default set driver_handles_share_servers=False
echo "Running tempest manila test suites for DHSS=False mode"
sudo -H -u $USER tox -eall-plugin $MANILA_TESTS -- --concurrency=$MANILA_TEMPEST_CONCURRENCY
sudo -H -u $USER tox -eall -- $MANILA_TESTS --concurrency=$MANILA_TEMPEST_CONCURRENCY
RETVAL2=$?
save_tempest_results 2

7
devstack/settings

@ -192,6 +192,13 @@ MANILA_DATA_NODE_IP=${MANILA_DATA_NODE_IP:=$MANILA_ADMIN_NET_RANGE}
# Data Service copy validation
MANILA_DATA_COPY_CHECK_HASH=${MANILA_DATA_COPY_CHECK_HASH:=True}
MANILA_TEMPEST_PLUGIN_PATH=$DEST/manila-tempest-plugin
if [[ $TEMPEST_PLUGINS != 0 ]] ; then
TEMPEST_PLUGINS="$MANILA_TEMPEST_PLUGIN_PATH $TEMPEST_PLUGINS"
else
TEMPEST_PLUGINS=$MANILA_TEMPEST_PLUGIN_PATH
fi
# Enable manila services
# ----------------------
# We have to add Manila to enabled services for screen_it to work

6
manila_tempest_tests/README.rst

@ -1,6 +0,0 @@
====================
Tempest Integration
====================
This directory contains Tempest tests to cover Manila project.

0
manila_tempest_tests/__init__.py

32
manila_tempest_tests/clients.py

@ -1,32 +0,0 @@
# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
from tempest import config
from tempest.lib.services import clients
CONF = config.CONF
class Clients(clients.ServiceClients):
"""Tempest stable service clients and loaded plugins service clients"""
def __init__(self, credentials, service=None):
"""Emulate the interface of Tempest's clients.Manager"""
# Identity settings
if CONF.identity.auth_version == 'v2':
identity_uri = CONF.identity.uri
else:
identity_uri = CONF.identity.uri_v3
super(Clients, self).__init__(credentials, identity_uri)

0
manila_tempest_tests/common/__init__.py

85
manila_tempest_tests/common/constants.py

@ -1,85 +0,0 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# Shares
STATUS_ERROR = 'error'
STATUS_AVAILABLE = 'available'
STATUS_ERROR_DELETING = 'error_deleting'
STATUS_MIGRATING = 'migrating'
TEMPEST_MANILA_PREFIX = 'tempest-manila'
# Replication
REPLICATION_STYLE_READABLE = 'readable'
REPLICATION_STYLE_WRITABLE = 'writable'
REPLICATION_STYLE_DR = 'dr'
REPLICATION_TYPE_CHOICES = (
REPLICATION_STYLE_READABLE,
REPLICATION_STYLE_WRITABLE,
REPLICATION_STYLE_DR,
)
REPLICATION_PROMOTION_CHOICES = (
REPLICATION_STYLE_READABLE,
REPLICATION_STYLE_DR,
)
REPLICATION_STATE_ACTIVE = 'active'
REPLICATION_STATE_IN_SYNC = 'in_sync'
REPLICATION_STATE_OUT_OF_SYNC = 'out_of_sync'
# Access Rules
RULE_STATE_ACTIVE = 'active'
RULE_STATE_OUT_OF_SYNC = 'out_of_sync'
RULE_STATE_ERROR = 'error'
TASK_STATE_MIGRATION_STARTING = 'migration_starting'
TASK_STATE_MIGRATION_IN_PROGRESS = 'migration_in_progress'
TASK_STATE_MIGRATION_COMPLETING = 'migration_completing'
TASK_STATE_MIGRATION_SUCCESS = 'migration_success'
TASK_STATE_MIGRATION_ERROR = 'migration_error'
TASK_STATE_MIGRATION_CANCELLED = 'migration_cancelled'
TASK_STATE_MIGRATION_DRIVER_STARTING = 'migration_driver_starting'
TASK_STATE_MIGRATION_DRIVER_IN_PROGRESS = 'migration_driver_in_progress'
TASK_STATE_MIGRATION_DRIVER_PHASE1_DONE = 'migration_driver_phase1_done'
TASK_STATE_DATA_COPYING_STARTING = 'data_copying_starting'
TASK_STATE_DATA_COPYING_IN_PROGRESS = 'data_copying_in_progress'
TASK_STATE_DATA_COPYING_COMPLETING = 'data_copying_completing'
TASK_STATE_DATA_COPYING_COMPLETED = 'data_copying_completed'
TASK_STATE_DATA_COPYING_CANCELLED = 'data_copying_cancelled'
TASK_STATE_DATA_COPYING_ERROR = 'data_copying_error'
# Revert to snapshot
REVERT_TO_SNAPSHOT_MICROVERSION = '2.27'
REVERT_TO_SNAPSHOT_SUPPORT = 'revert_to_snapshot_support'
STATUS_RESTORING = 'restoring'
STATUS_REVERTING = 'reverting'
STATUS_REVERTING_ERROR = 'reverting_error'
# Share groups
MIN_SHARE_GROUP_MICROVERSION = '2.31'
SHARE_GROUP_SIMPLE_KEYS = {
'id', 'name', 'links',
}
SHARE_GROUP_DETAIL_REQUIRED_KEYS = {
'id', 'name', 'description', 'created_at', 'status', 'share_types',
'project_id', 'host', 'links', 'share_group_type_id',
}
SHARE_GROUP_SNAPSHOT_SIMPLE_KEYS = {
'id', 'name', 'links',
}
SHARE_GROUP_SNAPSHOT_DETAIL_REQUIRED_KEYS = {
'id', 'name', 'description', 'created_at', 'status', 'project_id',
'links', 'share_group_id', 'members',
}
SHARE_GROUP_TYPE_REQUIRED_KEYS = {
'id', 'name', 'share_types', 'is_public', 'group_specs',
}

92
manila_tempest_tests/common/remote_client.py

@ -1,92 +0,0 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# 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 six
import sys
from oslo_log import log
from tempest import config
from tempest.lib.common import ssh
from tempest.lib.common.utils import test_utils
import tempest.lib.exceptions
CONF = config.CONF
LOG = log.getLogger(__name__)
def debug_ssh(function):
"""Decorator to generate extra debug info in case of ssh failure"""
def wrapper(self, *args, **kwargs):
try:
return function(self, *args, **kwargs)
except tempest.lib.exceptions.SSHTimeout:
try:
original_exception = sys.exc_info()
caller = test_utils.find_test_caller() or "not found"
if self.server:
msg = 'Caller: %s. Timeout trying to ssh to server %s'
LOG.debug(msg, caller, self.server)
if self.log_console and self.servers_client:
try:
msg = 'Console log for server %s: %s'
console_log = (
self.servers_client.get_console_output(
self.server['id'])['output'])
LOG.debug(msg, self.server['id'], console_log)
except Exception:
msg = 'Could not get console_log for server %s'
LOG.debug(msg, self.server['id'])
# re-raise the original ssh timeout exception
six.reraise(*original_exception)
finally:
# Delete the traceback to avoid circular references
_, _, trace = original_exception
del trace
return wrapper
class RemoteClient(object):
def __init__(self, ip_address, username, password=None, pkey=None,
server=None, servers_client=None):
"""Executes commands in a VM over ssh
:param ip_address: IP address to ssh to
:param username: ssh username
:param password: ssh password (optional)
:param pkey: ssh public key (optional)
:param server: server dict, used for debugging purposes
:param servers_client: servers client, used for debugging purposes
"""
self.server = server
self.servers_client = servers_client
self.log_console = CONF.compute_feature_enabled.console_output
self.ssh_client = ssh.Client(ip_address, username, password, pkey=pkey)
@debug_ssh
def exec_command(self, cmd):
# Shell options below add more clearness on failures,
# path is extended for some non-cirros guest oses (centos7)
cmd = CONF.validation.ssh_shell_prologue + " " + cmd
LOG.debug("Remote command: %s", cmd)
return self.ssh_client.exec_command(cmd)
@debug_ssh
def validate_authentication(self):
"""Validate ssh connection and authentication
This method raises an Exception when the validation fails.
"""
self.ssh_client.test_connection_auth()

247
manila_tempest_tests/config.py

@ -1,247 +0,0 @@
# Copyright 2014 Mirantis Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import print_function
from oslo_config import cfg
service_option = cfg.BoolOpt("manila",
default=True,
help="Whether or not manila is expected to be "
"available")
share_group = cfg.OptGroup(name="share", title="Share Service Options")
ShareGroup = [
cfg.StrOpt("min_api_microversion",
default="2.0",
help="The minimum api microversion is configured to be the "
"value of the minimum microversion supported by Manila."),
cfg.StrOpt("max_api_microversion",
default="2.42",
help="The maximum api microversion is configured to be the "
"value of the latest microversion supported by Manila."),
cfg.StrOpt("region",
default="",
help="The share region name to use. If empty, the value "
"of identity.region is used instead. If no such region "
"is found in the service catalog, the first found one is "
"used."),
cfg.StrOpt("catalog_type",
default="share",
help="Catalog type of the Share service."),
cfg.StrOpt('endpoint_type',
default='publicURL',
choices=['public', 'admin', 'internal',
'publicURL', 'adminURL', 'internalURL'],
help="The endpoint type to use for the share service."),
cfg.BoolOpt("multitenancy_enabled",
default=True,
help="This option used to determine backend driver type, "
"multitenant driver uses share-networks, but "
"single-tenant doesn't."),
cfg.BoolOpt("create_networks_when_multitenancy_enabled",
default=True,
help="This option is used only when other "
"'multitenancy_enabled' option is set to 'True'. "
"If this one is set to True, then tempest will create "
"neutron networks for each new manila share-network "
"it creates. Else it will use manila share-networks with "
"empty values (case of StandAloneNetworkPlugin and "
"NeutronSingleNetworkPlugin)."),
cfg.ListOpt("enable_protocols",
default=["nfs", "cifs"],
help="First value of list is protocol by default, "
"items of list show enabled protocols at all."),
cfg.ListOpt("enable_ip_rules_for_protocols",
default=["nfs", "cifs", ],
help="Selection of protocols, that should "
"be covered with ip rule tests"),
cfg.ListOpt("enable_user_rules_for_protocols",
default=[],
help="Selection of protocols, that should "
"be covered with user rule tests"),
cfg.ListOpt("enable_cert_rules_for_protocols",
default=["glusterfs", ],
help="Protocols that should be covered with cert rule tests."),
cfg.ListOpt("enable_cephx_rules_for_protocols",
default=["cephfs", ],
help="Protocols to be covered with cephx rule tests."),
cfg.StrOpt("username_for_user_rules",
default="Administrator",
help="Username, that will be used in user tests."),
cfg.ListOpt("enable_ro_access_level_for_protocols",
default=["nfs", ],
help="List of protocols to run tests with ro access level."),
# Capabilities
cfg.StrOpt("capability_storage_protocol",
deprecated_name="storage_protocol",
default="NFS_CIFS",
help="Backend protocol to target when creating volume types."),
cfg.BoolOpt("capability_snapshot_support",
help="Defines extra spec that satisfies specific back end "
"capability called 'snapshot_support' and will be used "
"for setting up custom share type. Defaults to value of "
"other config option 'run_snapshot_tests'."),
cfg.BoolOpt("capability_create_share_from_snapshot_support",
help="Defines extra spec that satisfies specific back end "
"capability called 'create_share_from_snapshot_support' "
"and will be used for setting up a custom share type. "
"Defaults to the value of run_snapshot_tests. Set it to "
"False if the driver being tested does not support "
"creating shares from snapshots."),
cfg.BoolOpt("capability_revert_to_snapshot_support",
deprecated_for_removal=True,
deprecated_reason="Redundant configuration option. Please use "
"'run_revert_to_snapshot_tests' config "
"option instead.",
help="Defines extra spec that satisfies specific back end "
"capability called 'revert_to_snapshot_support' "
"and will be used for setting up custom share type. "
"Defaults to the value of run_revert_to_snapshot_tests."),
cfg.StrOpt("capability_sg_consistent_snapshot_support",
choices=["host", "pool", None],
help="Backend capability to create consistent snapshots of "
"share group members. Will be used with creation "
"of new share group types as group spec."),
cfg.StrOpt("share_network_id",
default="",
help="Some backend drivers requires share network "
"for share creation. Share network id, that will be "
"used for shares. If not set, it won't be used."),
cfg.StrOpt("alt_share_network_id",
default="",
help="Share network id, that will be used for shares"
" in alt tenant. If not set, it won't be used"),
cfg.StrOpt("admin_share_network_id",
default="",
help="Share network id, that will be used for shares"
" in admin tenant. If not set, it won't be used"),
cfg.BoolOpt("multi_backend",
default=False,
help="Runs Manila multi-backend tests."),
cfg.ListOpt("backend_names",
default=[],
help="Names of share backends, that will be used with "
"multibackend tests. Tempest will use first two values."),
cfg.IntOpt("share_creation_retry_number",
default=0,
help="Defines number of retries for share creation. "
"It is useful to avoid failures caused by unstable "
"environment."),
cfg.IntOpt("build_interval",
default=3,
help="Time in seconds between share availability checks."),
cfg.IntOpt("build_timeout",
default=500,
help="Timeout in seconds to wait for a share to become"
"available."),
cfg.BoolOpt("suppress_errors_in_cleanup",
default=False,
help="Whether to suppress errors with clean up operation "
"or not. There are cases when we may want to skip "
"such errors and catch only test errors."),
# Switching ON/OFF test suites filtered by features
cfg.BoolOpt("run_quota_tests",
default=True,
help="Defines whether to run quota tests or not."),
cfg.BoolOpt("run_extend_tests",
default=True,
help="Defines whether to run share extend tests or not. "
"Disable this feature if used driver doesn't "
"support it."),
cfg.BoolOpt("run_shrink_tests",
default=True,
help="Defines whether to run share shrink tests or not. "
"Disable this feature if used driver doesn't "
"support it."),
cfg.BoolOpt("run_snapshot_tests",
default=True,
help="Defines whether to run tests that use share snapshots "
"or not. Disable this feature if used driver doesn't "
"support it."),
cfg.BoolOpt("run_revert_to_snapshot_tests",
default=False,
help="Defines whether to run tests that revert shares "
"to snapshots or not. Enable this feature if used "
"driver supports it."),
cfg.BoolOpt("run_share_group_tests",
default=True,
deprecated_name="run_consistency_group_tests",
help="Defines whether to run share group tests or not."),
cfg.BoolOpt("run_replication_tests",
default=False,
help="Defines whether to run replication tests or not. "
"Enable this feature if the driver is configured "
"for replication."),
cfg.BoolOpt("run_multiple_share_replicas_tests",
default=True,
help="Defines whether to run multiple replicas creation test "
"or not. Enable this if the driver can create more than "
"one replica for a share."),
cfg.BoolOpt("run_host_assisted_migration_tests",
deprecated_name="run_migration_tests",
default=False,
help="Enable or disable host-assisted migration tests."),
cfg.BoolOpt("run_driver_assisted_migration_tests",
deprecated_name="run_migration_tests",
default=False,
help="Enable or disable driver-assisted migration tests."),
cfg.BoolOpt("run_migration_with_preserve_snapshots_tests",
default=False,
help="Enable or disable migration with "
"preserve_snapshots tests set to True."),
cfg.BoolOpt("run_manage_unmanage_tests",
default=False,
help="Defines whether to run manage/unmanage tests or not. "
"These test may leave orphaned resources, so be careful "
"enabling this opt."),
cfg.BoolOpt("run_manage_unmanage_snapshot_tests",
default=False,
help="Defines whether to run manage/unmanage snapshot tests "
"or not. These tests may leave orphaned resources, so be "
"careful enabling this opt."),
cfg.BoolOpt("run_mount_snapshot_tests",
default=False,
help="Enable or disable mountable snapshot tests."),
cfg.StrOpt("image_with_share_tools",
default="manila-service-image-master",
help="Image name for vm booting with nfs/smb clients tool."),
cfg.StrOpt("image_username",
default="manila",
help="Image username."),
cfg.StrOpt("image_password",
help="Image password. Should be used for "
"'image_with_share_tools' without Nova Metadata support."),
cfg.StrOpt("client_vm_flavor_ref",
default="100",
help="Flavor used for client vm in scenario tests."),
cfg.IntOpt("migration_timeout",
default=1500,
help="Time to wait for share migration before "
"timing out (seconds)."),
cfg.StrOpt("default_share_type_name",
help="Default share type name to use in tempest tests."),
cfg.StrOpt("backend_replication_type",
default='none',
choices=['none', 'writable', 'readable', 'dr'],
help="Specify the replication type supported by the backend."),
cfg.IntOpt("share_size",
default=1,
help="Default size in GB for shares created by share tests."),
]

75
manila_tempest_tests/plugin.py

@ -1,75 +0,0 @@
# Copyright 2015 Deutsche Telekom AG
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# 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 os
from tempest import config
from tempest.test_discover import plugins
from manila_tempest_tests import config as config_share
class ManilaTempestPlugin(plugins.TempestPlugin):
def load_tests(self):
base_path = os.path.split(os.path.dirname(
os.path.abspath(__file__)))[0]
test_dir = "manila_tempest_tests/tests"
full_test_dir = os.path.join(base_path, test_dir)
return full_test_dir, base_path
def register_opts(self, conf):
conf.register_opt(config_share.service_option,
group='service_available')
conf.register_group(config_share.share_group)
conf.register_opts(config_share.ShareGroup, group='share')
# NOTE(vponomaryov): Set options 'capability_snapshot_support' and
# 'capability_create_share_from_snapshot_support' to opt
# 'run_snapshot_tests' if not configured.
if conf.share.capability_snapshot_support is None:
conf.set_default(
"capability_snapshot_support",
conf.share.run_snapshot_tests,
group="share",
)
if conf.share.capability_create_share_from_snapshot_support is None:
conf.set_default(
"capability_create_share_from_snapshot_support",
conf.share.run_snapshot_tests,
group="share",
)
def get_opt_lists(self):
return [(config_share.share_group.name, config_share.ShareGroup),
('service_available', [config_share.service_option])]
def get_service_clients(self):
shares_config = config.service_client_config('share')
v1_params = {
'name': 'share_v1',
'service_version': 'share.v1',
'module_path': 'manila_tempest_tests.services.share.json',
'client_names': ['SharesClient'],
}
v2_params = {
'name': 'share_v2',
'service_version': 'share.v2',
'module_path': 'manila_tempest_tests.services.share.v2',
'client_names': ['SharesV2Client'],
}
v1_params.update(shares_config)
v2_params.update(shares_config)
return [v1_params, v2_params]

0
manila_tempest_tests/services/__init__.py

18
manila_tempest_tests/services/share/__init__.py

@ -1,18 +0,0 @@
# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
from manila_tempest_tests.services.share import json as v1
from manila_tempest_tests.services.share.v2 import json as v2
__all__ = ['v1', 'v2']

17
manila_tempest_tests/services/share/json/__init__.py

@ -1,17 +0,0 @@
# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
from manila_tempest_tests.services.share.json.shares_client import SharesClient
__all__ = ['SharesClient']

760
manila_tempest_tests/services/share/json/shares_client.py

@ -1,760 +0,0 @@
# Copyright 2014 Mirantis Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# 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 json
import time
import six
from six.moves.urllib import parse as urlparse
from tempest import config
from tempest.lib.common import rest_client
from tempest.lib.common.utils import data_utils
from tempest.lib import exceptions
from manila_tempest_tests import share_exceptions
CONF = config.CONF
class SharesClient(rest_client.RestClient):
"""Tempest REST client for Manila.
It handles shares and access to it in OpenStack.
"""
def __init__(self, auth_provider, **kwargs):
super(SharesClient, self).__init__(auth_provider, **kwargs)
self.share_protocol = None
if CONF.share.enable_protocols:
self.share_protocol = CONF.share.enable_protocols[0]
self.share_network_id = CONF.share.share_network_id
self.share_size = CONF.share.share_size
def create_share(self, share_protocol=None, size=None,
name=None, snapshot_id=None, description=None,
metadata=None, share_network_id=None,
share_type_id=None, is_public=False):
metadata = metadata or {}
if name is None:
name = data_utils.rand_name("tempest-created-share")
if description is None:
description = data_utils.rand_name("tempest-created-share-desc")
if size is None:
size = self.share_size
if share_protocol is None:
share_protocol = self.share_protocol
if share_protocol is None:
raise share_exceptions.ShareProtocolNotSpecified()
post_body = {
"share": {
"share_proto": share_protocol,
"description": description,
"snapshot_id": snapshot_id,
"name": name,
"size": size,
"metadata": metadata,
"is_public": is_public,
}
}
if share_network_id:
post_body["share"]["share_network_id"] = share_network_id
if share_type_id:
post_body["share"]["share_type"] = share_type_id
body = json.dumps(post_body)
resp, body = self.post("shares", body)
self.expected_success(200, resp.status)
return self._parse_resp(body)
def delete_share(self, share_id):
resp, body = self.delete("shares/%s" % share_id)
self.expected_success(202, resp.status)
return body
def manage_share(self, service_host, protocol, export_path,
share_type_id, name=None, description=None):
post_body = {
"share": {
"export_path": export_path,
"service_host": service_host,
"protocol": protocol,
"share_type": share_type_id,
"name": name,
"description": description,
}
}
body = json.dumps(post_body)
resp, body = self.post("os-share-manage", body)
self.expected_success(200, resp.status)
return self._parse_resp(body)
def unmanage_share(self, share_id):
resp, body = self.post(
"os-share-unmanage/%s/unmanage" % share_id, None)
self.expected_success(202, resp.status)
return body
def list_shares(self, detailed=False, params=None):
"""Get list of shares w/o filters."""
uri = 'shares/detail' if detailed else 'shares'
uri += '?%s' % urlparse.urlencode(params) if params else ''
resp, body = self.get(uri)
self.expected_success(200, resp.status)
return self._parse_resp(body)
def list_shares_with_detail(self, params=None):
"""Get detailed list of shares w/o filters."""
return self.list_shares(detailed=True, params=params)
def get_share(self, share_id):
resp, body = self.get("shares/%s" % share_id)
self.expected_success(200, resp.status)
return self._parse_resp(body)
def create_access_rule(self, share_id, access_type="ip",
access_to="0.0.0.0", access_level=None):
post_body = {
"os-allow_access": {
"access_type": access_type,
"access_to": access_to,
"access_level": access_level,
}
}
body = json.dumps(post_body)
resp, body = self.post("shares/%s/action" % share_id, body)
self.expected_success(200, resp.status)
return self._parse_resp(body)
def list_access_rules(self, share_id):
body = {"os-access_list": None}
resp, body = self.post("shares/%s/action" % share_id, json.dumps(body))
self.expected_success(200, resp.status)
return self._parse_resp(body)
def delete_access_rule(self, share_id, rule_id):
post_body = {
"os-deny_access": {
"access_id": rule_id,
}
}
body = json.dumps(post_body)
resp, body = self.post("shares/%s/action" % share_id, body)
self.expected_success(202, resp.status)
return body
def extend_share(self, share_id, new_size):
post_body = {
"os-extend": {
"new_size": new_size,
}
}
body = json.dumps(post_body)
resp, body = self.post("shares/%s/action" % share_id, body)
self.expected_success(202, resp.status)
return body
def shrink_share(self, share_id, new_size):
post_body = {
"os-shrink": {
"new_size": new_size,
}
}
body = json.dumps(post_body)
resp, body = self.post("shares/%s/action" % share_id, body)
self.expected_success(202, resp.status)
return body
def create_snapshot(self, share_id, name=None, description=None,
force=False):
if name is None:
name = data_utils.rand_name("tempest-created-share-snap")
if description is None:
description = data_utils.rand_name(
"tempest-created-share-snap-desc")
post_body = {
"snapshot": {
"name": name,
"force": force,
"description": description,
"share_id": share_id,
}
}
body = json.dumps(post_body)
resp, body = self.post("snapshots", body)
self.expected_success(202, resp.status)
return self._parse_resp(body)
def get_snapshot(self, snapshot_id):
resp, body = self.get("snapshots/%s" % snapshot_id)
self.expected_success(200, resp.status)
return self._parse_resp(body)
def list_snapshots(self, detailed=False, params=None):
"""Get list of share snapshots w/o filters."""
uri = 'snapshots/detail' if detailed else 'snapshots'
uri += '?%s' % urlparse.urlencode(params) if params else ''
resp, body = self.get(uri)
self.expected_success(200, resp.status)
return self._parse_resp(body)
def list_snapshots_with_detail(self, params=None):
"""Get detailed list of share snapshots w/o filters."""
return self.list_snapshots(detailed=True, params=params)
def delete_snapshot(self, snap_id):
resp, body = self.delete("snapshots/%s" % snap_id)
self.expected_success(202, resp.status)
return body
def wait_for_share_status(self, share_id, status):
"""Waits for a share to reach a given status."""
body = self.get_share(share_id)
share_name = body['name']
share_status = body['status']
start = int(time.time())
while share_status != status:
time.sleep(self.build_interval)
body = self.get_share(share_id)
share_status = body['status']
if share_status == status:
return
elif 'error' in share_status.lower():
raise share_exceptions.ShareBuildErrorException(
share_id=share_id)
if int(time.time()) - start >= self.build_timeout:
message = ('Share %s failed to reach %s status within '
'the required time (%s s).' %
(share_name, status, self.build_timeout))
raise exceptions.TimeoutException(message)
def wait_for_snapshot_status(self, snapshot_id, status):
"""Waits for a snapshot to reach a given status."""
body = self.get_snapshot(snapshot_id)
snapshot_name = body['name']
snapshot_status = body['status']
start = int(time.time())
while snapshot_status != status:
time.sleep(self.build_interval)
body = self.get_snapshot(snapshot_id)
snapshot_status = body['status']
if 'error' in snapshot_status:
raise share_exceptions.SnapshotBuildErrorException(
snapshot_id=snapshot_id)
if int(time.time()) - start >= self.build_timeout:
message = ('Share Snapshot %s failed to reach %s status '
'within the required time (%s s).' %
(snapshot_name, status, self.build_timeout))
raise exceptions.TimeoutException(message)
def wait_for_access_rule_status(self, share_id, rule_id, status):
"""Waits for an access rule to reach a given status."""
rule_status = "new"
start = int(time.time())
while rule_status != status:
time.sleep(self.build_interval)
rules = self.list_access_rules(share_id)
for rule in rules:
if rule["id"] in rule_id:
rule_status = rule['state']
break
if 'error' in rule_status:
raise share_exceptions.AccessRuleBuildErrorException(
rule_id=rule_id)
if int(time.time()) - start >= self.build_timeout:
message = ('Share Access Rule %s failed to reach %s status '
'within the required time (%s s).' %
(rule_id, status, self.build_timeout))
raise exceptions.TimeoutException(message)
def default_quotas(self, tenant_id):
resp, body = self.get("os-quota-sets/%s/defaults" % tenant_id)
self.expected_success(200, resp.status)
return self._parse_resp(body)
def show_quotas(self, tenant_id, user_id=None):
uri = "os-quota-sets/%s" % tenant_id
if user_id is not None:
uri += "?user_id=%s" % user_id
resp, body = self.get(uri)
self.expected_success(200, resp.status)
return self._parse_resp(body)
def reset_quotas(self, tenant_id, user_id=None):
uri = "os-quota-sets/%s" % tenant_id
if user_id is not None:
uri += "?user_id=%s" % user_id
resp, body = self.delete(uri)
self.expected_success(202, resp.status)
return body
def update_quotas(self, tenant_id, user_id=None, shares=None,
snapshots=None, gigabytes=None, snapshot_gigabytes=None,
share_networks=None, force=True):
uri = "os-quota-sets/%s" % tenant_id
if user_id is not None:
uri += "?user_id=%s" % user_id
put_body = {"tenant_id": tenant_id}
if force:
put_body["force"] = "true"
if shares is not None:
put_body["shares"] = shares
if snapshots is not None:
put_body["snapshots"] = snapshots
if gigabytes is not None:
put_body["gigabytes"] = gigabytes
if snapshot_gigabytes is not None:
put_body["snapshot_gigabytes"] = snapshot_gigabytes
if share_networks is not None:
put_body["share_networks"] = share_networks
put_body = json.dumps({"quota_set": put_body})
resp, body = self.put(uri, put_body)
self.expected_success(200, resp.status)
return self._parse_resp(body)
def get_limits(self):
resp, body = self.get("limits")
self.expected_success(200, resp.status)
return self._parse_resp(body)
def is_resource_deleted(self, *args, **kwargs):
"""Verifies whether provided resource deleted or not.
:param kwargs: dict with expected keys 'share_id', 'snapshot_id',
:param kwargs: 'sn_id', 'ss_id', 'vt_id' and 'server_id'
:raises share_exceptions.InvalidResource
"""
if "share_id" in kwargs:
if "rule_id" in kwargs:
rule_id = kwargs.get("rule_id")
share_id = kwargs.get("share_id")
rules = self.list_access_rules(share_id)
for rule in rules:
if rule["id"] == rule_id:
return False
return True
else:
return self._is_resource_deleted(
self.get_share, kwargs.get("share_id"))
elif "snapshot_id" in kwargs:
return self._is_resource_deleted(
self.get_snapshot, kwargs.get("snapshot_id"))
elif "sn_id" in kwargs:
return self._is_resource_deleted(
self.get_share_network, kwargs.get("sn_id"))
elif "ss_id" in kwargs:
return self._is_resource_deleted(
self.get_security_service, kwargs.get("ss_id"))
elif "vt_id" in kwargs:
return self._is_resource_deleted(
self.get_volume_type, kwargs.get("vt_id"))
elif "st_id" in kwargs:
return self._is_resource_deleted(
self.get_share_type, kwargs.get("st_id"))
elif "server_id" in kwargs:
return self._is_resource_deleted(
self.show_share_server, kwargs.get("server_id"))
else:
raise share_exceptions.InvalidResource(
message=six.text_type(kwargs))
def _is_resource_deleted(self, func, res_id):
try:
res = func(res_id)
except exceptions.NotFound:
return True
if res.get('status') in ['error_deleting', 'error']:
# Resource has "error_deleting" status and can not be deleted.
resource_type = func.__name__.split('_', 1)[-1]
raise share_exceptions.ResourceReleaseFailed(
res_type=resource_type, res_id=res_id)
return False
def wait_for_resource_deletion(self, *args, **kwargs):
"""Waits for a resource to be deleted."""
start_time = int(time.time())
while True:
if self.is_resource_deleted(*args, **kwargs):
return
if int(time.time()) - start_time >= self.build_timeout:
raise exceptions.TimeoutException
time.sleep(self.build_interval)
def list_extensions(self):
resp, extensions = self.get("extensions")
self.expected_success(200, resp.status)
return self._parse_resp(extensions)
def update_share(self, share_id, name=None, desc=None, is_public=None):
body = {"share": {}}
if name is not None:
body["share"].update({"display_name": name})
if desc is not None:
body["share"].update({"display_description": desc})
if is_public is not None:
body["share"].update({"is_public": is_public})
body = json.dumps(body)
resp, body = self.put("shares/%s" % share_id, body)
self.expected_success(200, resp.status)
return self._parse_resp(body)
def rename_snapshot(self, snapshot_id, name, desc=None):
body = {"snapshot": {"display_name": name}}
if desc is not None:
body["snapshot"].update({"display_description": desc})
body = json.dumps(body)
resp, body = self.put("snapshots/%s" % snapshot_id, body)
self.expected_success(200, resp.status)
return self._parse_resp(body)
def reset_state(self, s_id, status="error", s_type="shares"):
"""Resets the state of a share or a snapshot.
status: available, error, creating, deleting, error_deleting
s_type: shares, snapshots
"""
body = {"os-reset_status": {"status": status}}
body = json.dumps(body)
resp, body = self.post("%s/%s/action" % (s_type, s_id), body)
self.expected_success(202, resp.status)
return body
def force_delete(self, s_id, s_type="shares"):
"""Force delete share or snapshot.
s_type: shares, snapshots
"""
body = {"os-force_delete": None}
body = json.dumps(body)
resp, body = self.post("%s/%s/action" % (s_type, s_id), body)
self.expected_success(202, resp.status)
return body
###############
def list_services(self, params=None):
"""List services."""
uri = 'os-services'
if params:
uri += '?%s' % urlparse.urlencode(params)
resp, body = self.get(uri)
self.expected_success(200, resp.status)
return self._parse_resp(body)
###############
def _update_metadata(self, share_id, metadata=None, method="post"):
uri = "shares/%s/metadata" % share_id
if metadata is None:
metadata = {}
post_body = {"metadata": metadata}
body = json.dumps(post_body)
if method is "post":
resp, metadata = self.post(uri, body)
if method is "put":
resp, metadata = self.put(uri, body)
self.expected_success(200, resp.status)
return self._parse_resp(metadata)
def set_metadata(self, share_id, metadata=None):
return self._update_metadata(share_id, metadata)
def update_all_metadata(self, share_id, metadata=None):
return self._update_metadata(share_id, metadata, method="put")
def delete_metadata(self, share_id, key):
resp, body = self.delete("shares/%s/metadata/%s" % (share_id, key))
self.expected_success(200, resp.status)
return body
def get_metadata(self, share_id):
resp, body = self.get("shares/%s/metadata" % share_id)
self.expected_success(200, resp.status)
return self._parse_resp(body)
###############
def create_security_service(self, ss_type="ldap", **kwargs):
# ss_type: ldap, kerberos, active_directory
# kwargs: name, description, dns_ip, server, domain, user, password
post_body = {"type": ss_type}
post_body.update(kwargs)
body = json.dumps({"security_service": post_body})
resp, body = self.post("security-services", body)
self.expected_success(200, resp.status)
return self._parse_resp(body)
def update_security_service(self, ss_id, **kwargs):
# ss_id - id of security-service entity
# kwargs: dns_ip, server, domain, user, password, name, description
# for 'active' status can be changed
# only 'name' and 'description' fields
body = json.dumps({"security_service": kwargs})
resp, body = self.put("security-services/%s" % ss_id, body)
self.expected_success(200, resp.status)
return self._parse_resp(body)
def get_security_service(self, ss_id):
resp, body = self.get("security-services/%s" % ss_id)
self.expected_success(200, resp.status)
return self._parse_resp(body)
def list_security_services(self, detailed=False, params=None):
uri = "security-services"
if detailed:
uri += '/detail'
if params:
uri += "?%s" % urlparse.urlencode(params)
resp, body = self.get(uri)
self.expected_success(200, resp.status)
return self._parse_resp(body)
def delete_security_service(self, ss_id):
resp, body = self.delete("security-services/%s" % ss_id)
self.expected_success(202, resp.status)
return body
###############
def create_share_network(self, **kwargs):
# kwargs: name, description
# + for neutron: neutron_net_id, neutron_subnet_id
body = json.dumps({"share_network": kwargs})
resp, body = self.post("share-networks", body)
self.expected_success(200, resp.status)
return self._parse_resp(body)
def update_share_network(self, sn_id, **kwargs):
# kwargs: name, description
# + for neutron: neutron_net_id, neutron_subnet_id
body = json.dumps({"share_network": kwargs})
resp, body = self.put("share-networks/%s" % sn_id, body)
self.expected_success(200, resp.status)
return self._parse_resp(body)
def get_share_network(self, sn_id):
resp, body = self.get("share-networks/%s" % sn_id)
self.expected_success(200, resp.status)
return self._parse_resp(body)
def list_share_networks(self):
resp, body = self.get("share-networks")
self.expected_success(200, resp.status)
return self._parse_resp(body)
def list_share_networks_with_detail(self, params=None):
"""List the details of all shares."""
uri = "share-networks/detail"
if params:
uri += "?%s" % urlparse.urlencode(params)
resp, body = self.get(uri)
self.expected_success(200, resp.status)
return self._parse_resp(body)
def delete_share_network(self, sn_id):
resp, body = self.delete("share-networks/%s" % sn_id)
self.expected_success(202, resp.status)
return body
###############
def _map_security_service_and_share_network(self, sn_id, ss_id,
action="add"):
# sn_id: id of share_network_entity
# ss_id: id of security service entity
# action: add, remove
data = {
"%s_security_service" % action: {
"security_service_id": ss_id,
}
}
body = json.dumps(data)
resp, body = self.post("share-networks/%s/action" % sn_id, body)
self.expected_success(200, resp.status)
return self._parse_resp(body)
def add_sec_service_to_share_network(self, sn_id, ss_id):
body = self._map_security_service_and_share_network(sn_id, ss_id)
return body
def remove_sec_service_from_share_network(self, sn_id, ss_id):
body = self._map_security_service_and_share_network(
sn_id, ss_id, "remove")
return body
def list_sec_services_for_share_network(self, sn_id):
resp, body = self.get("security-services?share_network_id=%s" % sn_id)
self.expected_success(200, resp.status)
return self._parse_resp(body)
###############
def list_share_types(self, params=None):
uri = 'types'
if params is not None:
uri += '?%s' % urlparse.urlencode(params)
resp, body = self.get(uri)
self.expected_success(200, resp.status)
return self._parse_resp(body)
def create_share_type(self, name, is_public=True, **kwargs):
post_body = {
'name': name,
'extra_specs': kwargs.get('extra_specs'),
'os-share-type-access:is_public': is_public,
}
post_body = json.dumps({'share_type': post_body})
resp, body = self.post('types', post_body)
self.expected_success(200, resp.status)
return self._parse_resp(body)
def delete_share_type(self, share_type_id):
resp, body = self.delete("types/%s" % share_type_id)
self.expected_success(202, resp.status)
return body
def get_share_type(self, share_type_id):
resp, body = self.get("types/%s" % share_type_id)
self.expected_success(200, resp.status)