Fix H405 (multi line docstring) warnings (openstack_dashboard)
H405: multi line docstring summary not separated with an empty line Closes-Bug: #1696996 Change-Id: Id895695663b19522d9cdc22f8b012e49680d708b
This commit is contained in:
parent
95629a337e
commit
b9d0243c33
@ -199,8 +199,10 @@ class Quota(object):
|
||||
|
||||
|
||||
class QuotaSet(Sequence):
|
||||
"""Wrapper for client QuotaSet objects which turns the individual quotas
|
||||
into Quota objects for easier handling/iteration.
|
||||
"""Wrapper for client QuotaSet objects.
|
||||
|
||||
This turns the individual quotas into Quota objects
|
||||
for easier handling/iteration.
|
||||
|
||||
`QuotaSet` objects support a mix of `list` and `dict` methods; you can use
|
||||
the bracket notation (`qs["my_quota"] = 0`) to add new quota values, and
|
||||
@ -229,8 +231,9 @@ class QuotaSet(Sequence):
|
||||
return self.items[index]
|
||||
|
||||
def __add__(self, other):
|
||||
"""Merge another QuotaSet into this one. Existing quotas are
|
||||
not overridden.
|
||||
"""Merge another QuotaSet into this one.
|
||||
|
||||
Existing quotas are not overridden.
|
||||
"""
|
||||
if not isinstance(other, QuotaSet):
|
||||
msg = "Can only add QuotaSet to QuotaSet, " \
|
||||
|
@ -253,7 +253,9 @@ def update_pagination(entities, page_size, marker, sort_dir):
|
||||
@profiler.trace
|
||||
def volume_list_paged(request, search_opts=None, marker=None, paginate=False,
|
||||
sort_dir="desc"):
|
||||
"""To see all volumes in the cloud as an admin you can pass in a special
|
||||
"""List volumes with pagination.
|
||||
|
||||
To see all volumes in the cloud as an admin you can pass in a special
|
||||
search option: {'all_tenants': 1}
|
||||
"""
|
||||
has_more_data = False
|
||||
@ -611,8 +613,7 @@ def volume_cg_snapshot_delete(request, cg_snapshot_id):
|
||||
|
||||
@memoized
|
||||
def volume_backup_supported(request):
|
||||
"""This method will determine if cinder supports backup.
|
||||
"""
|
||||
"""This method will determine if cinder supports backup."""
|
||||
# TODO(lcheng) Cinder does not expose the information if cinder
|
||||
# backup is configured yet. This is a workaround until that
|
||||
# capability is available.
|
||||
@ -970,8 +971,7 @@ def list_extensions(cinder_api):
|
||||
|
||||
@memoized_with_request(list_extensions)
|
||||
def extension_supported(extensions, extension_name):
|
||||
"""This method will determine if Cinder supports a given extension name.
|
||||
"""
|
||||
"""This method will determine if Cinder supports a given extension name."""
|
||||
for extension in extensions:
|
||||
if extension.name == extension_name:
|
||||
return True
|
||||
@ -980,7 +980,9 @@ def extension_supported(extensions, extension_name):
|
||||
|
||||
@profiler.trace
|
||||
def transfer_list(request, detailed=True, search_opts=None):
|
||||
"""To see all volumes transfers as an admin pass in a special
|
||||
"""List volume transfers.
|
||||
|
||||
To see all volumes transfers as an admin pass in a special
|
||||
search option: {'all_tenants': 1}
|
||||
"""
|
||||
c_client = cinderclient(request)
|
||||
|
@ -239,9 +239,7 @@ def image_delete(request, image_id):
|
||||
|
||||
@profiler.trace
|
||||
def image_get(request, image_id):
|
||||
"""Returns an Image object populated with metadata for image
|
||||
with supplied identifier.
|
||||
"""
|
||||
"""Returns an Image object populated with metadata for a given image."""
|
||||
image = glanceclient(request).images.get(image_id)
|
||||
return Image(image)
|
||||
|
||||
@ -525,8 +523,9 @@ class Namespace(BaseGlanceMetadefAPIResourceWrapper):
|
||||
def filter_properties_target(namespaces_iter,
|
||||
resource_types,
|
||||
properties_target):
|
||||
"""Filter metadata namespaces based on the given resource types and
|
||||
properties target.
|
||||
"""Filter metadata namespaces.
|
||||
|
||||
Filtering is done based ongiven resource types and a properties target.
|
||||
|
||||
:param namespaces_iter: Metadata namespaces iterable.
|
||||
:param resource_types: List of resource type names.
|
||||
|
@ -311,8 +311,9 @@ def get_default_domain(request, get_name=True):
|
||||
|
||||
|
||||
def get_effective_domain_id(request):
|
||||
"""Gets the id of the default domain to use when creating Identity
|
||||
objects. If the requests default domain is the same as DEFAULT_DOMAIN,
|
||||
"""Gets the id of the default domain.
|
||||
|
||||
If the requests default domain is the same as DEFAULT_DOMAIN,
|
||||
return None.
|
||||
"""
|
||||
default_domain = get_default_domain(request)
|
||||
|
@ -225,8 +225,9 @@ class FlavorExtraSpec(object):
|
||||
|
||||
|
||||
def get_auth_params_from_request(request):
|
||||
"""Extracts the properties from the request object needed by the novaclient
|
||||
call below. These will be used to memoize the calls to novaclient
|
||||
"""Extracts properties needed by novaclient call from the request object.
|
||||
|
||||
These will be used to memoize the calls to novaclient.
|
||||
"""
|
||||
return (
|
||||
request.user.username,
|
||||
|
@ -11,8 +11,7 @@
|
||||
# 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.
|
||||
"""API over the cinder service.
|
||||
"""
|
||||
"""API over the cinder service."""
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.views import generic
|
||||
@ -28,14 +27,12 @@ CLIENT_KEYWORDS = {'marker', 'sort_dir', 'paginate'}
|
||||
|
||||
@urls.register
|
||||
class Volumes(generic.View):
|
||||
"""API for cinder volumes.
|
||||
"""
|
||||
"""API for cinder volumes."""
|
||||
url_regex = r'cinder/volumes/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request):
|
||||
"""Get a detailed list of volumes associated with the current user's
|
||||
project.
|
||||
"""Get a detailed list of volumes associated with the current project.
|
||||
|
||||
Example GET:
|
||||
http://localhost/api/cinder/volumes?paginate=true&sort_dir=asc
|
||||
@ -99,8 +96,7 @@ class Volumes(generic.View):
|
||||
|
||||
@urls.register
|
||||
class Volume(generic.View):
|
||||
"""API for cinder volume.
|
||||
"""
|
||||
"""API for cinder volume."""
|
||||
url_regex = r'cinder/volumes/(?P<volume_id>[^/]+)/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
@ -118,8 +114,7 @@ class Volume(generic.View):
|
||||
|
||||
@urls.register
|
||||
class VolumeTypes(generic.View):
|
||||
"""API for volume types.
|
||||
"""
|
||||
"""API for volume types."""
|
||||
url_regex = r'cinder/volumetypes/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
@ -162,8 +157,7 @@ class VolumeMetadata(generic.View):
|
||||
|
||||
@urls.register
|
||||
class VolumeType(generic.View):
|
||||
"""API for getting a volume type.
|
||||
"""
|
||||
"""API for getting a volume type."""
|
||||
url_regex = r'cinder/volumetypes/(?P<volumetype_id>[^/]+)/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
@ -189,14 +183,12 @@ class VolumeType(generic.View):
|
||||
|
||||
@urls.register
|
||||
class VolumeSnapshots(generic.View):
|
||||
"""API for cinder volume snapshots.
|
||||
"""
|
||||
"""API for cinder volume snapshots."""
|
||||
url_regex = r'cinder/volumesnapshots/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request):
|
||||
"""Get a detailed list of volume snapshots associated with the current
|
||||
user's project.
|
||||
"""Get a list of volume snapshots associated with the current project.
|
||||
|
||||
The listing result is an object with property "items".
|
||||
"""
|
||||
@ -277,8 +269,7 @@ class VolumeTypeMetadata(generic.View):
|
||||
|
||||
@urls.register
|
||||
class Extensions(generic.View):
|
||||
"""API for cinder extensions.
|
||||
"""
|
||||
# API for cinder extensions.
|
||||
url_regex = r'cinder/extensions/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
@ -324,13 +315,13 @@ class TenantAbsoluteLimits(generic.View):
|
||||
|
||||
@urls.register
|
||||
class Services(generic.View):
|
||||
"""API for cinder services.
|
||||
"""
|
||||
"""API for cinder services."""
|
||||
url_regex = r'cinder/services/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request):
|
||||
"""Get a list of cinder services.
|
||||
|
||||
Will return HTTP 501 status code if the service_list extension is
|
||||
not supported.
|
||||
"""
|
||||
@ -352,8 +343,7 @@ class Services(generic.View):
|
||||
|
||||
@urls.register
|
||||
class DefaultQuotaSets(generic.View):
|
||||
"""API for getting default quotas for cinder
|
||||
"""
|
||||
"""API for getting default quotas for cinder"""
|
||||
url_regex = r'cinder/quota-sets/defaults/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
@ -400,8 +390,7 @@ class DefaultQuotaSets(generic.View):
|
||||
|
||||
@urls.register
|
||||
class QuotaSets(generic.View):
|
||||
"""API for setting quotas for a given project.
|
||||
"""
|
||||
"""API for setting quotas for a given project."""
|
||||
url_regex = r'cinder/quota-sets/(?P<project_id>[0-9a-f]+)$'
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
|
@ -11,8 +11,7 @@
|
||||
# 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.
|
||||
"""API for the glance service.
|
||||
"""
|
||||
"""API for the glance service."""
|
||||
|
||||
from django import forms
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
@ -29,21 +28,18 @@ CLIENT_KEYWORDS = {'resource_type', 'marker',
|
||||
|
||||
@urls.register
|
||||
class Version(generic.View):
|
||||
"""API for active glance version.
|
||||
"""
|
||||
"""API for active glance version."""
|
||||
url_regex = r'glance/version/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request):
|
||||
"""Get active glance version.
|
||||
"""
|
||||
"""Get active glance version."""
|
||||
return {'version': str(api.glance.get_version())}
|
||||
|
||||
|
||||
@urls.register
|
||||
class Image(generic.View):
|
||||
"""API for retrieving a single image
|
||||
"""
|
||||
"""API for retrieving a single image"""
|
||||
url_regex = r'glance/images/(?P<image_id>[^/]+|default)/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
@ -95,14 +91,12 @@ class Image(generic.View):
|
||||
|
||||
@urls.register
|
||||
class ImageProperties(generic.View):
|
||||
"""API for retrieving only a custom properties of single image.
|
||||
"""
|
||||
"""API for retrieving only a custom properties of single image."""
|
||||
url_regex = r'glance/images/(?P<image_id>[^/]+)/properties/'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request, image_id):
|
||||
"""Get custom properties of specific image.
|
||||
"""
|
||||
"""Get custom properties of specific image."""
|
||||
return api.glance.image_get(request, image_id).properties
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
@ -123,8 +117,7 @@ class UploadObjectForm(forms.Form):
|
||||
|
||||
@urls.register
|
||||
class Images(generic.View):
|
||||
"""API for Glance images.
|
||||
"""
|
||||
"""API for Glance images."""
|
||||
url_regex = r'glance/images/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
|
@ -9,8 +9,7 @@
|
||||
# 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.
|
||||
"""API for the heat service.
|
||||
"""
|
||||
"""API for the heat service."""
|
||||
|
||||
from django.views import generic
|
||||
|
||||
@ -21,8 +20,7 @@ from openstack_dashboard.api.rest import utils as rest_utils
|
||||
|
||||
@urls.register
|
||||
class Validate(generic.View):
|
||||
"""API for validating a template
|
||||
"""
|
||||
"""API for validating a template"""
|
||||
url_regex = r'heat/validate/$'
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
@ -40,14 +38,12 @@ class Validate(generic.View):
|
||||
|
||||
@urls.register
|
||||
class Services(generic.View):
|
||||
"""API for heat services.
|
||||
"""
|
||||
"""API for heat services."""
|
||||
url_regex = r'heat/services/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request):
|
||||
"""Get a list of heat services.
|
||||
"""
|
||||
"""Get a list of heat services."""
|
||||
if api.base.is_service_enabled(request, 'orchestration'):
|
||||
result = api.heat.service_list(request)
|
||||
return {'items': [u.to_dict() for u in result]}
|
||||
|
@ -25,7 +25,9 @@ class NaNJSONEncoder(json.JSONEncoder):
|
||||
super(NaNJSONEncoder, self).__init__(**kwargs)
|
||||
|
||||
def iterencode(self, o, _one_shot=False):
|
||||
"""The sole purpose of defining a custom JSONEncoder class is to
|
||||
"""JSON encoder with NaN and float inf support.
|
||||
|
||||
The sole purpose of defining a custom JSONEncoder class is to
|
||||
override floatstr() inner function, or more specifically the
|
||||
representation of NaN and +/-float('inf') values in a JSON. Although
|
||||
Infinity values are not supported by JSON standard, we still can
|
||||
|
@ -11,8 +11,7 @@
|
||||
# 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.
|
||||
"""API over the keystone service.
|
||||
"""
|
||||
"""API over the keystone service."""
|
||||
|
||||
from django.conf import settings
|
||||
import django.http
|
||||
@ -25,21 +24,18 @@ from openstack_dashboard.api.rest import utils as rest_utils
|
||||
|
||||
@urls.register
|
||||
class Version(generic.View):
|
||||
"""API for active keystone version.
|
||||
"""
|
||||
"""API for active keystone version."""
|
||||
url_regex = r'keystone/version/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request):
|
||||
"""Get active keystone version.
|
||||
"""
|
||||
"""Get active keystone version."""
|
||||
return {'version': str(api.keystone.get_version())}
|
||||
|
||||
|
||||
@urls.register
|
||||
class Users(generic.View):
|
||||
"""API for keystone users.
|
||||
"""
|
||||
"""API for keystone users."""
|
||||
url_regex = r'keystone/users/$'
|
||||
client_keywords = {'project_id', 'domain_id', 'group_id'}
|
||||
|
||||
@ -113,8 +109,7 @@ class Users(generic.View):
|
||||
|
||||
@urls.register
|
||||
class User(generic.View):
|
||||
"""API for a single keystone user.
|
||||
"""
|
||||
"""API for a single keystone user."""
|
||||
url_regex = r'keystone/users/(?P<id>[0-9a-f]+|current)$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
@ -172,8 +167,7 @@ class User(generic.View):
|
||||
|
||||
@urls.register
|
||||
class Roles(generic.View):
|
||||
"""API over all roles.
|
||||
"""
|
||||
"""API over all roles."""
|
||||
url_regex = r'keystone/roles/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
@ -230,8 +224,7 @@ class Roles(generic.View):
|
||||
|
||||
@urls.register
|
||||
class Role(generic.View):
|
||||
"""API for a single role.
|
||||
"""
|
||||
"""API for a single role."""
|
||||
url_regex = r'keystone/roles/(?P<id>[0-9a-f]+|default)$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
@ -269,8 +262,7 @@ class Role(generic.View):
|
||||
|
||||
@urls.register
|
||||
class Domains(generic.View):
|
||||
"""API over all domains.
|
||||
"""
|
||||
"""API over all domains."""
|
||||
url_regex = r'keystone/domains/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
@ -321,8 +313,7 @@ class Domains(generic.View):
|
||||
|
||||
@urls.register
|
||||
class Domain(generic.View):
|
||||
"""API over a single domains.
|
||||
"""
|
||||
"""API over a single domains."""
|
||||
url_regex = r'keystone/domains/(?P<id>[0-9a-f]+|default)$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
@ -477,8 +468,7 @@ class Project(generic.View):
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request, id):
|
||||
"""Get a specific project by id.
|
||||
"""
|
||||
"""Get a specific project by id."""
|
||||
return api.keystone.tenant_get(request, id).to_dict()
|
||||
|
||||
@rest_utils.ajax()
|
||||
@ -532,16 +522,13 @@ class ServiceCatalog(generic.View):
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request):
|
||||
"""Return the Keystone service catalog associated with the current
|
||||
user.
|
||||
"""
|
||||
"""Return the service catalog associated with the current user."""
|
||||
return request.user.service_catalog
|
||||
|
||||
|
||||
@urls.register
|
||||
class UserSession(generic.View):
|
||||
"""API for a single keystone user.
|
||||
"""
|
||||
"""API for a single keystone user."""
|
||||
url_regex = r'keystone/user-session/$'
|
||||
allowed_fields = {
|
||||
'available_services_regions',
|
||||
@ -561,8 +548,7 @@ class UserSession(generic.View):
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request):
|
||||
"""Get the current user session.
|
||||
"""
|
||||
"""Get the current user session."""
|
||||
res = {k: getattr(request.user, k, None) for k in self.allowed_fields}
|
||||
if getattr(settings, 'ENABLE_CLIENT_TOKEN', True):
|
||||
res['token'] = request.user.token.id
|
||||
@ -571,14 +557,12 @@ class UserSession(generic.View):
|
||||
|
||||
@urls.register
|
||||
class Services(generic.View):
|
||||
"""API for keystone services.
|
||||
"""
|
||||
"""API for keystone services."""
|
||||
url_regex = r'keystone/services/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request):
|
||||
"""Get a list of keystone services.
|
||||
"""
|
||||
"""Get a list of keystone services."""
|
||||
region = request.user.services_region
|
||||
services = []
|
||||
for i, service in enumerate(request.user.service_catalog):
|
||||
@ -591,13 +575,13 @@ class Services(generic.View):
|
||||
|
||||
@urls.register
|
||||
class Groups(generic.View):
|
||||
"""API over all groups.
|
||||
"""
|
||||
"""API over all groups."""
|
||||
url_regex = r'keystone/groups/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request):
|
||||
"""Get a list of groups.
|
||||
|
||||
The listing result is an object with property "items".
|
||||
"""
|
||||
domain_context = request.session.get('domain_context')
|
||||
|
@ -13,8 +13,7 @@
|
||||
# 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.
|
||||
"""API for the network abstraction APIs.
|
||||
"""
|
||||
"""API for the network abstraction APIs."""
|
||||
|
||||
from django.views import generic
|
||||
|
||||
@ -49,8 +48,7 @@ class SecurityGroups(generic.View):
|
||||
|
||||
@urls.register
|
||||
class FloatingIP(generic.View):
|
||||
"""API for a single floating IP address.
|
||||
"""
|
||||
"""API for a single floating IP address."""
|
||||
url_regex = r'network/floatingip/$'
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
@ -84,8 +82,7 @@ class FloatingIP(generic.View):
|
||||
|
||||
@urls.register
|
||||
class FloatingIPs(generic.View):
|
||||
"""API for floating IP addresses.
|
||||
"""
|
||||
"""API for floating IP addresses."""
|
||||
url_regex = r'network/floatingips/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
@ -104,8 +101,7 @@ class FloatingIPs(generic.View):
|
||||
|
||||
@urls.register
|
||||
class FloatingIPPools(generic.View):
|
||||
"""API for floating IP pools.
|
||||
"""
|
||||
"""API for floating IP pools."""
|
||||
url_regex = r'network/floatingippools/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
|
@ -12,8 +12,7 @@
|
||||
# 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.
|
||||
"""API over the neutron service.
|
||||
"""
|
||||
"""API over the neutron service."""
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.views import generic
|
||||
@ -72,7 +71,8 @@ class Networks(generic.View):
|
||||
|
||||
@urls.register
|
||||
class Subnets(generic.View):
|
||||
"""API for Neutron SubNets
|
||||
"""API for Neutron Subnets
|
||||
|
||||
http://developer.openstack.org/api-ref-networking-v2.html#subnets
|
||||
"""
|
||||
url_regex = r'neutron/subnets/$'
|
||||
@ -119,6 +119,7 @@ class Subnets(generic.View):
|
||||
@urls.register
|
||||
class Ports(generic.View):
|
||||
"""API for Neutron Ports
|
||||
|
||||
http://developer.openstack.org/api-ref-networking-v2.html#ports
|
||||
"""
|
||||
url_regex = r'neutron/ports/$'
|
||||
@ -138,8 +139,7 @@ class Ports(generic.View):
|
||||
|
||||
@urls.register
|
||||
class Trunks(generic.View):
|
||||
"""API for neutron Trunks
|
||||
"""
|
||||
"""API for neutron Trunks"""
|
||||
url_regex = r'neutron/trunks/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
@ -155,14 +155,12 @@ class Trunks(generic.View):
|
||||
|
||||
@urls.register
|
||||
class Services(generic.View):
|
||||
"""API for Neutron agents
|
||||
"""
|
||||
"""API for Neutron agents"""
|
||||
url_regex = r'neutron/agents/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request):
|
||||
"""Get a list of agents
|
||||
"""
|
||||
"""Get a list of agents"""
|
||||
if api.base.is_service_enabled(request, 'network') and \
|
||||
api.neutron.is_extension_supported(request, 'agent'):
|
||||
result = api.neutron.agent_list(request, **request.GET)
|
||||
@ -173,8 +171,7 @@ class Services(generic.View):
|
||||
|
||||
@urls.register
|
||||
class Extensions(generic.View):
|
||||
"""API for neutron extensions.
|
||||
"""
|
||||
"""API for neutron extensions."""
|
||||
url_regex = r'neutron/extensions/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
@ -192,8 +189,7 @@ class Extensions(generic.View):
|
||||
|
||||
|
||||
class DefaultQuotaSets(generic.View):
|
||||
"""API for getting default quotas for neutron
|
||||
"""
|
||||
"""API for getting default quotas for neutron"""
|
||||
url_regex = r'neutron/quota-sets/defaults/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
@ -218,8 +214,7 @@ class DefaultQuotaSets(generic.View):
|
||||
|
||||
@urls.register
|
||||
class QuotasSets(generic.View):
|
||||
"""API for setting quotas of a given project.
|
||||
"""
|
||||
"""API for setting quotas of a given project."""
|
||||
url_regex = r'neutron/quotas-sets/(?P<project_id>[0-9a-f]+)$'
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
|
@ -11,8 +11,7 @@
|
||||
# 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.
|
||||
"""API over the nova service.
|
||||
"""
|
||||
"""API over the nova service."""
|
||||
from collections import OrderedDict
|
||||
|
||||
from django.http import HttpResponse
|
||||
@ -34,8 +33,7 @@ from openstack_dashboard.usage import quotas
|
||||
|
||||
@urls.register
|
||||
class Snapshots(generic.View):
|
||||
"""API for nova snapshots.
|
||||
"""
|
||||
"""API for nova snapshots."""
|
||||
url_regex = r'nova/snapshots/$'
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
@ -50,14 +48,12 @@ class Snapshots(generic.View):
|
||||
|
||||
@urls.register
|
||||
class Keypairs(generic.View):
|
||||
"""API for nova keypairs.
|
||||
"""
|
||||
"""API for nova keypairs."""
|
||||
url_regex = r'nova/keypairs/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request):
|
||||
"""Get a list of keypairs associated with the current logged-in
|
||||
account.
|
||||
"""Get a list of keypairs associated with the current logged-in user.
|
||||
|
||||
The listing result is an object with property "items".
|
||||
"""
|
||||
@ -130,13 +126,13 @@ class Keypair(generic.View):
|
||||
|
||||
@urls.register
|
||||
class Services(generic.View):
|
||||
"""API for nova services.
|
||||
"""
|
||||
"""API for nova services."""
|
||||
url_regex = r'nova/services/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request):
|
||||
"""Get a list of nova services.
|
||||
|
||||
Will return HTTP 501 status code if the service_list extension is
|
||||
not supported.
|
||||
"""
|
||||
@ -150,8 +146,7 @@ class Services(generic.View):
|
||||
|
||||
@urls.register
|
||||
class AvailabilityZones(generic.View):
|
||||
"""API for nova availability zones.
|
||||
"""
|
||||
"""API for nova availability zones."""
|
||||
url_regex = r'nova/availzones/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
@ -173,8 +168,7 @@ class AvailabilityZones(generic.View):
|
||||
|
||||
@urls.register
|
||||
class Limits(generic.View):
|
||||
"""API for nova limits.
|
||||
"""
|
||||
"""API for nova limits."""
|
||||
url_regex = r'nova/limits/$'
|
||||
|
||||
@rest_utils.ajax(json_encoder=json_encoder.NaNJSONEncoder)
|
||||
@ -199,8 +193,7 @@ class Limits(generic.View):
|
||||
|
||||
@urls.register
|
||||
class ServerActions(generic.View):
|
||||
"""API over all server actions.
|
||||
"""
|
||||
"""API over all server actions."""
|
||||
url_regex = r'nova/servers/(?P<server_id>[^/]+)/actions/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
@ -219,8 +212,7 @@ class ServerActions(generic.View):
|
||||
|
||||
@urls.register
|
||||
class SecurityGroups(generic.View):
|
||||
"""API over all server security groups.
|
||||
"""
|
||||
"""API over all server security groups."""
|
||||
url_regex = r'nova/servers/(?P<server_id>[^/]+)/security-groups/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
@ -239,8 +231,7 @@ class SecurityGroups(generic.View):
|
||||
|
||||
@urls.register
|
||||
class Volumes(generic.View):
|
||||
"""API over all server volumes.
|
||||
"""
|
||||
"""API over all server volumes."""
|
||||
url_regex = r'nova/servers/(?P<server_id>[^/]+)/volumes/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
@ -259,14 +250,12 @@ class Volumes(generic.View):
|
||||
|
||||
@urls.register
|
||||
class RemoteConsoleInfo(generic.View):
|
||||
"""API for remote console information.
|
||||
"""
|
||||
"""API for remote console information."""
|
||||
url_regex = r'nova/servers/(?P<server_id>[^/]+)/console-info/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def post(self, request, server_id):
|
||||
"""Gets information about an available remote console for the given
|
||||
server.
|
||||
"""Gets information of a remote console for the given server.
|
||||
|
||||
Example POST:
|
||||
http://localhost/api/nova/servers/abcd/console-info/
|
||||
@ -317,8 +306,7 @@ class RemoteConsoleInfo(generic.View):
|
||||
|
||||
@urls.register
|
||||
class ConsoleOutput(generic.View):
|
||||
"""API for console output.
|
||||
"""
|
||||
"""API for console output."""
|
||||
url_regex = r'nova/servers/(?P<server_id>[^/]+)/console-output/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
@ -339,8 +327,7 @@ class ConsoleOutput(generic.View):
|
||||
|
||||
@urls.register
|
||||
class Servers(generic.View):
|
||||
"""API over all servers.
|
||||
"""
|
||||
"""API over all servers."""
|
||||
url_regex = r'nova/servers/$'
|
||||
|
||||
_optional_create = [
|
||||
@ -415,8 +402,7 @@ class Servers(generic.View):
|
||||
|
||||
@urls.register
|
||||
class Server(generic.View):
|
||||
"""API for retrieving a single server
|
||||
"""
|
||||
"""API for retrieving a single server"""
|
||||
url_regex = r'nova/servers/(?P<server_id>[^/]+|default)$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
@ -429,8 +415,7 @@ class Server(generic.View):
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
def post(self, request, server_id):
|
||||
"""Perform a change to a server
|
||||
"""
|
||||
"""Perform a change to a server"""
|
||||
operation = request.DATA.get('operation', 'none')
|
||||
operations = {
|
||||
'stop': api.nova.server_stop,
|
||||
@ -451,8 +436,7 @@ class Server(generic.View):
|
||||
|
||||
@urls.register
|
||||
class ServerGroups(generic.View):
|
||||
"""API for nova server groups.
|
||||
"""
|
||||
"""API for nova server groups."""
|
||||
url_regex = r'nova/servergroups/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
@ -467,8 +451,7 @@ class ServerGroups(generic.View):
|
||||
|
||||
@urls.register
|
||||
class ServerMetadata(generic.View):
|
||||
"""API for server metadata.
|
||||
"""
|
||||
"""API for server metadata."""
|
||||
url_regex = r'nova/servers/(?P<server_id>[^/]+|default)/metadata$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
@ -496,8 +479,7 @@ class ServerMetadata(generic.View):
|
||||
|
||||
@urls.register
|
||||
class Extensions(generic.View):
|
||||
"""API for nova extensions.
|
||||
"""
|
||||
"""API for nova extensions."""
|
||||
url_regex = r'nova/extensions/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
@ -516,8 +498,7 @@ class Extensions(generic.View):
|
||||
|
||||
@urls.register
|
||||
class Flavors(generic.View):
|
||||
"""API for nova flavors.
|
||||
"""
|
||||
"""API for nova flavors."""
|
||||
url_regex = r'nova/flavors/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
@ -580,8 +561,7 @@ class Flavors(generic.View):
|
||||
|
||||
@urls.register
|
||||
class Flavor(generic.View):
|
||||
"""API for retrieving a single flavor
|
||||
"""
|
||||
"""API for retrieving a single flavor"""
|
||||
url_regex = r'nova/flavors/(?P<flavor_id>[^/]+)/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
@ -654,8 +634,7 @@ class Flavor(generic.View):
|
||||
|
||||
@urls.register
|
||||
class FlavorExtraSpecs(generic.View):
|
||||
"""API for managing flavor extra specs
|
||||
"""
|
||||
"""API for managing flavor extra specs"""
|
||||
url_regex = r'nova/flavors/(?P<flavor_id>[^/]+)/extra-specs/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
@ -684,8 +663,7 @@ class FlavorExtraSpecs(generic.View):
|
||||
|
||||
@urls.register
|
||||
class AggregateExtraSpecs(generic.View):
|
||||
"""API for managing aggregate extra specs
|
||||
"""
|
||||
"""API for managing aggregate extra specs"""
|
||||
url_regex = r'nova/aggregates/(?P<aggregate_id>[^/]+)/extra-specs/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
@ -712,8 +690,7 @@ class AggregateExtraSpecs(generic.View):
|
||||
|
||||
@urls.register
|
||||
class DefaultQuotaSets(generic.View):
|
||||
"""API for getting default quotas for nova
|
||||
"""
|
||||
"""API for getting default quotas for nova"""
|
||||
url_regex = r'nova/quota-sets/defaults/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
@ -771,8 +748,7 @@ class DefaultQuotaSets(generic.View):
|
||||
|
||||
@urls.register
|
||||
class EditableQuotaSets(generic.View):
|
||||
"""API for editable quotas.
|
||||
"""
|
||||
"""API for editable quotas."""
|
||||
url_regex = r'nova/quota-sets/editable/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
@ -791,8 +767,7 @@ class EditableQuotaSets(generic.View):
|
||||
|
||||
@urls.register
|
||||
class QuotaSets(generic.View):
|
||||
"""API for setting quotas for a given project.
|
||||
"""
|
||||
"""API for setting quotas for a given project."""
|
||||
url_regex = r'nova/quota-sets/(?P<project_id>[0-9a-f]+)$'
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
|
@ -11,8 +11,8 @@
|
||||
# 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.
|
||||
"""API for the swift service.
|
||||
"""
|
||||
"""API for the swift service."""
|
||||
|
||||
import os
|
||||
|
||||
from django import forms
|
||||
@ -31,22 +31,19 @@ from openstack_dashboard.api import swift
|
||||
|
||||
@urls.register
|
||||
class Info(generic.View):
|
||||
"""API for information about the Swift installation.
|
||||
"""
|
||||
"""API for information about the Swift installation."""
|
||||
url_regex = r'swift/info/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request):
|
||||
"""Get information about the Swift installation.
|
||||
"""
|
||||
"""Get information about the Swift installation."""
|
||||
capabilities = api.swift.swift_get_capabilities(request)
|
||||
return {'info': capabilities}
|
||||
|
||||
|
||||
@urls.register
|
||||
class Containers(generic.View):
|
||||
"""API for swift container listing for an account
|
||||
"""
|
||||
"""API for swift container listing for an account"""
|
||||
url_regex = r'swift/containers/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
@ -71,15 +68,13 @@ class Containers(generic.View):
|
||||
|
||||
@urls.register
|
||||
class Container(generic.View):
|
||||
"""API for swift container level information
|
||||
"""
|
||||
"""API for swift container level information"""
|
||||
|
||||
url_regex = r'swift/containers/(?P<container>[^/]+)/metadata/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request, container):
|
||||
"""Get the container details
|
||||
"""
|
||||
"""Get the container details"""
|
||||
return api.swift.swift_get_container(request, container).to_dict()
|
||||
|
||||
@rest_utils.ajax()
|
||||
@ -117,8 +112,7 @@ class Container(generic.View):
|
||||
|
||||
@urls.register
|
||||
class Objects(generic.View):
|
||||
"""API for a list of swift objects
|
||||
"""
|
||||
"""API for a list of swift objects"""
|
||||
url_regex = r'swift/containers/(?P<container>[^/]+)/objects/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
@ -158,8 +152,7 @@ class UploadObjectForm(forms.Form):
|
||||
|
||||
@urls.register
|
||||
class Object(generic.View):
|
||||
"""API for a single swift object or pseudo-folder
|
||||
"""
|
||||
"""API for a single swift object or pseudo-folder"""
|
||||
url_regex = r'swift/containers/(?P<container>[^/]+)/object/' \
|
||||
'(?P<object_name>.+)$'
|
||||
|
||||
@ -219,8 +212,7 @@ class Object(generic.View):
|
||||
api.swift.swift_delete_object(request, container, object_name)
|
||||
|
||||
def get(self, request, container, object_name):
|
||||
"""Get the object contents.
|
||||
"""
|
||||
"""Get the object contents."""
|
||||
obj = api.swift.swift_get_object(
|
||||
request,
|
||||
container,
|
||||
@ -245,8 +237,7 @@ class Object(generic.View):
|
||||
|
||||
@urls.register
|
||||
class ObjectMetadata(generic.View):
|
||||
"""API for a single swift object
|
||||
"""
|
||||
"""API for a single swift object"""
|
||||
url_regex = r'swift/containers/(?P<container>[^/]+)/metadata/' \
|
||||
'(?P<object_name>.+)$'
|
||||
|
||||
@ -262,8 +253,7 @@ class ObjectMetadata(generic.View):
|
||||
|
||||
@urls.register
|
||||
class ObjectCopy(generic.View):
|
||||
"""API to copy a swift object
|
||||
"""
|
||||
"""API to copy a swift object"""
|
||||
url_regex = r'swift/containers/(?P<container>[^/]+)/copy/' \
|
||||
'(?P<object_name>.+)$'
|
||||
|
||||
|
@ -20,13 +20,13 @@ urlpatterns = []
|
||||
# @register below, and the import the endpoint module in the
|
||||
# rest_api/__init__.py module
|
||||
def register(view):
|
||||
'''Register API views to respond to a regex pattern (url_regex on the
|
||||
view class).
|
||||
"""Register API views to respond to a regex pattern.
|
||||
|
||||
``url_regex`` on a wrapped view class is used as the regex pattern.
|
||||
The view should be a standard Django class-based view implementing an
|
||||
as_view() method. The url_regex attribute of the view should be a standard
|
||||
Django URL regex pattern.
|
||||
'''
|
||||
"""
|
||||
p = urls.url(view.url_regex, view.as_view())
|
||||
urlpatterns.append(p)
|
||||
return view
|
||||
|
@ -75,7 +75,9 @@ class JSONResponse(_RestResponse):
|
||||
|
||||
def ajax(authenticated=True, data_required=False,
|
||||
json_encoder=json.JSONEncoder):
|
||||
'''Provide a decorator to wrap a view method so that it may exist in an
|
||||
"""Decorator to allow the wrappered view to exist in an AJAX environment.
|
||||
|
||||
Provide a decorator to wrap a view method so that it may exist in an
|
||||
entirely AJAX environment:
|
||||
|
||||
- data decoded from JSON as input and data coded as JSON as output
|
||||
@ -98,7 +100,7 @@ def ajax(authenticated=True, data_required=False,
|
||||
|
||||
Methods returning nothing (or None explicitly) will result in a 204 "NO
|
||||
CONTENT" being returned to the caller.
|
||||
'''
|
||||
"""
|
||||
def decorator(function, authenticated=authenticated,
|
||||
data_required=data_required):
|
||||
@functools.wraps(function,
|
||||
@ -173,7 +175,9 @@ def parse_filters_kwargs(request, client_keywords=None):
|
||||
|
||||
|
||||
def post2data(func):
|
||||
"""The sole purpose of this decorator is to restore original form values
|
||||
"""Decorator to restore original form values along with their types.
|
||||
|
||||
The sole purpose of this decorator is to restore original form values
|
||||
along with their types stored on client-side under key $$originalJSON.
|
||||
This in turn prevents the loss of field types when they are passed with
|
||||
header 'Content-Type: multipart/form-data', which is needed to pass a
|
||||
|
@ -71,9 +71,9 @@ class HypervisorViewTest(test.BaseAdminViewTests):
|
||||
'hypervisor_stats',
|
||||
'service_list')})
|
||||
def test_service_list_unavailable(self):
|
||||
"""test that error message should be returned when
|
||||
nova.service_list isn't available
|
||||
"""
|
||||
# test that error message should be returned when
|
||||
# nova.service_list isn't available.
|
||||
|
||||
hypervisors = self.hypervisors.list()
|
||||
stats = self.hypervisors.stats
|
||||
api.nova.hypervisor_list(IsA(http.HttpRequest)).AndReturn(hypervisors)
|
||||
|
@ -45,9 +45,8 @@ class ImageURLField(forms.URLField):
|
||||
|
||||
|
||||
def create_image_metadata(data):
|
||||
"""Use the given dict of image form data to generate the metadata used for
|
||||
creating the image in glance.
|
||||
"""
|
||||
"""Generate metadata dict for a new image from a given form data."""
|
||||
|
||||
# Glance does not really do anything with container_format at the
|
||||
# moment. It requires it is set to the same disk_format for the three
|
||||
# Amazon image types, otherwise it just treats them as 'bare.' As such
|
||||
|
@ -19,13 +19,12 @@ from openstack_dashboard.api import glance
|
||||
|
||||
|
||||
def get_available_images(request, project_id=None, images_cache=None):
|
||||
"""Returns a list of images that are public or owned by the given
|
||||
project_id. If project_id is not specified, only public images
|
||||
are returned.
|
||||
"""Returns a list of images that are public or owned by the given project.
|
||||
|
||||
If project_id is not specified, only public images are returned.
|
||||
|
||||
:param images_cache: An optional dict-like object in which to
|
||||
cache public and per-project id image metadata.
|
||||
|
||||
"""
|
||||
if images_cache is None:
|
||||
images_cache = {}
|
||||
|
@ -101,9 +101,6 @@ class DeleteInstance(policy.PolicyTargetMixin, tables.DeleteAction):
|
||||
)
|
||||
|
||||
def allowed(self, request, instance=None):
|
||||
"""Allow delete action if instance is in error state or not currently
|
||||
being deleted.
|
||||
"""
|
||||
error_state = False
|
||||
if instance:
|
||||
error_state = (instance.status == 'ERROR')
|
||||
|
@ -3920,9 +3920,9 @@ class InstanceTests(helpers.ResetImageAPIVersionMixin, helpers.TestCase):
|
||||
api.network: ('servers_update_addresses',),
|
||||
})
|
||||
def test_index_form_action_with_pagination(self):
|
||||
"""The form action on the next page should have marker
|
||||
object from the previous page last element.
|
||||
"""
|
||||
# The form action on the next page should have marker
|
||||
# object from the previous page last element.
|
||||
|
||||
page_size = getattr(settings, 'API_RESULT_PAGE_SIZE', 2)
|
||||
servers = self.servers.list()[:3]
|
||||
|
||||
@ -3984,9 +3984,8 @@ class InstanceTests(helpers.ResetImageAPIVersionMixin, helpers.TestCase):
|
||||
api.glance: ('image_list_detailed',),
|
||||
api.network: ('servers_update_addresses',)})
|
||||
def test_delete_instance_with_pagination(self):
|
||||
"""Instance should be deleted from
|
||||
the next page.
|
||||
"""
|
||||
# Instance should be deleted from the next page.
|
||||
|
||||
page_size = getattr(settings, 'API_RESULT_PAGE_SIZE', 2)
|
||||
servers = self.servers.list()[:3]
|
||||
server = servers[-1]
|
||||
|
@ -35,10 +35,10 @@ def flavor_list(request):
|
||||
|
||||
def sort_flavor_list(request, flavors):
|
||||
"""Utility method to sort a list of flavors.
|
||||
By default, returns the available flavors, sorted by RAM
|
||||
usage (ascending). Override these behaviours with a
|
||||
CREATE_INSTANCE_FLAVOR_SORT dict
|
||||
in local_settings.py.
|
||||
|
||||
By default, returns the available flavors, sorted by RAM usage (ascending).
|
||||
Override these behaviours with a ``CREATE_INSTANCE_FLAVOR_SORT`` dict
|
||||
in ``local_settings.py``.
|
||||
"""
|
||||
def get_key(flavor, sort_key):
|
||||
try:
|
||||
|
@ -85,8 +85,10 @@ console_invalid_status = {
|
||||
|
||||
|
||||
class TranslationHelper(object):
|
||||
"""Helper class to provide the translations of instances, networks,
|
||||
routers and ports from other parts of the code to the network topology
|
||||
"""Helper class to provide the translations.
|
||||
|
||||
This allows the network topology to access the translated strings
|
||||
for various resources defined in other parts of the code.
|
||||
"""
|
||||
def __init__(self):
|
||||
# turn translation tuples into dicts for easy access
|
||||
|
@ -228,8 +228,7 @@ class SecurityGroupsViewTests(test.TestCase):
|
||||
self._create_security_group(sec_group)
|
||||
|
||||
def test_create_security_groups_special_chars(self):
|
||||
"""Ensure that a group name is not restricted to alphanumeric
|
||||
characters.
|
||||
"""Ensure non-alphanumeric characters can be used as a group name.
|
||||
|
||||
bug #1233501 Security group names cannot contain at characters
|
||||
bug #1224576 Security group names cannot contain spaces
|
||||
|
@ -566,9 +566,6 @@ class DetachVolume(tables.BatchAction):
|
||||
|
||||
|
||||
class AttachedInstanceColumn(tables.WrappingColumn):
|
||||
"""Customized column class that does complex processing on the attachments
|
||||
for a volume instance.
|
||||
"""
|
||||
def get_raw_data(self, attachment):
|
||||
request = self.table.request
|
||||
return safestring.mark_safe(get_attachment_name(request, attachment))
|
||||
|
@ -93,8 +93,9 @@ class BaseWebObject(unittest.TestCase):
|
||||
self.driver.implicitly_wait(self.conf.selenium.implicit_wait)
|
||||
|
||||
def _wait_until(self, predicate, timeout=None, poll_frequency=0.5):
|
||||
"""Wait until the value returned by predicate is not False or
|
||||
the timeout is elapsed.
|
||||
"""Wait until the value returned by predicate is not False.
|
||||
|
||||
It also returns when the timeout is elapsed.
|
||||
'predicate' takes the driver as argument.
|
||||
"""
|
||||
if not timeout:
|
||||
@ -103,10 +104,12 @@ class BaseWebObject(unittest.TestCase):
|
||||
predicate)
|
||||
|
||||
def _wait_till_text_present_in_element(self, element, texts, timeout=None):
|
||||
"""Waiting for a text to appear in a certain element very often is
|
||||
actually waiting for a _different_ element with a different text to
|
||||
appear in place of an old element. So a way to avoid capturing stale
|
||||
element reference should be provided for this use case.
|
||||
"""Waiting for a text to appear in a certain element.
|
||||
|
||||
Most frequent usage is actually to wait for a _different_ element
|
||||
with a different text to appear in place of an old element.
|
||||
So a way to avoid capturing stale element reference should be provided
|
||||
for this use case.
|
||||
|
||||
Better to wrap getting entity status cell in a lambda
|
||||
to avoid problems with cell being replaced with totally different
|
||||
|
@ -32,7 +32,9 @@ def _is_test_cls(cls):
|
||||
|
||||
|
||||
def _mark_method_skipped(meth, reason):
|
||||
"""Mark method as skipped by replacing the actual method with wrapper
|
||||
"""Decorate to mark method as skipped.
|
||||
|
||||
This marks method as skipped by replacing the actual method with wrapper
|
||||
that raises the testtools.testcase.TestSkipped exception.
|
||||
"""
|
||||
|
||||
@ -71,8 +73,9 @@ def _get_skip_method(obj):
|
||||
|
||||
|
||||
def services_required(*req_services):
|
||||
"""Decorator for marking test's service requirements,
|
||||
if requirements are not met in the configuration file
|
||||
"""Decorator for marking test's service requirements.
|
||||
|
||||
If requirements are not met in the configuration file
|
||||
test is marked as skipped.
|
||||
|
||||
Usage:
|
||||
@ -110,8 +113,9 @@ def services_required(*req_services):
|
||||
|
||||
|
||||
def _parse_compound_config_option_value(option_name):
|
||||
"""Parses the value of a given config option where option's section name is
|
||||
separated from option name by '.'.
|
||||
"""Parses the value of a given config option.
|
||||
|
||||
The section name of the option is separated from option name by '.'.
|
||||
"""
|
||||
name_parts = option_name.split('.')
|
||||
name_parts.reverse()
|
||||
@ -163,8 +167,7 @@ def skip_because(**kwargs):
|
||||
|
||||
|
||||
def attach_video(func):
|
||||
"""Notify test runner to attach test video in any case
|
||||
"""
|
||||
"""Notify test runner to attach test video in any case"""
|
||||
|
||||
@functools.wraps(func)
|
||||
def wrapper(self, *args, **kwgs):
|
||||
|
@ -200,8 +200,9 @@ class BaseTestCase(testtools.TestCase):
|
||||
super(BaseTestCase, self).addOnException(wrapped_handler)
|
||||
|
||||
def _configure_log(self):
|
||||
"""Configure log to capture test logs include selenium logs in order
|
||||
to attach them if test will be broken.
|
||||
"""Configure log to capture test logs include selenium logs.
|
||||
|
||||
This allows us to attach them if test will be broken.
|
||||
"""
|
||||
# clear other handlers to set target handler
|
||||
ROOT_LOGGER.handlers[:] = []
|
||||
@ -276,8 +277,10 @@ class BaseTestCase(testtools.TestCase):
|
||||
return rec(_log)
|
||||
|
||||
def zoom_out(self, times=3):
|
||||
"""Zooming out prevents different elements being driven out of xvfb
|
||||
viewport (which in Selenium>=2.50.1 prevents interaction with them.
|
||||
"""Zooming out a specified element.
|
||||
|
||||
It prevents different elements being driven out of xvfb viewport
|
||||
(which in Selenium>=2.50.1 prevents interaction with them).
|
||||
"""
|
||||
html = self.driver.find_element(by.By.TAG_NAME, 'html')
|
||||
html.send_keys(keys.Keys.NULL)
|
||||
|
@ -61,6 +61,7 @@ class MetadatadefinitionsPage(basepage.BaseNavigationPage):
|
||||
|
||||
def json_load_template(self, namespace_template_name):
|
||||
"""Read template for namespace creation
|
||||
|
||||
:param namespace_template_name: Path to template
|
||||
:return = json data container
|
||||
"""
|
||||
|
@ -56,6 +56,7 @@ class PageObject(basewebobject.BaseWebObject):
|
||||
|
||||
def switch_window(self, window_name=None, window_index=None):
|
||||
"""Switches focus between the webdriver windows.
|
||||
|
||||
Args:
|
||||
- window_name: The name of the window to switch to.
|
||||
- window_index: The index of the window handle to switch to.
|
||||
|
@ -42,15 +42,15 @@ class BaseRegion(basewebobject.BaseWebObject):
|
||||
self._dynamic_properties = {}
|
||||
|
||||
def __getattr__(self, name):
|
||||
"""It is not possible to create property bounded just to object
|
||||
and not class at runtime, therefore it is necessary to
|
||||
override __getattr__ and make fake 'properties' by storing them in
|
||||
the protected attribute _dynamic_attributes and returning result
|
||||
of the method associated with the specified attribute.
|
||||
# It is not possible to create property bounded just to object
|
||||
# and not class at runtime, therefore it is necessary to
|
||||
# override __getattr__ and make fake 'properties' by storing them in
|
||||
# the protected attribute _dynamic_attributes and returning result
|
||||
# of the method associated with the specified attribute.
|
||||
|
||||
# This way the feeling of having regions accessed as 'properties'
|
||||
# is created, which is one of the requirement of page object pattern.
|
||||
|
||||
This way the feeling of having regions accessed as 'properties'
|
||||
is created, which is one of the requirement of page object pattern.
|
||||
"""
|
||||
try:
|
||||
return self._dynamic_properties[name]
|
||||
except KeyError:
|
||||
|
@ -287,9 +287,8 @@ class BaseFormRegion(baseregion.BaseRegion):
|
||||
_default_form_locator = (by.By.CSS_SELECTOR, 'div.modal-dialog')
|
||||
|
||||
def __init__(self, driver, conf, src_elem=None):
|
||||
"""In most cases forms can be located through _default_form_locator,
|
||||
so specifying source element can be skipped.
|
||||
"""
|
||||
# In most cases forms can be located through _default_form_locator,
|
||||
# so specifying source element can be skipped.
|
||||
if src_elem is None:
|
||||
# fake self.src_elem must be set up in order self._get_element work
|
||||
self.src_elem = driver
|
||||
@ -449,9 +448,10 @@ class TabbedFormRegion(FormRegion):
|
||||
|
||||
|
||||
class DateFormRegion(BaseFormRegion):
|
||||
"""Form that queries data to table that is regularly below the form,
|
||||
typical example is located on Project/Compute/Overview page.
|
||||
"""
|
||||
"""Form that queries data to table that is regularly below the form.
|
||||
|
||||
A typical example is located on Project/Compute/Overview page.
|
||||
"""
|
||||
|
||||
_from_field_locator = (by.By.CSS_SELECTOR, 'input#id_start')
|
||||
_to_field_locator = (by.By.CSS_SELECTOR, 'input#id_end')
|
||||
|
@ -221,8 +221,9 @@ class DropDownMenuRegion(baseregion.BaseRegion):
|
||||
|
||||
|
||||
class UserDropDownMenuRegion(DropDownMenuRegion):
|
||||
"""Drop down menu located in the right side of the topbar,
|
||||
contains links to settings and help.
|
||||
"""Drop down menu located in the right side of the topbar.
|
||||
|
||||
This menu contains links to settings and help.
|
||||
"""
|
||||
_settings_link_locator = (by.By.CSS_SELECTOR,
|
||||
'a[href*="/settings/"]')
|
||||
|
@ -208,6 +208,7 @@ class TableRegion(baseregion.BaseRegion):
|
||||
|
||||
def assert_definition(self, expected_table_definition, sorting=False):
|
||||
"""Checks that actual table is expected one.
|
||||
|
||||
Items to compare: 'next' and 'prev' links, count of rows and names of
|
||||
elements in list
|
||||
:param expected_table_definition: expected values (dictionary)
|
||||
@ -225,8 +226,7 @@ class TableRegion(baseregion.BaseRegion):
|
||||
|
||||
|
||||
def bind_table_action(action_name):
|
||||
"""A decorator to bind table region method to an actual table action
|
||||
button.
|
||||
"""Decorator to bind table region method to an actual table action button.
|
||||
|
||||
Many table actions when started (by clicking a corresponding button
|
||||
in UI) lead to some form showing up. To further interact with this form,
|
||||
|
@ -43,6 +43,7 @@ class TestDownloadRCFile(helpers.AdminTestCase):
|
||||
|
||||
def test_download_rc_v2_file(self):
|
||||
"""This is a basic scenario test:
|
||||
|
||||
Steps:
|
||||
1) Login to Horizon Dashboard as admin user
|
||||
2) Navigate to Project > Compute > Access & Security > API Access tab
|
||||
@ -62,6 +63,7 @@ class TestDownloadRCFile(helpers.AdminTestCase):
|
||||
@decorators.skip_because(bugs=['1584057'])
|
||||
def test_download_rc_v3_file(self):
|
||||
"""This is a basic scenario test:
|
||||
|
||||
Steps:
|
||||
1) Login to Horizon Dashboard as admin user
|
||||
2) Navigate to Project > Compute > Access & Security > API Access tab
|
||||
|
@ -25,6 +25,7 @@ class TestDefaults(helpers.AdminTestCase):
|
||||
|
||||
def test_update_defaults(self):
|
||||
"""Tests the Update Default Quotas functionality:
|
||||
|
||||
1) Login as Admin and go to Admin > System > Defaults
|
||||
2) Updates default Quotas by adding a random number between 1 and 10
|
||||
3) Verifies that the updated values are present in the
|
||||
|
@ -80,6 +80,7 @@ class TestFlavors(helpers.AdminTestCase):
|
||||
|
||||
def test_flavor_create(self):
|
||||
"""tests the flavor creation and deletion functionalities:
|
||||
|
||||
* creates a new flavor
|
||||
* verifies the flavor appears in the flavors table
|
||||
* deletes the newly created flavor
|
||||
@ -89,8 +90,7 @@ class TestFlavors(helpers.AdminTestCase):
|
||||
self._delete_flavor(self.FLAVOR_NAME)
|
||||
|
||||
def test_flavor_update_info(self):
|
||||
"""Tests the flavor Edit row action functionality:
|
||||
"""
|
||||
"""Tests the flavor Edit row action functionality"""
|
||||
|
||||
self._create_flavor(self.FLAVOR_NAME)
|
||||
|
||||
|
@ -20,6 +20,7 @@ class TestHostAggregates(helpers.AdminTestCase):
|
||||
|
||||
def test_host_aggregate_create(self):
|
||||
"""tests the host aggregate creation and deletion functionalities:
|
||||
|
||||
* creates a new host aggregate
|
||||
* verifies the host aggregate appears in the host aggregates table
|
||||
* deletes the newly created host aggregate
|
||||
|
@ -74,6 +74,7 @@ class TestImagesBasic(TestImagesLegacy):
|
||||
@decorators.skip_because(bugs=['1595335'])
|
||||
def test_image_create_delete(self):
|
||||
"""tests the image creation and deletion functionalities:
|
||||
|
||||
* creates a new image from horizon.conf http_image
|
||||
* verifies the image appears in the images table as active
|
||||
* deletes the newly created image
|
||||
@ -84,6 +85,7 @@ class TestImagesBasic(TestImagesLegacy):
|
||||
|
||||
def test_image_create_delete_from_local_file(self):
|
||||
"""tests the image creation and deletion functionalities:
|
||||
|
||||
* downloads image from horizon.conf stated in http_image
|
||||
* creates the image from the downloaded file
|
||||
* verifies the image appears in the images table as active
|
||||
@ -96,20 +98,21 @@ class TestImagesBasic(TestImagesLegacy):
|
||||
|
||||
def test_images_pagination(self):
|
||||
"""This test checks images pagination
|
||||
Steps:
|
||||
1) Login to Horizon Dashboard as horizon user
|
||||
2) Navigate to user settings page
|
||||
3) Change 'Items Per Page' value to 1
|
||||
4) Go to Project -> Compute -> Images page
|
||||
5) Check that only 'Next' link is available, only one image is
|
||||
available (and it has correct name)
|
||||
6) Click 'Next' and check that both 'Prev' and 'Next' links are
|
||||
available, only one image is available (and it has correct name)
|
||||
7) Click 'Next' and check that only 'Prev' link is available,
|
||||
only one image is visible (and it has correct name)
|
||||
8) Click 'Prev' and check results (should be the same as for step6)
|
||||
9) Click 'Prev' and check results (should be the same as for step5)
|
||||
10) Go to user settings page and restore 'Items Per Page'
|
||||
|
||||
Steps:
|
||||
1) Login to Horizon Dashboard as horizon user
|
||||
2) Navigate to user settings page
|
||||
3) Change 'Items Per Page' value to 1
|
||||
4) Go to Project -> Compute -> Images page
|
||||
5) Check that only 'Next' link is available, only one image is
|
||||
available (and it has correct name)
|
||||
6) Click 'Next' and check that both 'Prev' and 'Next' links are
|
||||
available, only one image is available (and it has correct name)
|
||||
7) Click 'Next' and check that only 'Prev' link is available,
|
||||
only one image is visible (and it has correct name)
|
||||
8) Click 'Prev' and check results (should be the same as for step6)
|
||||
9) Click 'Prev' and check results (should be the same as for step5)
|
||||
10) Go to user settings page and restore 'Items Per Page'
|
||||
"""
|
||||
default_image_list = self.CONFIG.image.images_list
|
||||
items_per_page = 1
|
||||
@ -148,6 +151,7 @@ class TestImagesBasic(TestImagesLegacy):
|
||||
|
||||
def test_update_image_metadata(self):
|
||||
"""Test update image metadata
|
||||
|
||||
* logs in as admin user
|
||||
* creates image from locally downloaded file
|
||||
* verifies the image appears in the images table as active
|
||||
@ -176,6 +180,7 @@ class TestImagesBasic(TestImagesLegacy):
|
||||
|
||||
def test_remove_protected_image(self):
|
||||
"""tests that protected image is not deletable
|
||||
|
||||
* logs in as admin user
|
||||
* creates image from locally downloaded file
|
||||
* verifies the image appears in the images table as active
|
||||
@ -215,6 +220,7 @@ class TestImagesBasic(TestImagesLegacy):
|
||||
|
||||
def test_edit_image_description_and_name(self):
|
||||
"""tests that image description is editable
|
||||
|
||||
* creates image from locally downloaded file
|
||||
* verifies the image appears in the images table as active
|
||||
* toggle edit action and adds some description
|
||||
@ -263,12 +269,13 @@ class TestImagesAdvanced(TestImagesLegacy):
|
||||
"""Login as demo user"""
|
||||
def test_create_volume_from_image(self):
|
||||
"""This test case checks create volume from image functionality:
|
||||
Steps:
|
||||
1. Login to Horizon Dashboard as regular user
|
||||
2. Navigate to Project -> Compute -> Images
|
||||
3. Create new volume from image
|
||||
4. Check that volume is created with expected name
|
||||
5. Check that volume status is Available
|
||||
|
||||
Steps:
|
||||
1. Login to Horizon Dashboard as regular user
|
||||
2. Navigate to Project -> Compute -> Images
|
||||
3. Create new volume from image
|
||||
4. Check that volume is created with expected name
|
||||
5. Check that volume status is Available
|
||||
"""
|
||||
images_page = self.images_page
|
||||
source_image = self.CONFIG.image.images_list[0]
|
||||
@ -290,13 +297,14 @@ class TestImagesAdvanced(TestImagesLegacy):
|
||||
|
||||
def test_launch_instance_from_image(self):
|
||||
"""This test case checks launch instance from image functionality:
|
||||
Steps:
|
||||
1. Login to Horizon Dashboard as regular user
|
||||
2. Navigate to Project -> Compute -> Images
|
||||
3. Launch new instance from image
|
||||
4. Check that instance is create
|
||||
5. Check that status of newly created instance is Active
|
||||
6. Check that image_name in correct in instances table
|
||||
|
||||
Steps:
|
||||
1. Login to Horizon Dashboard as regular user
|
||||
2. Navigate to Project -> Compute -> Images
|
||||
3. Launch new instance from image
|
||||
4. Check that instance is create
|
||||
5. Check that status of newly created instance is Active
|
||||
6. Check that image_name in correct in instances table
|
||||
"""
|
||||
images_page = self.images_page
|
||||
source_image = self.CONFIG.image.images_list[0]
|
||||
@ -331,15 +339,16 @@ class TestImagesAdmin(helpers.AdminTestCase, TestImagesLegacy):
|
||||
|
||||
def test_filter_images(self):
|
||||
"""This test checks filtering of images
|
||||
Steps:
|
||||
1) Login to Horizon dashboard as admin user
|
||||
2) Go to Admin -> System -> Images
|
||||
3) Use filter by Image Name
|
||||
4) Check that filtered table has one image only (which name is
|
||||
equal to filter value)
|
||||
5) Check that no other images in the table
|
||||
6) Clear filter and set nonexistent image name. Check that 0 rows
|
||||
are displayed
|
||||
|
||||
Steps:
|
||||
1) Login to Horizon dashboard as admin user
|
||||
2) Go to Admin -> System -> Images
|
||||
3) Use filter by Image Name
|
||||
4) Check that filtered table has one image only (which name is
|
||||
equal to filter value)
|
||||
5) Check that no other images in the table
|
||||
6) Clear filter and set nonexistent image name. Check that 0 rows
|
||||
are displayed
|
||||
"""
|
||||
images_list = self.CONFIG.image.images_list
|
||||
images_page = self.images_page
|
||||
|
@ -24,6 +24,7 @@ class TestInstances(helpers.TestCase):
|
||||
|
||||
def test_create_delete_instance(self):
|
||||
"""tests the instance creation and deletion functionality:
|
||||
|
||||
* creates a new instance in Project > Compute > Instances page
|
||||
* verifies the instance appears in the instances table as active
|
||||
* deletes the newly created instance via proper page (depends on user)
|
||||
@ -49,6 +50,7 @@ class TestInstances(helpers.TestCase):
|
||||
@decorators.skip_because(bugs=['1584057'])
|
||||
def test_instances_pagination(self):
|
||||
"""This test checks instance pagination
|
||||
|
||||
Steps:
|
||||
1) Login to Horizon Dashboard as regular user
|
||||
2) Navigate to user settings page
|
||||
@ -57,9 +59,9 @@ class TestInstances(helpers.TestCase):
|
||||
5) Create 2 instances
|
||||
6) Go to appropriate page (depends on user)
|
||||
7) Check that only 'Next' link is available, only one instance is
|
||||
available (and it has correct name) on the first page
|
||||
available (and it has correct name) on the first page
|
||||
8) Click 'Next' and check that on the second page only one instance is
|
||||
available (and it has correct name), there is no 'Next' link on page
|
||||
available (and it has correct name), there is no 'Next' link on page
|
||||
9) Go to user settings page and restore 'Items Per Page'
|
||||
10) Delete created instances via proper page (depends on user)
|
||||
"""
|
||||
@ -110,6 +112,7 @@ class TestInstances(helpers.TestCase):
|
||||
|
||||
def test_instances_pagination_and_filtration(self):
|
||||
"""This test checks instance pagination and filtration
|
||||
|
||||
Steps:
|
||||
1) Login to Horizon Dashboard as regular user
|
||||
2) Go to to user settings page
|
||||
@ -118,11 +121,11 @@ class TestInstances(helpers.TestCase):
|
||||
5) Create 2 instances
|
||||
6) Go to appropriate page (depends on user)
|
||||
7) Check filter by Name of the first and the second instance in order
|
||||
to have one instance in the list (and it should have correct name) and
|
||||
no 'Next' link is available
|
||||
to have one instance in the list (and it should have correct name)
|
||||
and no 'Next' link is available
|
||||
8) Check filter by common part of Name of in order to have one instance
|
||||
in the list (and it should have correct name) and 'Next' link is
|
||||
available on the first page and is not available on the second page
|
||||
in the list (and it should have correct name) and 'Next' link is
|
||||
available on the first page and is not available on the second page
|
||||
9) Go to user settings page and restore 'Items Per Page'
|
||||
10) Delete created instances via proper page (depends on user)
|
||||
|
||||
@ -181,6 +184,7 @@ class TestInstances(helpers.TestCase):
|
||||
|
||||
def test_filter_instances(self):
|
||||
"""This test checks filtering of instances by Instance Name
|
||||
|
||||
Steps:
|
||||
1) Login to Horizon dashboard as regular user
|
||||
2) Go to Project > Compute > Instances
|
||||
@ -188,9 +192,9 @@ class TestInstances(helpers.TestCase):
|
||||
4) Go to appropriate page (depends on user)
|
||||
5) Use filter by Instance Name
|
||||
6) Check that filtered table has one instance only (which name is equal
|
||||
to filter value) and no other instances in the table
|
||||
to filter value) and no other instances in the table
|
||||
7) Check that filtered table has both instances (search by common part
|
||||
of instance names)
|
||||
of instance names)
|
||||
8) Set nonexistent instance name. Check that 0 rows are displayed
|
||||
9) Clear filter and delete instances via proper page (depends on user)
|
||||
"""
|
||||
|
@ -16,6 +16,7 @@ from openstack_dashboard.test.integration_tests.pages import loginpage
|
||||
|
||||
class TestLogin(helpers.BaseTestCase):
|
||||
"""This is a basic scenario test:
|
||||
|
||||
* checks that the login page is available
|
||||
* logs in as a regular user
|
||||
* checks that the user home page loads without error
|
||||
|
@ -31,6 +31,7 @@ class TestMetadataDefinitions(helpers.AdminTestCase):
|
||||
is_public=True, is_protected=False, template_path=None,
|
||||
checks=(PUBLIC, PROTECTED)):
|
||||
"""Create NameSpace and run checks
|
||||
|
||||
:param namespace_name: Display name of namespace in template
|
||||
:param page: Connection point
|
||||
:param template_json_container: JSON container with NameSpace content
|
||||
@ -71,6 +72,7 @@ class TestMetadataDefinitions(helpers.AdminTestCase):
|
||||
|
||||
def namespace_delete_with_checks(self, namespace_name, page):
|
||||
"""Delete NameSpace and run checks
|
||||
|
||||
:param namespace_name: Display name of namespace in template
|
||||
:param page: Connection point
|
||||
:return: Nothing
|
||||
@ -83,6 +85,7 @@ class TestMetadataDefinitions(helpers.AdminTestCase):
|
||||
|
||||
def test_namespace_create_delete(self):
|
||||
"""Tests the NameSpace creation and deletion functionality:
|
||||
|
||||
* Actions:
|
||||
* 1) Login to Horizon Dashboard as admin user.
|
||||
* 2) Navigate to Admin -> System -> Metadata Definitions.
|
||||
|
@ -23,6 +23,7 @@ class TestNetworks(helpers.TestCase):
|
||||
|
||||
def test_private_network_create(self):
|
||||
"""tests the network creation and deletion functionalities:
|
||||
|
||||
* creates a new private network and a new subnet associated with it
|
||||
* verifies the network appears in the networks table as active
|
||||
* deletes the newly created network
|
||||
|
@ -44,6 +44,7 @@ class TestRouters(helpers.TestCase):
|
||||
|
||||
def test_router_create(self):
|
||||
"""tests the router creation and deletion functionalities:
|
||||
|
||||
* creates a new router for public network
|
||||
* verifies the router appears in the routers table as active
|
||||
* deletes the newly created router
|
||||
@ -73,6 +74,7 @@ class TestRouters(helpers.TestCase):
|
||||
|
||||
def test_router_add_delete_interface(self):
|
||||
"""Tests the router interface creation and deletion functionalities:
|
||||
|
||||
* Follows the steps to create a new router
|
||||
* Clicks on the new router name from the routers table
|
||||
* Moves to the Interfaces page/tab
|
||||
@ -102,8 +104,8 @@ class TestRouters(helpers.TestCase):
|
||||
self._delete_router()
|
||||
|
||||
def test_router_delete_interface_by_row(self):
|
||||
"""Tests the router interface creation and deletion by
|
||||
row action functionalities:
|
||||
"""Tests the router interface creation and deletion by row action:
|
||||
|
||||
* Follows the steps to create a new router
|
||||
* Clicks on the new router name from the routers table
|
||||
* Moves to the Interfaces page/tab
|
||||
@ -160,6 +162,7 @@ class TestAdminRouters(helpers.AdminTestCase):
|
||||
@decorators.services_required("neutron")
|
||||
def test_router_create_admin(self):
|
||||
"""tests the router creation and deletion functionalities:
|
||||
|
||||
* creates a new router for public network
|
||||
* verifies the router appears in the routers table as active
|
||||
* edits router name
|
||||
|
@ -20,25 +20,26 @@ class TestRouters(helpers.TestCase):
|
||||
|
||||
@decorators.services_required("neutron")
|
||||
def test_router_create(self):
|
||||
"""This test case checks create, clear/set gateway,
|
||||
delete router functionality
|
||||
executed by non-admin user::
|
||||
Steps:
|
||||
1. Login to Horizon Dashboard as horizon user
|
||||
2. Navigate to Project -> Network -> Routers page
|
||||
3. Create new router
|
||||
4. Check that the router appears in the routers table as active
|
||||
5. Check that no Error messages present
|
||||
6. Clear the gateway
|
||||
7. Check that the router is still in the routers table
|
||||
with no external network
|
||||
8. Check that no Error messages present
|
||||
9. Set the gateway to 'public' network
|
||||
10. Check that no Error messages present
|
||||
11. Check that router's external network is set to 'public'
|
||||
12. Delete the router
|
||||
13. Check that the router is absent in the routers table
|
||||
14. Check that no Error messages present
|
||||
"""Checks create, clear/set gateway, delete router functionality
|
||||
|
||||
Executed by non-admin user.
|
||||
|
||||
Steps:
|
||||
1. Login to Horizon Dashboard as horizon user
|
||||
2. Navigate to Project -> Network -> Routers page
|
||||
3. Create new router
|
||||
4. Check that the router appears in the routers table as active
|
||||
5. Check that no Error messages present
|
||||
6. Clear the gateway
|
||||
7. Check that the router is still in the routers table
|
||||
with no external network
|
||||
8. Check that no Error messages present
|
||||
9. Set the gateway to 'public' network
|
||||
10. Check that no Error messages present
|
||||
11. Check that router's external network is set to 'public'
|
||||
12. Delete the router
|
||||
13. Check that the router is absent in the routers table
|
||||
14. Check that no Error messages present
|
||||
"""
|
||||
|
||||
routers_page = self.home_pg.go_to_network_routerspage()
|
||||
|
@ -64,17 +64,19 @@ class TestSecuritygroup(helpers.TestCase):
|
||||
|
||||
def test_securitygroup_create_delete(self):
|
||||
"""tests the security group creation and deletion functionalities:
|
||||
|
||||
* creates a new security group
|
||||
* verifies the security group appears in the security groups table
|
||||
* deletes the newly created security group
|
||||
* verifies the security group does not appear in the table after
|
||||
deletion
|
||||
deletion
|
||||
"""
|
||||
self._create_securitygroup()
|
||||
self._delete_securitygroup()
|
||||
|
||||
def test_managerules_create_delete_by_row(self):
|
||||
"""tests the manage rules creation and deletion functionalities:
|
||||
|
||||
* create a new security group
|
||||
* verifies the security group appears in the security groups table
|
||||
* creates a new rule
|
||||
@ -83,7 +85,7 @@ class TestSecuritygroup(helpers.TestCase):
|
||||
* verifies the rule does not appear in the table after deletion
|
||||
* deletes the newly created security group
|
||||
* verifies the security group does not appear in the table after
|
||||
deletion
|
||||
deletion
|
||||
"""
|
||||
self._create_securitygroup()
|
||||
self._add_rule()
|
||||
@ -92,6 +94,7 @@ class TestSecuritygroup(helpers.TestCase):
|
||||
|
||||
def test_managerules_create_delete_by_table(self):
|
||||
"""tests the manage rules creation and deletion functionalities:
|
||||
|
||||
* create a new security group
|
||||
* verifies the security group appears in the security groups table
|
||||
* creates a new rule
|
||||
@ -100,7 +103,7 @@ class TestSecuritygroup(helpers.TestCase):
|
||||
* verifies the rule does not appear in the table after deletion
|
||||
* deletes the newly created security group
|
||||
* verifies the security group does not appear in the table after
|
||||
deletion
|
||||
deletion
|
||||
"""
|
||||
self._create_securitygroup()
|
||||
self._add_rule()
|
||||
|
@ -43,9 +43,10 @@ class TestStacks(helpers.AdminTestCase):
|
||||
@decorators.services_required("heat")
|
||||
def test_create_delete_stack(self):
|
||||
"""tests the stack creation and deletion functionality
|
||||
|
||||
* creates a new stack
|
||||
* verifies the stack appears in the stacks table in Create Complete
|
||||
state
|
||||
state
|
||||
* deletes the newly created stack
|
||||
* verifies the stack does not appear in the table after deletion
|
||||
"""
|
||||
|
@ -56,9 +56,8 @@ class TestPasswordChange(helpers.TestCase):
|
||||
"Failed to login with default password")
|
||||
|
||||
def test_password_change(self):
|
||||
"""Changes the password, verifies it was indeed changed and resets to
|
||||
default password.
|
||||
"""
|
||||
# Changes the password, verifies it was indeed changed and
|
||||
# resets to default password.
|
||||
passwordchange_page = self.home_pg.go_to_settings_changepasswordpage()
|
||||
|
||||
try:
|
||||
@ -74,9 +73,8 @@ class TestPasswordChange(helpers.TestCase):
|
||||
self._login()
|
||||
|
||||
def test_show_message_after_logout(self):
|
||||
"""Ensure an informational message is shown on the login page after the
|
||||
user is logged out.
|
||||
"""
|
||||
# Ensure an informational message is shown on the login page
|
||||
# after the user is logged out.
|
||||
passwordchange_page = self.home_pg.go_to_settings_changepasswordpage()
|
||||
|
||||
try:
|
||||
@ -111,6 +109,7 @@ class TestUserSettings(helpers.TestCase):
|
||||
|
||||
def test_user_settings_change(self):
|
||||
"""tests the user's settings options:
|
||||
|
||||
* changes the system's language
|
||||
* changes the timezone
|
||||
* changes the number of items per page (page size)
|
||||
|
@ -45,16 +45,17 @@ class TestVolumeSnapshotsBasic(helpers.TestCase):
|
||||
|
||||
def test_create_edit_delete_volume_snapshot(self):
|
||||
"""Test checks create/delete volume snapshot action
|
||||
Steps:
|
||||
1. Login to Horizon Dashboard
|
||||
2. Navigate to Project -> Compute -> Volumes page
|
||||
3. Create snapshot for existed volume
|
||||
4. Check that no ERROR appears
|
||||
5. Check that snapshot is in the list
|
||||
6. Check that snapshot has reference to correct volume
|
||||
7. Edit snapshot name and description
|
||||
8. Delete volume snapshot from proper page
|
||||
9. Check that volume snapshot not in the list
|
||||
|
||||
Steps:
|
||||
1. Login to Horizon Dashboard
|
||||
2. Navigate to Project -> Compute -> Volumes page
|
||||
3. Create snapshot for existed volume
|
||||
4. Check that no ERROR appears
|
||||
5. Check that snapshot is in the list
|
||||
6. Check that snapshot has reference to correct volume
|
||||
7. Edit snapshot name and description
|
||||
8. Delete volume snapshot from proper page
|
||||
9. Check that volume snapshot not in the list
|
||||
"""
|
||||
volumes_page = self.home_pg.go_to_compute_volumes_volumespage()
|
||||
volumes_snapshot_page = volumes_page.create_volume_snapshot(
|
||||
@ -90,25 +91,26 @@ class TestVolumeSnapshotsBasic(helpers.TestCase):
|
||||
|
||||
def test_volume_snapshots_pagination(self):
|
||||
"""This test checks volumes snapshots pagination
|
||||
Steps:
|
||||
1) Login to Horizon Dashboard
|
||||
2) Go to Project -> Compute -> Volumes -> Volumes tab, create
|
||||
volumes and 3 snapshots
|
||||
3) Navigate to user settings page
|
||||
4) Change 'Items Per Page' value to 1
|
||||
5) Go to Project -> Compute -> Volumes -> Volumes Snapshot tab
|
||||
or Admin -> System -> Volumes -> Volumes Snapshot tab
|
||||
(depends on user)
|
||||
6) Check that only 'Next' link is available, only one snapshot is
|
||||
available (and it has correct name)
|
||||
7) Click 'Next' and check that both 'Prev' and 'Next' links are
|
||||
available, only one snapshot is available (and it has correct name)
|
||||
8) Click 'Next' and check that only 'Prev' link is available,
|
||||
only one snapshot is visible (and it has correct name)
|
||||
9) Click 'Prev' and check result (should be the same as for step7)
|
||||
10) Click 'Prev' and check result (should be the same as for step6)
|
||||
11) Go to user settings page and restore 'Items Per Page'
|
||||
12) Delete created snapshots and volumes
|
||||
|
||||
Steps:
|
||||
1) Login to Horizon Dashboard
|
||||
2) Go to Project -> Compute -> Volumes -> Volumes tab, create
|
||||
volumes and 3 snapshots
|
||||
3) Navigate to user settings page
|
||||
4) Change 'Items Per Page' value to 1
|
||||
5) Go to Project -> Compute -> Volumes -> Volumes Snapshot tab
|
||||
or Admin -> System -> Volumes -> Volumes Snapshot tab
|
||||
(depends on user)
|
||||
6) Check that only 'Next' link is available, only one snapshot is
|
||||
available (and it has correct name)
|
||||
7) Click 'Next' and check that both 'Prev' and 'Next' links are
|
||||
available, only one snapshot is available (and it has correct name)
|
||||
8) Click 'Next' and check that only 'Prev' link is available,
|
||||
only one snapshot is visible (and it has correct name)
|
||||
9) Click 'Prev' and check result (should be the same as for step7)
|
||||
10) Click 'Prev' and check result (should be the same as for step6)
|
||||
11) Go to user settings page and restore 'Items Per Page'
|
||||
12) Delete created snapshots and volumes
|
||||
"""
|
||||
volumes_page = self.home_pg.go_to_compute_volumes_volumespage()
|
||||
count = 3
|
||||
@ -223,14 +225,15 @@ class TestVolumeSnapshotsAdvanced(helpers.TestCase):
|
||||
|
||||
def test_create_volume_from_snapshot(self):
|
||||
"""Test checks possibility to create volume from snapshot
|
||||
Steps:
|
||||
1. Login to Horizon Dashboard as regular user
|
||||
2. Navigate to Project -> Compute -> Volumes page
|
||||
3. Create snapshot for existed volume
|
||||
4. Create new volume from snapshot
|
||||
5. Check the volume is created and has 'Available' status
|
||||
6. Delete volume snapshot
|
||||
7. Delete volume
|
||||
|
||||
Steps:
|
||||
1. Login to Horizon Dashboard as regular user
|
||||
2. Navigate to Project -> Compute -> Volumes page
|
||||
3. Create snapshot for existed volume
|
||||
4. Create new volume from snapshot
|
||||
5. Check the volume is created and has 'Available' status
|
||||
6. Delete volume snapshot
|
||||
7. Delete volume
|
||||
"""
|
||||
volumes_page = self.home_pg.go_to_compute_volumes_volumespage()
|
||||
volumes_snapshot_page = volumes_page.create_volume_snapshot(
|
||||
|
@ -27,18 +27,19 @@ class TestVolumesBasic(helpers.TestCase):
|
||||
|
||||
def test_volume_create_edit_delete(self):
|
||||
"""This test case checks create, edit, delete volume functionality:
|
||||
Steps:
|
||||
1. Login to Horizon Dashboard
|
||||
2. Navigate to Project -> Compute -> Volumes page
|
||||
3. Create new volume
|
||||
4. Check that the volume is in the list
|
||||
5. Check that no Error messages present
|
||||
6. Edit the volume
|
||||
7. Check that the volume is still in the list
|
||||
8. Check that no Error messages present
|
||||
9. Delete the volume via proper page (depends on user)
|
||||
10. Check that the volume is absent in the list
|
||||
11. Check that no Error messages present
|
||||
|
||||
Steps:
|
||||
1. Login to Horizon Dashboard
|
||||
2. Navigate to Project -> Compute -> Volumes page
|
||||
3. Create new volume
|
||||
4. Check that the volume is in the list
|
||||
5. Check that no Error messages present
|
||||
6. Edit the volume
|
||||
7. Check that the volume is still in the list
|
||||
8. Check that no Error messages present
|
||||
9. Delete the volume via proper page (depends on user)
|
||||
10. Check that the volume is absent in the list
|
||||
11. Check that no Error messages present
|
||||
"""
|
||||
volumes_page = self.home_pg.go_to_compute_volumes_volumespage()
|
||||
volumes_page.create_volume(self.VOLUME_NAME)
|
||||
@ -78,24 +79,25 @@ class TestVolumesBasic(helpers.TestCase):
|
||||
|
||||
def test_volumes_pagination(self):
|
||||
"""This test checks volumes pagination
|
||||
Steps:
|
||||
1) Login to Horizon Dashboard
|
||||
2) Go to Project -> Compute -> Volumes -> Volumes tab and create
|
||||
three volumes
|
||||
3) Navigate to user settings page
|
||||
4) Change 'Items Per Page' value to 1
|
||||
5) Go to Project -> Compute -> Volumes -> Volumes tab or
|
||||
Admin -> System -> Volumes -> Volumes tab (depends on user)
|
||||
6) Check that only 'Next' link is available, only one volume is
|
||||
available (and it has correct name)
|
||||
7) Click 'Next' and check that both 'Prev' and 'Next' links are
|
||||
available, only one volume is available (and it has correct name)
|
||||
8) Click 'Next' and check that only 'Prev' link is available,
|
||||
only one volume is visible (and it has correct name)
|
||||
9) Click 'Prev' and check result (should be the same as for step7)
|
||||
10) Click 'Prev' and check result (should be the same as for step6)
|
||||
11) Go to user settings page and restore 'Items Per Page'
|
||||
12) Delete created volumes
|
||||
|
||||
Steps:
|
||||
1) Login to Horizon Dashboard
|
||||
2) Go to Project -> Compute -> Volumes -> Volumes tab and create
|
||||
three volumes
|
||||
3) Navigate to user settings page
|
||||
4) Change 'Items Per Page' value to 1
|
||||
5) Go to Project -> Compute -> Volumes -> Volumes tab or
|
||||
Admin -> System -> Volumes -> Volumes tab (depends on user)
|
||||
6) Check that only 'Next' link is available, only one volume is
|
||||
available (and it has correct name)
|
||||
7) Click 'Next' and check that both 'Prev' and 'Next' links are
|
||||
available, only one volume is available (and it has correct name)
|
||||
8) Click 'Next' and check that only 'Prev' link is available,
|
||||
only one volume is visible (and it has correct name)
|
||||
9) Click 'Prev' and check result (should be the same as for step7)
|
||||
10) Click 'Prev' and check result (should be the same as for step6)
|
||||
11) Go to user settings page and restore 'Items Per Page'
|
||||
12) Delete created volumes
|
||||
"""
|
||||
volumes_page = self.home_pg.go_to_compute_volumes_volumespage()
|
||||
count = 3
|
||||
@ -167,15 +169,16 @@ class TestVolumesAdvanced(helpers.TestCase):
|
||||
@decorators.skip_because(bugs=['1584057'])
|
||||
def test_manage_volume_attachments(self):
|
||||
"""This test case checks attach/detach actions for volume
|
||||
Steps:
|
||||
1. Login to Horizon Dashboard as horizon user
|
||||
2. Navigate to Project -> Compute -> Instances, create instance
|
||||
3. Navigate to Project -> Compute -> Volumes, create volume
|
||||
4. Attach volume to instance from step2
|
||||
5. Check that volume status and link to instance
|
||||
6. Detach volume from instance
|
||||
7. Check volume status
|
||||
8. Delete volume and instance
|
||||
|
||||
Steps:
|
||||
1. Login to Horizon Dashboard as horizon user
|
||||
2. Navigate to Project -> Compute -> Instances, create instance
|
||||
3. Navigate to Project -> Compute -> Volumes, create volume
|
||||
4. Attach volume to instance from step2
|
||||
5. Check that volume status and link to instance
|
||||
6. Detach volume from instance
|
||||
7. Check volume status
|
||||
8. Delete volume and instance
|
||||
"""
|
||||
instance_name = helpers.gen_random_resource_name('instance')
|
||||
instances_page = self.home_pg.go_to_compute_instancespage()
|
||||
@ -251,12 +254,13 @@ class TestVolumesActions(helpers.TestCase):
|
||||
|
||||
def test_volume_extend(self):
|
||||
"""This test case checks extend volume functionality:
|
||||
Steps:
|
||||
1. Check current volume size
|
||||
2. Extend volume
|
||||
3. Check that no Error messages present
|
||||
4. Check that the volume is still in the list
|
||||
5. Check that the volume size is changed
|
||||
|
||||
Steps:
|
||||
1. Check current volume size
|
||||
2. Extend volume
|
||||
3. Check that no Error messages present
|
||||
4. Check that the volume is still in the list
|
||||
5. Check that the volume size is changed
|
||||
"""
|
||||
orig_size = self.volumes_page.get_size(self.VOLUME_NAME)
|
||||
self.volumes_page.extend_volume(self.VOLUME_NAME, orig_size + 1)
|
||||
@ -272,12 +276,13 @@ class TestVolumesActions(helpers.TestCase):
|
||||
@decorators.skip_because(bugs=['1584057'])
|
||||
def test_volume_upload_to_image(self):
|
||||
"""This test case checks upload volume to image functionality:
|
||||
Steps:
|
||||
1. Upload volume to image with some disk format
|
||||
2. Check that image is created
|
||||
3. Check that no Error messages present
|
||||
4. Delete the image
|
||||
5. Repeat actions for all disk formats
|
||||
|
||||
Steps:
|
||||
1. Upload volume to image with some disk format
|
||||
2. Check that image is created
|
||||
3. Check that no Error messages present
|
||||
4. Delete the image
|
||||
5. Repeat actions for all disk formats
|
||||
"""
|
||||
self.volumes_page = self.home_pg.go_to_compute_volumes_volumespage()
|
||||
all_formats = {"qcow2": u'QCOW2', "raw": u'Raw', "vdi": u'VDI',
|
||||
@ -306,13 +311,14 @@ class TestVolumesActions(helpers.TestCase):
|
||||
|
||||
def test_volume_launch_as_instance(self):
|
||||
"""This test case checks launch volume as instance functionality:
|
||||
Steps:
|
||||
1. Launch volume as instance
|
||||
2. Check that instance is created
|
||||
3. Check that no Error messages present
|
||||
4. Check that instance status is 'active'
|
||||
5. Check that volume status is 'in use'
|
||||
6. Delete instance
|
||||
|
||||
Steps:
|
||||
1. Launch volume as instance
|
||||
2. Check that instance is created
|
||||
3. Check that no Error messages present
|
||||
4. Check that instance status is 'active'
|
||||
5. Check that volume status is 'in use'
|
||||
6. Delete instance
|
||||
"""
|
||||
self.volumes_page.launch_instance(self.VOLUME_NAME, self.INSTANCE_NAME)
|
||||
self.assertTrue(
|
||||
|
@ -19,15 +19,16 @@ class TestAdminVolumeTypes(helpers.AdminTestCase):
|
||||
|
||||
def test_volume_type_create_delete(self):
|
||||
"""This test case checks create, delete volume type:
|
||||
Steps:
|
||||
1. Login to Horizon Dashboard as admin user
|
||||
2. Navigate to Admin -> System -> Volumes -> Volume Types page
|
||||
3. Create new volume type
|
||||
4. Check that the volume type is in the list
|
||||
5. Check that no Error messages present
|
||||
6. Delete the volume type
|
||||
7. Check that the volume type is absent in the list
|
||||
8. Check that no Error messages present
|
||||
|
||||
Steps:
|
||||
1. Login to Horizon Dashboard as admin user
|
||||
2. Navigate to Admin -> System -> Volumes -> Volume Types page
|
||||
3. Create new volume type
|
||||
4. Check that the volume type is in the list
|
||||
5. Check that no Error messages present
|
||||
6. Delete the volume type
|
||||
7. Check that the volume type is absent in the list
|
||||
8. Check that no Error messages present
|
||||
"""
|
||||
volume_types_page = self.home_pg.go_to_system_volumes_volumetypespage()
|
||||
|
||||
@ -55,6 +56,7 @@ class TestQoSSpec(helpers.AdminTestCase):
|
||||
|
||||
def test_qos_spec_create_delete(self):
|
||||
"""tests the QoS Spec creation and deletion functionality
|
||||
|
||||
* creates a new QoS Spec
|
||||
* verifies the QoS Spec appears in the QoS Specs table
|
||||
* deletes the newly created QoS Spec
|
||||
@ -78,10 +80,11 @@ class TestQoSSpec(helpers.AdminTestCase):
|
||||
|
||||
def test_qos_spec_edit_consumer(self):
|
||||
"""tests Edit Consumer of QoS Spec functionality
|
||||
|
||||
* creates a new QoS Spec
|
||||
* verifies the QoS Spec appears in the QoS Specs table
|
||||
* edit consumer of created QoS Spec (check all options - front-end,
|
||||
both, back-end)
|
||||
both, back-end)
|
||||
* verifies current consumer of the QoS Spec in the QoS Specs table
|
||||
* deletes the newly created QoS Spec
|
||||
* verifies the QoS Spec does not appear in the table after deletion
|
||||
|
@ -43,9 +43,11 @@ def load_test_data(load_onto=None):
|
||||
|
||||
|
||||
class TestData(object):
|
||||
"""Holder object for test data. Any functions passed to the init method
|
||||
will be called with the ``TestData`` object as their only argument. They
|
||||
can then load data onto the object as desired.
|
||||
"""Holder object for test data.
|
||||
|
||||
Any functions passed to the init method will be called with the
|
||||
``TestData`` object as their only argument.
|
||||
They can then load data onto the object as desired.
|
||||
|
||||
The idea is to use the instantiated object like this::
|
||||
|
||||
@ -93,9 +95,7 @@ class TestDataContainer(object):
|
||||
return self._objects
|
||||
|
||||
def filter(self, filtered=None, **kwargs):
|
||||
"""Returns objects in this container whose attributes match the given
|
||||
keyword arguments.
|
||||
"""
|
||||
"""Returns objects whose attributes match the given kwargs."""
|
||||
if filtered is None:
|
||||
filtered = self._objects
|
||||
try:
|
||||
@ -111,8 +111,9 @@ class TestDataContainer(object):
|
||||
return self.filter(filtered=filtered, **kwargs)
|
||||
|
||||
def get(self, **kwargs):
|
||||
"""Returns the single object in this container whose attributes match
|
||||
the given keyword arguments. An error will be raised if the arguments
|
||||
"""Returns a single object whose attributes match the given kwargs.
|
||||
|
||||
An error will be raised if the arguments
|
||||
provided don't return exactly one match.
|
||||
"""
|
||||
matches = self.filter(**kwargs)
|
||||
|
@ -396,6 +396,7 @@ def _get_tenant_volume_usages(request, usages, disabled_quotas, tenant_id):
|
||||
@memoized
|
||||
def tenant_quota_usages(request, tenant_id=None):
|
||||
"""Get our quotas and construct our usage object.
|
||||
|
||||
If no tenant_id is provided, a the request.user.project_id
|
||||
is assumed to be used
|
||||
"""
|
||||
|
@ -30,7 +30,9 @@ def get_int_or_uuid(value):
|
||||
|
||||
|
||||
def get_display_label(choices, status):
|
||||
"""This method is used in places where a resource's status or
|
||||
"""Get a display label for resource status.
|
||||
|
||||
This method is used in places where a resource's status or
|
||||
admin state labels need to assigned before they are sent to the
|
||||
view template.
|
||||
"""
|
||||
|
3
tox.ini
3
tox.ini
@ -117,8 +117,7 @@ commands =
|
||||
|
||||
[flake8]
|
||||
exclude = .venv,.git,.tox,dist,*lib/python*,*egg,build,panel_template,dash_template,local_settings.py,*/local/*,*/test/test_plugins/*,.ropeproject,node_modules
|
||||
# H405 multi line docstring summary not separated with an empty line
|
||||
ignore = H405
|
||||
ignore =
|
||||
# Enable the following hacking rules which are disabled by default
|
||||
# H203 Use assertIs(Not)None to check for None
|
||||
# H904 Delay string interpolations at logging calls
|
||||
|
Loading…
Reference in New Issue
Block a user