Updates to novacli and cindercli
* Removes redundant cindercli behaviors * Adds os_auth_system as parameter to cindercli client init. * Renames client methods to match CLI names ('create_volume' in the cinder client is now just 'create', etc.) * Adds the cinder composite. * Adds os_region_name and os_auth_system as configurable options to cinder config * Adds os_region_name to nova config. Change-Id: I999c39fc9d99b689bf4c8ff967fd8b05c6ee79b3
This commit is contained in:
parent
d6219722b9
commit
90c4f05b2a
@ -1,198 +0,0 @@
|
||||
from time import time, sleep
|
||||
|
||||
from cafe.engine.behaviors import behavior
|
||||
|
||||
from cloudcafe.common.tools.datagen import random_string
|
||||
from cloudcafe.openstackcli.common.behaviors import (
|
||||
OpenstackCLI_BaseBehavior, OpenstackCLI_BehaviorError)
|
||||
from cloudcafe.openstackcli.cindercli.client import CinderCLI
|
||||
from cloudcafe.openstackcli.cindercli.config import CinderCLI_Config
|
||||
from cloudcafe.blockstorage.volumes_api.v1.config import VolumesAPIConfig
|
||||
from cloudcafe.blockstorage.volumes_api.v1.models import statuses
|
||||
|
||||
|
||||
class CinderCLIBehaviorError(OpenstackCLI_BehaviorError):
|
||||
pass
|
||||
|
||||
|
||||
class CinderCLI_Behaviors(OpenstackCLI_BaseBehavior):
|
||||
|
||||
_default_error = CinderCLIBehaviorError
|
||||
|
||||
def __init__(
|
||||
self, cinder_cli_client, cinder_cli_config=None,
|
||||
volumes_api_client=None, volumes_api_config=None):
|
||||
|
||||
super(CinderCLI_Behaviors, self).__init__()
|
||||
self.client = cinder_cli_client
|
||||
self.config = cinder_cli_config or CinderCLI_Config()
|
||||
|
||||
self.api_client = volumes_api_client
|
||||
self.api_config = volumes_api_config or VolumesAPIConfig()
|
||||
|
||||
@behavior(CinderCLI)
|
||||
def create_available_volume(self, name=None, type_=None, size=None):
|
||||
name = random_string(prefix="Volume_", size=10)
|
||||
size = size or self.api_config.min_volume_size
|
||||
type_ = type_ or self.api_config.default_volume_type
|
||||
|
||||
resp = self.client.create_volume(
|
||||
size=size, volume_type=type_, display_name=name)
|
||||
|
||||
self.raise_on_error(resp, "Cinder CLI Volume Create call failed.")
|
||||
volume = resp.entity
|
||||
self.wait_for_volume_status(volume.id_, statuses.Volume.AVAILABLE)
|
||||
return volume
|
||||
|
||||
@behavior(CinderCLI)
|
||||
def get_volume_status(self, volume_id):
|
||||
resp = self.client.show_volume(volume_id=volume_id)
|
||||
self.raise_on_error(resp, "Cinder CLI Show Volume call failed.")
|
||||
return resp.entity.status
|
||||
|
||||
@behavior(CinderCLI)
|
||||
def wait_for_volume_status(
|
||||
self, volume_id, expected_status, timeout=None, poll_rate=None):
|
||||
""" Waits for a specific status and returns None when that status is
|
||||
observed.
|
||||
Note: Unreliable for transient statuses like 'deleting'.
|
||||
"""
|
||||
|
||||
poll_rate = int(
|
||||
poll_rate or self.api_config.volume_status_poll_frequency)
|
||||
timeout = int(timeout or self.api_config.volume_create_timeout)
|
||||
end_time = time() + int(timeout)
|
||||
|
||||
while time() < end_time:
|
||||
status = self.get_volume_status(volume_id)
|
||||
if status == expected_status:
|
||||
self._log.info(
|
||||
"Expected Volume status '{0}' observed as expected".format(
|
||||
expected_status))
|
||||
break
|
||||
sleep(poll_rate)
|
||||
|
||||
else:
|
||||
msg = (
|
||||
"wait_for_volume_status() ran for {0} seconds and did not "
|
||||
"observe the volume attain the {1} status.".format(
|
||||
timeout, expected_status))
|
||||
self._log.info(msg)
|
||||
raise self._default_error(msg)
|
||||
|
||||
@behavior(CinderCLI)
|
||||
def list_volumes(self):
|
||||
resp = self.client.list_volumes
|
||||
|
||||
self.raise_on_error(resp, "Unable to list volumes via the cinder cli")
|
||||
|
||||
return resp.entity
|
||||
|
||||
@behavior(CinderCLI)
|
||||
def delete_volume(self, volume_id):
|
||||
resp = self.client.delete_volume(volume_id)
|
||||
self.raise_if(
|
||||
self.is_process_error(resp),
|
||||
"Cinder CLI Volume Delete did not execute successfully")
|
||||
|
||||
@behavior(CinderCLI)
|
||||
def delete_volume_confirmed(self, volume_id, timeout=60, poll_rate=5):
|
||||
self.delete_volume(volume_id)
|
||||
return self.wait_for_volume_delete(volume_id, timeout, poll_rate)
|
||||
|
||||
@behavior(CinderCLI)
|
||||
def wait_for_volume_delete(
|
||||
self, volume_id, timeout=60, poll_rate=5):
|
||||
expected_err_msg = (
|
||||
"ERROR: No volume with a name or ID of '{0}' exists.".format(
|
||||
volume_id))
|
||||
end = time() + timeout
|
||||
while time() <= end:
|
||||
resp = self.client.show_volume(volume_id)
|
||||
if self.is_cli_error(resp) or self.is_process_error(resp):
|
||||
if resp.standard_error[-1] == expected_err_msg:
|
||||
return True
|
||||
sleep(poll_rate)
|
||||
else:
|
||||
return False
|
||||
|
||||
# snapshots
|
||||
@behavior(CinderCLI)
|
||||
def list_snapshots(self):
|
||||
resp = self.client.list_snapshots
|
||||
|
||||
self.raise_on_error(resp, "Unable to list snapshots via the cinder cli")
|
||||
|
||||
return resp.entity
|
||||
|
||||
@behavior(CinderCLI)
|
||||
def delete_snapshot(self, snapshot_id):
|
||||
resp = self.client.delete_snapshot()
|
||||
|
||||
self.raise_if(
|
||||
self.is_process_error(resp),
|
||||
"Cinder CLI Snapshot Delete did not execute successfully")
|
||||
|
||||
@behavior(CinderCLI)
|
||||
def get_snapshot_status(self, snapshot_id):
|
||||
resp = self.client.show_snapshot(snapshot_id=snapshot_id)
|
||||
self.raise_on_error(resp, "Cinder CLI snapshot-show call failed.")
|
||||
return resp.entity.status
|
||||
|
||||
@behavior(CinderCLI)
|
||||
def list_volume_snapshot_ids(self, volume_id):
|
||||
snapshots = self.list_snapshots()
|
||||
return [snap.id_ for snap in snapshots if snap.volume_id == volume_id]
|
||||
|
||||
@behavior(CinderCLI)
|
||||
def wait_for_snapshot_status(
|
||||
self, snapshot_id, expected_status, timeout, poll_rate=None):
|
||||
""" Waits for a specific status and returns None when that status is
|
||||
observed.
|
||||
Note: Unreliable for transient statuses like 'deleting'.
|
||||
"""
|
||||
|
||||
poll_rate = int(
|
||||
poll_rate or self.api_config.snapshot_status_poll_frequency)
|
||||
end_time = time() + int(timeout)
|
||||
|
||||
while time() < end_time:
|
||||
status = self.get_snapshot_status(snapshot_id)
|
||||
if status == expected_status:
|
||||
self._log.info(
|
||||
"Expected snapshot status '{0}' observed as "
|
||||
"expected".format(expected_status))
|
||||
break
|
||||
sleep(poll_rate)
|
||||
|
||||
else:
|
||||
msg = (
|
||||
"wait_for_snapshot_status() ran for {0} seconds and did not "
|
||||
"observe the volume attain the {1} status.".format(
|
||||
timeout, expected_status))
|
||||
self._log.info(msg)
|
||||
raise self._default_error(msg)
|
||||
|
||||
@behavior(CinderCLI)
|
||||
def wait_for_snapshot_delete(self, snapshot_id, timeout=60, poll_rate=5):
|
||||
|
||||
expected_err_msg = (
|
||||
"ERROR: No snapshot with a name or ID of '{0}' exists.".format(
|
||||
snapshot_id))
|
||||
|
||||
end = time() + timeout
|
||||
while time() <= end:
|
||||
resp = self.client.show_snapshot(snapshot_id)
|
||||
if self.is_cli_error(resp) or self.is_process_error(resp):
|
||||
if resp.standard_error[-1] == expected_err_msg:
|
||||
return True
|
||||
sleep(poll_rate)
|
||||
else:
|
||||
return False
|
||||
|
||||
# volume types
|
||||
@behavior(CinderCLI)
|
||||
def list_volume_types(self):
|
||||
resp = self.client.list_volume_types()
|
||||
self.raise_on_error(resp, "Unable to list snapshots via the cinder cli")
|
||||
return resp.entity
|
@ -7,27 +7,30 @@ class CinderCLI(BaseOpenstackPythonCLI_Client):
|
||||
|
||||
_KWMAP = {
|
||||
'volume_service_name': 'volume-service-name',
|
||||
'os_volume_api_version': 'os-volume-api-version'}
|
||||
'os_volume_api_version': 'os-volume-api-version',
|
||||
'os_auth_system': 'os-auth-system'}
|
||||
|
||||
# Make sure to include all openstack common cli paramaters in addition to
|
||||
# the cinder specific ones
|
||||
_KWMAP.update(BaseOpenstackPythonCLI_Client._KWMAP)
|
||||
|
||||
#The client command the must precede any call to the cli
|
||||
# The client command the must precede any call to the cli
|
||||
_CMD = 'cinder'
|
||||
|
||||
def __init__(
|
||||
self, volume_service_name=None, os_volume_api_version=None,
|
||||
**kwargs):
|
||||
os_auth_system=None, **kwargs):
|
||||
super(CinderCLI, self).__init__(**kwargs)
|
||||
self.volume_service_name = volume_service_name
|
||||
self.os_volume_api_version = os_volume_api_version
|
||||
self.os_auth_system = os_auth_system
|
||||
|
||||
# Volumes
|
||||
def create_volume(
|
||||
def create(
|
||||
self, size, snapshot_id=None, source_volid=None, image_id=None,
|
||||
display_name=None, display_description=None, volume_type=None,
|
||||
availability_zone=None, metadata=None):
|
||||
"""Create a new volume via the cinder command line client"""
|
||||
|
||||
metadata = self._dict_to_string(metadata)
|
||||
|
||||
@ -44,17 +47,19 @@ class CinderCLI(BaseOpenstackPythonCLI_Client):
|
||||
'metadata': 'metadata'}
|
||||
return self._process_command()
|
||||
|
||||
def show_volume(self, volume_id):
|
||||
def show(self, volume_id):
|
||||
"""Get details for a volume via the cinder command line client"""
|
||||
_cmd = 'show'
|
||||
_response_type = CinderResponses.VolumeResponse
|
||||
return self._process_command()
|
||||
|
||||
def delete_volume(self, volume_name_or_id):
|
||||
def delete(self, volume_name_or_id):
|
||||
"""delete a volume via the cinder command line client"""
|
||||
_cmd = 'delete'
|
||||
return self._process_command()
|
||||
|
||||
def list_volumes(self, display_name=None, status=None, all_tenants=False):
|
||||
|
||||
def list(self, display_name=None, status=None, all_tenants=False):
|
||||
"""List all volumes via the cinder command line client"""
|
||||
all_tenants = 1 if all_tenants is True else 0
|
||||
_response_type = CinderResponses.VolumeListResponse
|
||||
_cmd = 'list'
|
||||
@ -65,10 +70,10 @@ class CinderCLI(BaseOpenstackPythonCLI_Client):
|
||||
return self._process_command()
|
||||
|
||||
# Snapshots
|
||||
def create_snapshot(
|
||||
def snapshot_create(
|
||||
self, volume_id, force=True, display_name=None,
|
||||
display_description=None):
|
||||
|
||||
"""Create a snapshot of a volume via the cinder command line client"""
|
||||
force = 'True' if force else 'False'
|
||||
_kwmap = {
|
||||
'force': 'force',
|
||||
@ -78,18 +83,26 @@ class CinderCLI(BaseOpenstackPythonCLI_Client):
|
||||
_response_type = CinderResponses.SnapshotResponse
|
||||
return self._process_command()
|
||||
|
||||
def list_snapshots(self):
|
||||
def snapshot_list(self):
|
||||
"""List all snapshots via the cinder command line client"""
|
||||
_cmd = 'snapshot-list'
|
||||
_response_type = CinderResponses.SnapshotListResponse
|
||||
return self._process_command()
|
||||
|
||||
def show_snapshot(self, snapshot_id):
|
||||
def snapshot_show(self, snapshot_id):
|
||||
"""Get details for a snapshot via the cinder command line client"""
|
||||
_cmd = 'snapshot-show'
|
||||
_response_type = CinderResponses.SnapshotResponse
|
||||
return self._process_command()
|
||||
|
||||
def snapshot_delete(self, snapshot_id):
|
||||
"""Delete a snapshot via the cinder command line client"""
|
||||
_cmd = 'snapshot-delete'
|
||||
return self._process_command()
|
||||
|
||||
# Volume Types
|
||||
def list_volume_types(self):
|
||||
def type_list(self):
|
||||
"""Get a list of all volume types via the cinder command line client"""
|
||||
_cmd = 'type-list'
|
||||
_response_type = CinderResponses.VolumeTypeListResponse
|
||||
return self._process_command()
|
||||
|
42
cloudcafe/openstackcli/cindercli/composites.py
Normal file
42
cloudcafe/openstackcli/cindercli/composites.py
Normal file
@ -0,0 +1,42 @@
|
||||
"""
|
||||
Copyright 2014 Rackspace
|
||||
|
||||
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 cloudcafe.openstackcli.cindercli.client import CinderCLI
|
||||
from cloudcafe.openstackcli.common.config import OpenstackCLI_CommonConfig
|
||||
from cloudcafe.openstackcli.cindercli.config import CinderCLI_Config
|
||||
|
||||
|
||||
class CinderCLI_Composite(object):
|
||||
|
||||
def __init__(self):
|
||||
self.openstack_config = OpenstackCLI_CommonConfig()
|
||||
self.config = CinderCLI_Config()
|
||||
self._cli_kwargs = {
|
||||
'volume_service_name': self.config.volume_service_name,
|
||||
'os_volume_api_version': self.config.os_volume_api_version,
|
||||
'os_username': self.openstack_config.os_username,
|
||||
'os_password': self.openstack_config.os_password,
|
||||
'os_tenant_name': self.openstack_config.os_tenant_name,
|
||||
'os_auth_url': self.openstack_config.os_auth_url,
|
||||
'os_region_name': self.config.os_region_name or
|
||||
self.openstack_config.os_region_name,
|
||||
'os_cacert': self.openstack_config.os_cacert,
|
||||
'os_auth_system': self.config.os_auth_system,
|
||||
'retries': self.openstack_config.retries,
|
||||
'debug': self.openstack_config.debug}
|
||||
self.client = CinderCLI(**self._cli_kwargs)
|
||||
self.client.set_environment_variables(
|
||||
self.config.environment_variable_dictionary)
|
@ -42,6 +42,16 @@ class CinderCLI_Config(ConfigSectionInterface):
|
||||
def os_volume_api_version(self):
|
||||
return self.get('os_volume_api_version')
|
||||
|
||||
@property
|
||||
def os_region_name(self):
|
||||
"""cinder cli endpoint region"""
|
||||
return self.get('os_region_name')
|
||||
|
||||
@property
|
||||
def os_auth_system(self):
|
||||
"""Selects which auth system to use"""
|
||||
return self.get('os_auth_system')
|
||||
|
||||
@property
|
||||
def environment_variable_dictionary(self):
|
||||
"""Expects a python dictionary as a string. Returns an empty dict
|
||||
|
@ -185,6 +185,7 @@ class NovaCLI(BaseOpenstackPythonCLI_Client):
|
||||
"""
|
||||
|
||||
_cmd = 'volume-attach'
|
||||
device = device or 'auto'
|
||||
_response_type = responses.VolumeAttachResponse
|
||||
return self._process_command()
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
from cloudcafe.openstackcli.novacli.client import NovaCLI
|
||||
from cloudcafe.openstackcli.common.config import OpenstackCLI_CommonConfig
|
||||
from cloudcafe.openstackcli.novacli.client import NovaCLI
|
||||
from cloudcafe.openstackcli.novacli.config import NovaCLI_Config
|
||||
|
||||
|
||||
@ -13,10 +13,12 @@ class NovaCLI_Composite(object):
|
||||
'os_password': self.openstack_config.os_password,
|
||||
'os_tenant_name': self.openstack_config.os_tenant_name,
|
||||
'os_auth_url': self.openstack_config.os_auth_url,
|
||||
'os_region_name': self.openstack_config.os_region_name,
|
||||
'debug': self.openstack_config.debug,
|
||||
'os_auth_system': self.config.os_auth_system,
|
||||
'insecure': self.config.insecure}
|
||||
'insecure': self.config.insecure,
|
||||
# Allows for individual product configs to override the region name
|
||||
'os_region_name':
|
||||
self.config.os_region_name or self.openstack_config.os_region_name}
|
||||
self.client = NovaCLI(**self._cli_kwargs)
|
||||
self.client.set_environment_variables(
|
||||
self.config.environment_variable_dictionary)
|
||||
|
@ -32,6 +32,11 @@ class NovaCLI_Config(ConfigSectionInterface):
|
||||
"""Selects which auth system to use"""
|
||||
return self.get('os_auth_system')
|
||||
|
||||
@property
|
||||
def os_region_name(self):
|
||||
"""nova cli endpoint region"""
|
||||
return self.get('os_region_name')
|
||||
|
||||
@property
|
||||
def environment_variable_dictionary(self):
|
||||
"""Expects a python dictionary as a string. Returns an empty dict
|
||||
|
Loading…
x
Reference in New Issue
Block a user