Added behaviors and behavior tests
* Added verify_volume_status_progression_during_detachment behavior. * Modified verify_volume_status_progression_during_attachment behavior for better code reuse. * Added metatests for both behaviors. Change-Id: I0f3290e01b1380ecfcbc34e02e2aae2abf384a8c
This commit is contained in:
parent
ac40f3e2a7
commit
523f22d015
@ -1,5 +1,6 @@
|
||||
from time import sleep, time
|
||||
from cloudcafe.common.behaviors import StatusProgressionVerifier
|
||||
from cloudcafe.common.behaviors import (
|
||||
StatusProgressionVerifier, StatusProgressionVerifierError)
|
||||
from cloudcafe.compute.common.behaviors import BaseComputeBehavior
|
||||
from cloudcafe.compute.volume_attachments_api.config import \
|
||||
VolumeAttachmentsAPIConfig
|
||||
@ -14,6 +15,7 @@ class VolumeAttachmentsAPI_Behaviors(BaseComputeBehavior):
|
||||
def __init__(
|
||||
self, volume_attachments_client=None,
|
||||
volume_attachments_config=None, volumes_client=None):
|
||||
super(VolumeAttachmentsAPI_Behaviors, self).__init__()
|
||||
|
||||
self.client = volume_attachments_client
|
||||
self.config = volume_attachments_config or VolumeAttachmentsAPIConfig()
|
||||
@ -34,53 +36,95 @@ class VolumeAttachmentsAPI_Behaviors(BaseComputeBehavior):
|
||||
else:
|
||||
return False
|
||||
|
||||
def _get_volume_status(self, volume_id):
|
||||
resp = self.volumes_client.get_volume_info(volume_id=volume_id)
|
||||
if not resp.ok:
|
||||
msg = (
|
||||
"get_volume_status() failure: get_volume_info() call"
|
||||
" failed with a {0} status code".format(resp.status_code))
|
||||
self._log.error(msg)
|
||||
raise Exception(msg)
|
||||
|
||||
if resp.entity is None:
|
||||
msg = (
|
||||
"get_volume_status() failure: unable to deserialize"
|
||||
" response from get_volume_info() call")
|
||||
self._log.error(msg)
|
||||
raise Exception(msg)
|
||||
|
||||
return resp.entity.status
|
||||
|
||||
def verify_volume_status_progression_during_attachment(
|
||||
self, volume_id, state_list=None):
|
||||
|
||||
def _get_volume_status(self, volume_id):
|
||||
resp = self.volumes_client.get_volume_info(volume_id=volume_id)
|
||||
if not resp.ok:
|
||||
msg = (
|
||||
"get_volume_status() failure: get_volume_info() call"
|
||||
" failed with a {0} status code".format(resp.status_code))
|
||||
self._log.error(msg)
|
||||
raise Exception(msg)
|
||||
|
||||
if resp.entity is None:
|
||||
msg = (
|
||||
"get_volume_status() failure: unable to deserialize"
|
||||
" response from get_volume_info() call")
|
||||
self._log.error(msg)
|
||||
raise Exception(msg)
|
||||
|
||||
return resp.entity.status
|
||||
|
||||
verifier = StatusProgressionVerifier(
|
||||
'volume', volume_id, _get_volume_status, *[self, volume_id])
|
||||
'volume', volume_id, self._get_volume_status, volume_id)
|
||||
verifier.set_global_state_properties(
|
||||
timeout=self.config.attachment_timeout)
|
||||
|
||||
verifier.add_state(
|
||||
expected_statuses=['available'],
|
||||
acceptable_statuses=['attaching', 'in-use'],
|
||||
error_statuses=['error', 'creating'],
|
||||
timeout=10, poll_rate=1, poll_failure_retry_limit=3)
|
||||
poll_rate=self.config.api_poll_rate,
|
||||
poll_failure_retry_limit=3)
|
||||
|
||||
verifier.add_state(
|
||||
expected_statuses=['attaching'],
|
||||
acceptable_statuses=['in-use'],
|
||||
error_statuses=['error', 'creating'],
|
||||
timeout=self.config.attachment_timeout,
|
||||
poll_rate=self.config.api_poll_rate,
|
||||
poll_failure_retry_limit=3)
|
||||
|
||||
verifier.add_state(
|
||||
expected_statuses=['in-use'],
|
||||
error_statuses=['available', 'error', 'creating'],
|
||||
timeout=self.config.attachment_timeout,
|
||||
poll_rate=self.config.api_poll_rate,
|
||||
poll_failure_retry_limit=3)
|
||||
|
||||
verifier.start()
|
||||
|
||||
def verify_volume_status_progression_during_detachment(
|
||||
self, volume_id, raise_on_error=True):
|
||||
"""
|
||||
Track the status progression of volume volume_id being detached.
|
||||
|
||||
Optionally fails silently if rais_on_error is set to False.
|
||||
:param volume_id: the uuid of the volume being tracked
|
||||
:returns: None
|
||||
"""
|
||||
|
||||
verifier = StatusProgressionVerifier(
|
||||
'volume', volume_id, self._get_volume_status, volume_id)
|
||||
verifier.set_global_state_properties(
|
||||
timeout=self.config.attachment_timeout)
|
||||
|
||||
verifier.add_state(
|
||||
expected_statuses=['in-use'],
|
||||
acceptable_statuses=['detaching', 'available'],
|
||||
error_statuses=['error', 'attaching', 'creating', 'deleting'],
|
||||
poll_rate=self.config.api_poll_rate,
|
||||
poll_failure_retry_limit=3)
|
||||
|
||||
verifier.add_state(
|
||||
expected_statuses=['detaching'],
|
||||
acceptable_statuses=['available'],
|
||||
error_statuses=['error', 'attaching', 'creating', 'deleting'],
|
||||
poll_rate=self.config.api_poll_rate,
|
||||
poll_failure_retry_limit=3)
|
||||
|
||||
verifier.add_state(
|
||||
expected_statuses=['available'],
|
||||
error_statuses=['error', 'attaching', 'creating', 'deleting'],
|
||||
poll_rate=self.config.api_poll_rate,
|
||||
poll_failure_retry_limit=3)
|
||||
|
||||
try:
|
||||
verifier.start()
|
||||
except Exception as exception:
|
||||
if raise_on_error:
|
||||
raise exception
|
||||
|
||||
def delete_volume_attachment(
|
||||
self, attachment_id, server_id, timeout=None, poll_rate=None):
|
||||
"""Waits timeout seconds for volume attachment to 404 after issuing
|
||||
|
@ -0,0 +1,15 @@
|
||||
"""
|
||||
Copyright 2015 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.
|
||||
"""
|
@ -0,0 +1,89 @@
|
||||
"""
|
||||
Copyright 2015 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 collections import OrderedDict
|
||||
from mock import MagicMock, Mock
|
||||
from requests import Response
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
from cafe.common.reporting import cclogging
|
||||
from cafe.configurator.managers import _lazy_property
|
||||
cclogging.init_root_log_handler()
|
||||
from cloudcafe.common.behaviors import StatusProgressionVerifierError
|
||||
from cloudcafe.compute.volume_attachments_api.behaviors import \
|
||||
VolumeAttachmentsAPI_Behaviors
|
||||
from cloudcafe.compute.volume_attachments_api.client import \
|
||||
VolumeAttachmentsAPIClient
|
||||
from cloudcafe.compute.volume_attachments_api.config import \
|
||||
VolumeAttachmentsAPIConfig
|
||||
from cloudcafe.blockstorage.volumes_api.v2.client import \
|
||||
VolumesClient
|
||||
from cloudcafe.blockstorage.volumes_api.v2.models.responses import \
|
||||
VolumeResponse
|
||||
|
||||
class MockBuilder(object):
|
||||
|
||||
def _mock(self, name, **defaults):
|
||||
defaults.update(**self._overrides.get(name, {}))
|
||||
setattr(self, name, Mock(**defaults))
|
||||
|
||||
|
||||
class TestMocks(MockBuilder):
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self._overrides = kwargs
|
||||
self._mock(
|
||||
'volume_model', spec=VolumeResponse, status='in-use', id_='111111')
|
||||
self._mock(
|
||||
'response', spec=Response, ok=True, entity=self.volume_model)
|
||||
self._mock(
|
||||
'volumes_client', spec=VolumesClient,
|
||||
get_volume_info=MagicMock(return_value=self.response))
|
||||
self._mock(
|
||||
'volume_attachments_client', spec=VolumeAttachmentsAPIClient)
|
||||
self._mock(
|
||||
'volume_attachments_config', spec=VolumeAttachmentsAPIConfig,
|
||||
attachment_timeout=1, api_poll_rate=1)
|
||||
|
||||
class BaseTestCase(object):
|
||||
mocks = TestMocks()
|
||||
|
||||
def behavior_class_under_test(self):
|
||||
return VolumeAttachmentsAPI_Behaviors(
|
||||
self.mocks.volume_attachments_client,
|
||||
self.mocks.volume_attachments_config,
|
||||
self.mocks.volumes_client)
|
||||
|
||||
def setUp(self):
|
||||
self.behaviors = self.behavior_class_under_test()
|
||||
|
||||
|
||||
class MethodTests_verify_volume_status_progression_during_attachment(
|
||||
BaseTestCase, unittest.TestCase):
|
||||
|
||||
def test_volume_is_attached(self):
|
||||
r = self.behaviors.verify_volume_status_progression_during_attachment(
|
||||
self.mocks.volume_model.id_)
|
||||
|
||||
|
||||
class MethodTests_verify_volume_status_progression_during_detachment(
|
||||
BaseTestCase, unittest.TestCase):
|
||||
mocks = TestMocks(volume_model=dict(status='available'))
|
||||
|
||||
def test_volume_is_detached(self):
|
||||
r = self.behaviors.verify_volume_status_progression_during_detachment(
|
||||
self.mocks.volume_model.id_)
|
Loading…
x
Reference in New Issue
Block a user