Introduce ability to execute custom flow
Flow deploy driver is now capable of executing customly defined flow of actions. When custom flow is chooen, then the flow itself must be provided within input data as a list of actions under 'custom_flow' key. This is the only beginning and only 2 flows are made available: custom and nailgun. Partially implements blueprint: pluggable-do-actions Change-Id: I3cf3fcabb47bdc7d1aadb97614310ee3184ed77c
This commit is contained in:
parent
3fb08a05ec
commit
2409996980
@ -28,9 +28,8 @@ class BaseDataDriver(object):
|
||||
|
||||
def __init__(self, data):
|
||||
self.data = copy.deepcopy(data)
|
||||
self.flow = []
|
||||
if self.data and self.data.get('flow'):
|
||||
self.flow = self.data.get('flow', [])
|
||||
if 'flow' in self.data:
|
||||
self.flow = self.data['flow']
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
|
@ -15,21 +15,42 @@
|
||||
|
||||
from bareon.drivers.deploy import base
|
||||
from bareon.drivers.deploy import mixins
|
||||
from bareon import errors
|
||||
from bareon.openstack.common import log as logging
|
||||
|
||||
import stevedore.named
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
FLOWS = {
|
||||
'nailgun': [
|
||||
'do_partitioning',
|
||||
'do_configdrive',
|
||||
'do_copyimage',
|
||||
'do_bootloader'
|
||||
],
|
||||
'custom': [],
|
||||
}
|
||||
|
||||
|
||||
class Flow(base.SimpleDeployDriver, mixins.MountableMixin):
|
||||
|
||||
def __init__(self, data_driver):
|
||||
super(Flow, self).__init__(data_driver)
|
||||
requested_flow = self.driver.flow
|
||||
if requested_flow in FLOWS:
|
||||
if requested_flow == 'custom':
|
||||
flow = self.driver.data.get('custom_flow', [])
|
||||
if not flow:
|
||||
raise errors.EmptyCustomFlow(
|
||||
'Requested custom flow was empty')
|
||||
else:
|
||||
flow = FLOWS[requested_flow]
|
||||
else:
|
||||
raise errors.NonexistingFlow(
|
||||
"Requested flow doesn't exist: %s" % requested_flow)
|
||||
self.ext_mgr = stevedore.named.NamedExtensionManager(
|
||||
'bareon.actions', names=self.driver.flow,
|
||||
invoke_on_load=True, invoke_args=(self.driver, ),
|
||||
name_order=True)
|
||||
'bareon.actions', names=flow, invoke_on_load=True,
|
||||
invoke_args=(self.driver, ), name_order=True)
|
||||
|
||||
def execute_flow(self):
|
||||
for action in self.ext_mgr:
|
||||
|
@ -182,3 +182,11 @@ class IncorrectChroot(BaseError):
|
||||
|
||||
class TooManyKernels(BaseError):
|
||||
pass
|
||||
|
||||
|
||||
class EmptyCustomFlow(BaseError):
|
||||
pass
|
||||
|
||||
|
||||
class NonexistingFlow(BaseError):
|
||||
pass
|
||||
|
@ -17,6 +17,7 @@ import unittest2
|
||||
|
||||
from bareon.actions import base as base_action
|
||||
from bareon.drivers.deploy import flow
|
||||
from bareon import errors
|
||||
|
||||
import stevedore
|
||||
|
||||
@ -28,12 +29,18 @@ elif six.PY3:
|
||||
|
||||
class TestFlowDriver(unittest2.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestFlowDriver, self).setUp()
|
||||
self.data_drv = mock.Mock(data={}, flow='custom')
|
||||
|
||||
@mock.patch.object(flow.Flow, '__init__',
|
||||
return_value=None)
|
||||
def test_execute_flow(self, mock_init):
|
||||
@mock.patch.object(flow, 'FLOWS', return_value={'fake_flow': ['foo']})
|
||||
def test_execute_flow(self, mock_flows, mock_init):
|
||||
fake_ext = mock.Mock(spec=base_action.BaseAction)
|
||||
fake_ext.name = 'foo'
|
||||
self.drv = flow.Flow('fake_data_driver')
|
||||
self.data_drv.flow = 'fake_flow'
|
||||
self.drv = flow.Flow(self.data_drv)
|
||||
self.drv.ext_mgr = stevedore.NamedExtensionManager.make_test_instance(
|
||||
[fake_ext], namespace='TESTING')
|
||||
self.drv.execute_flow()
|
||||
@ -42,12 +49,18 @@ class TestFlowDriver(unittest2.TestCase):
|
||||
fake_ext.execute.assert_called_once_with()
|
||||
|
||||
@mock.patch('stevedore.named.NamedExtensionManager')
|
||||
def test_init(self, mock_stevedore):
|
||||
fake_data_driver = mock.Mock()
|
||||
def test_init_succesfull_custom_flow(self, mock_stevedore):
|
||||
expected_flow = ['action1', 'action3']
|
||||
fake_data_driver.flow = expected_flow
|
||||
self.drv = flow.Flow(fake_data_driver)
|
||||
self.data_drv.data['custom_flow'] = expected_flow
|
||||
self.drv = flow.Flow(self.data_drv)
|
||||
mock_stevedore.assert_called_once_with(
|
||||
'bareon.actions', names=expected_flow,
|
||||
invoke_on_load=True, invoke_args=(fake_data_driver,),
|
||||
invoke_on_load=True, invoke_args=(self.data_drv,),
|
||||
name_order=True)
|
||||
|
||||
def test_init_empty_custom_flow_failed(self):
|
||||
self.assertRaises(errors.EmptyCustomFlow, flow.Flow, self.data_drv)
|
||||
|
||||
def test_init_invalid_flow_failed(self):
|
||||
self.data_drv.flow = 'invalid'
|
||||
self.assertRaises(errors.NonexistingFlow, flow.Flow, self.data_drv)
|
||||
|
@ -22,7 +22,7 @@ from bareon.drivers.data import generic
|
||||
class TestKsDisks(unittest2.TestCase):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(TestKsDisks, self).__init__(*args, **kwargs)
|
||||
self.driver = generic.GenericDataDriver(None)
|
||||
self.driver = generic.GenericDataDriver({})
|
||||
self.driver._partition_data = self.mock_part_data = mock.MagicMock()
|
||||
|
||||
def test_no_partition_data(self):
|
||||
@ -74,7 +74,7 @@ class TestKsDisks(unittest2.TestCase):
|
||||
class TestKsVgs(unittest2.TestCase):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(TestKsVgs, self).__init__(*args, **kwargs)
|
||||
self.driver = generic.GenericDataDriver(None)
|
||||
self.driver = generic.GenericDataDriver({})
|
||||
self.driver._partition_data = self.mock_part_data = mock.MagicMock()
|
||||
|
||||
def test_no_partition_data(self):
|
||||
@ -114,7 +114,7 @@ class TestKsVgs(unittest2.TestCase):
|
||||
class TestSmallKsDisks(unittest2.TestCase):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(TestSmallKsDisks, self).__init__(*args, **kwargs)
|
||||
self.driver = generic.GenericDataDriver(None)
|
||||
self.driver = generic.GenericDataDriver({})
|
||||
self.driver._partition_data = self.mock_part_data = mock.MagicMock()
|
||||
|
||||
def test_no_partition_data(self):
|
||||
@ -154,7 +154,7 @@ class TestSmallKsDisks(unittest2.TestCase):
|
||||
class TestGetLabel(unittest2.TestCase):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(TestGetLabel, self).__init__(*args, **kwargs)
|
||||
self.driver = generic.GenericDataDriver(None)
|
||||
self.driver = generic.GenericDataDriver({})
|
||||
|
||||
def test_no_label(self):
|
||||
label = None
|
||||
|
@ -52,7 +52,7 @@ class TestGetImageSchema(unittest2.TestCase):
|
||||
class TestMatchDevice(unittest2.TestCase):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(TestMatchDevice, self).__init__(*args, **kwargs)
|
||||
self.driver = ironic.Ironic(None)
|
||||
self.driver = ironic.Ironic({})
|
||||
|
||||
def test_match_list_value(self):
|
||||
test_type = 'path'
|
||||
@ -98,7 +98,7 @@ class TestMatchDevice(unittest2.TestCase):
|
||||
class TestDiskDev(unittest2.TestCase):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(TestDiskDev, self).__init__(*args, **kwargs)
|
||||
self.driver = ironic.Ironic(None)
|
||||
self.driver = ironic.Ironic({})
|
||||
self.driver._match_device = self.mock_match_device = mock.MagicMock()
|
||||
|
||||
def test_no_valid_disks(self):
|
||||
@ -136,7 +136,7 @@ class TestDiskDev(unittest2.TestCase):
|
||||
class TestMatchPartition(unittest2.TestCase):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(TestMatchPartition, self).__init__(*args, **kwargs)
|
||||
self.driver = ironic.Ironic(None)
|
||||
self.driver = ironic.Ironic({})
|
||||
|
||||
def test_match_list_value(self):
|
||||
test_type = 'path'
|
||||
@ -224,7 +224,7 @@ class TestMatchPartition(unittest2.TestCase):
|
||||
class TestDiskPartition(unittest2.TestCase):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(TestDiskPartition, self).__init__(*args, **kwargs)
|
||||
self.driver = ironic.Ironic(None)
|
||||
self.driver = ironic.Ironic({})
|
||||
self.driver._match_data_by_pattern = \
|
||||
self.mock_match_part = mock.MagicMock()
|
||||
|
||||
@ -265,7 +265,7 @@ class TestDiskPartition(unittest2.TestCase):
|
||||
class TestGetPartitionIds(unittest2.TestCase):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(TestGetPartitionIds, self).__init__(*args, **kwargs)
|
||||
self.driver = ironic.Ironic(None)
|
||||
self.driver = ironic.Ironic({})
|
||||
|
||||
def test_no_devices(self, mock_ids, mock_partitions):
|
||||
mock_partitions.return_value = []
|
||||
@ -306,7 +306,7 @@ class TestFindHwFstab(unittest2.TestCase):
|
||||
fss = [fs(mount='/', type='ext4', device='/dev/sda', os_id='1'),
|
||||
fs(mount='/usr', type='ext4', device='/dev/sdb', os_id='1')]
|
||||
|
||||
data_driver = ironic.Ironic(None)
|
||||
data_driver = ironic.Ironic({})
|
||||
data_driver._partition_scheme = ironic.objects.PartitionScheme()
|
||||
data_driver.partition_scheme.fss = fss
|
||||
|
||||
@ -325,7 +325,7 @@ class TestFindHwFstab(unittest2.TestCase):
|
||||
fs(mount='/usr', type='ext4', device='/dev/sdb', os_id='1'),
|
||||
fs(mount='/', type='ext4', device='/dev/sda', os_id='2')]
|
||||
|
||||
data_driver = ironic.Ironic(None)
|
||||
data_driver = ironic.Ironic({})
|
||||
data_driver._partition_scheme = ironic.objects.PartitionScheme()
|
||||
data_driver.partition_scheme.fss = fss
|
||||
|
||||
@ -346,7 +346,7 @@ class TestFindHwFstab(unittest2.TestCase):
|
||||
fss = [fs(mount='/etc', type='ext4', device='/dev/sda', os_id='1'),
|
||||
fs(mount='/', type='ext4', device='/dev/sda', os_id='1')]
|
||||
|
||||
data_driver = ironic.Ironic(None)
|
||||
data_driver = ironic.Ironic({})
|
||||
data_driver._partition_scheme = ironic.objects.PartitionScheme()
|
||||
data_driver.partition_scheme.fss = fss
|
||||
exec_mock.side_effect = [('stdout', 'stderr'),
|
||||
@ -863,7 +863,7 @@ class TestConvertPercentSizes(unittest2.TestCase):
|
||||
class TestProcessPartition(unittest2.TestCase):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(TestProcessPartition, self).__init__(*args, **kwargs)
|
||||
self.driver = ironic.Ironic(None)
|
||||
self.driver = ironic.Ironic({})
|
||||
self.driver._partition_data = self.mock_part_data = mock.MagicMock()
|
||||
self.driver._add_partition = self.mock_add_part = mock.MagicMock()
|
||||
self.mock_add_part.return_value = self.mock_part = mock.MagicMock()
|
||||
|
Loading…
Reference in New Issue
Block a user