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):
|
def __init__(self, data):
|
||||||
self.data = copy.deepcopy(data)
|
self.data = copy.deepcopy(data)
|
||||||
self.flow = []
|
if 'flow' in self.data:
|
||||||
if self.data and self.data.get('flow'):
|
self.flow = self.data['flow']
|
||||||
self.flow = self.data.get('flow', [])
|
|
||||||
|
|
||||||
|
|
||||||
@six.add_metaclass(abc.ABCMeta)
|
@six.add_metaclass(abc.ABCMeta)
|
||||||
|
@ -15,21 +15,42 @@
|
|||||||
|
|
||||||
from bareon.drivers.deploy import base
|
from bareon.drivers.deploy import base
|
||||||
from bareon.drivers.deploy import mixins
|
from bareon.drivers.deploy import mixins
|
||||||
|
from bareon import errors
|
||||||
from bareon.openstack.common import log as logging
|
from bareon.openstack.common import log as logging
|
||||||
|
|
||||||
import stevedore.named
|
import stevedore.named
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
FLOWS = {
|
||||||
|
'nailgun': [
|
||||||
|
'do_partitioning',
|
||||||
|
'do_configdrive',
|
||||||
|
'do_copyimage',
|
||||||
|
'do_bootloader'
|
||||||
|
],
|
||||||
|
'custom': [],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class Flow(base.SimpleDeployDriver, mixins.MountableMixin):
|
class Flow(base.SimpleDeployDriver, mixins.MountableMixin):
|
||||||
|
|
||||||
def __init__(self, data_driver):
|
def __init__(self, data_driver):
|
||||||
super(Flow, self).__init__(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(
|
self.ext_mgr = stevedore.named.NamedExtensionManager(
|
||||||
'bareon.actions', names=self.driver.flow,
|
'bareon.actions', names=flow, invoke_on_load=True,
|
||||||
invoke_on_load=True, invoke_args=(self.driver, ),
|
invoke_args=(self.driver, ), name_order=True)
|
||||||
name_order=True)
|
|
||||||
|
|
||||||
def execute_flow(self):
|
def execute_flow(self):
|
||||||
for action in self.ext_mgr:
|
for action in self.ext_mgr:
|
||||||
|
@ -182,3 +182,11 @@ class IncorrectChroot(BaseError):
|
|||||||
|
|
||||||
class TooManyKernels(BaseError):
|
class TooManyKernels(BaseError):
|
||||||
pass
|
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.actions import base as base_action
|
||||||
from bareon.drivers.deploy import flow
|
from bareon.drivers.deploy import flow
|
||||||
|
from bareon import errors
|
||||||
|
|
||||||
import stevedore
|
import stevedore
|
||||||
|
|
||||||
@ -28,12 +29,18 @@ elif six.PY3:
|
|||||||
|
|
||||||
class TestFlowDriver(unittest2.TestCase):
|
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__',
|
@mock.patch.object(flow.Flow, '__init__',
|
||||||
return_value=None)
|
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 = mock.Mock(spec=base_action.BaseAction)
|
||||||
fake_ext.name = 'foo'
|
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(
|
self.drv.ext_mgr = stevedore.NamedExtensionManager.make_test_instance(
|
||||||
[fake_ext], namespace='TESTING')
|
[fake_ext], namespace='TESTING')
|
||||||
self.drv.execute_flow()
|
self.drv.execute_flow()
|
||||||
@ -42,12 +49,18 @@ class TestFlowDriver(unittest2.TestCase):
|
|||||||
fake_ext.execute.assert_called_once_with()
|
fake_ext.execute.assert_called_once_with()
|
||||||
|
|
||||||
@mock.patch('stevedore.named.NamedExtensionManager')
|
@mock.patch('stevedore.named.NamedExtensionManager')
|
||||||
def test_init(self, mock_stevedore):
|
def test_init_succesfull_custom_flow(self, mock_stevedore):
|
||||||
fake_data_driver = mock.Mock()
|
|
||||||
expected_flow = ['action1', 'action3']
|
expected_flow = ['action1', 'action3']
|
||||||
fake_data_driver.flow = expected_flow
|
self.data_drv.data['custom_flow'] = expected_flow
|
||||||
self.drv = flow.Flow(fake_data_driver)
|
self.drv = flow.Flow(self.data_drv)
|
||||||
mock_stevedore.assert_called_once_with(
|
mock_stevedore.assert_called_once_with(
|
||||||
'bareon.actions', names=expected_flow,
|
'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)
|
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):
|
class TestKsDisks(unittest2.TestCase):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(TestKsDisks, self).__init__(*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()
|
self.driver._partition_data = self.mock_part_data = mock.MagicMock()
|
||||||
|
|
||||||
def test_no_partition_data(self):
|
def test_no_partition_data(self):
|
||||||
@ -74,7 +74,7 @@ class TestKsDisks(unittest2.TestCase):
|
|||||||
class TestKsVgs(unittest2.TestCase):
|
class TestKsVgs(unittest2.TestCase):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(TestKsVgs, self).__init__(*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()
|
self.driver._partition_data = self.mock_part_data = mock.MagicMock()
|
||||||
|
|
||||||
def test_no_partition_data(self):
|
def test_no_partition_data(self):
|
||||||
@ -114,7 +114,7 @@ class TestKsVgs(unittest2.TestCase):
|
|||||||
class TestSmallKsDisks(unittest2.TestCase):
|
class TestSmallKsDisks(unittest2.TestCase):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(TestSmallKsDisks, self).__init__(*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()
|
self.driver._partition_data = self.mock_part_data = mock.MagicMock()
|
||||||
|
|
||||||
def test_no_partition_data(self):
|
def test_no_partition_data(self):
|
||||||
@ -154,7 +154,7 @@ class TestSmallKsDisks(unittest2.TestCase):
|
|||||||
class TestGetLabel(unittest2.TestCase):
|
class TestGetLabel(unittest2.TestCase):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(TestGetLabel, self).__init__(*args, **kwargs)
|
super(TestGetLabel, self).__init__(*args, **kwargs)
|
||||||
self.driver = generic.GenericDataDriver(None)
|
self.driver = generic.GenericDataDriver({})
|
||||||
|
|
||||||
def test_no_label(self):
|
def test_no_label(self):
|
||||||
label = None
|
label = None
|
||||||
|
@ -52,7 +52,7 @@ class TestGetImageSchema(unittest2.TestCase):
|
|||||||
class TestMatchDevice(unittest2.TestCase):
|
class TestMatchDevice(unittest2.TestCase):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(TestMatchDevice, self).__init__(*args, **kwargs)
|
super(TestMatchDevice, self).__init__(*args, **kwargs)
|
||||||
self.driver = ironic.Ironic(None)
|
self.driver = ironic.Ironic({})
|
||||||
|
|
||||||
def test_match_list_value(self):
|
def test_match_list_value(self):
|
||||||
test_type = 'path'
|
test_type = 'path'
|
||||||
@ -98,7 +98,7 @@ class TestMatchDevice(unittest2.TestCase):
|
|||||||
class TestDiskDev(unittest2.TestCase):
|
class TestDiskDev(unittest2.TestCase):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(TestDiskDev, self).__init__(*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()
|
self.driver._match_device = self.mock_match_device = mock.MagicMock()
|
||||||
|
|
||||||
def test_no_valid_disks(self):
|
def test_no_valid_disks(self):
|
||||||
@ -136,7 +136,7 @@ class TestDiskDev(unittest2.TestCase):
|
|||||||
class TestMatchPartition(unittest2.TestCase):
|
class TestMatchPartition(unittest2.TestCase):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(TestMatchPartition, self).__init__(*args, **kwargs)
|
super(TestMatchPartition, self).__init__(*args, **kwargs)
|
||||||
self.driver = ironic.Ironic(None)
|
self.driver = ironic.Ironic({})
|
||||||
|
|
||||||
def test_match_list_value(self):
|
def test_match_list_value(self):
|
||||||
test_type = 'path'
|
test_type = 'path'
|
||||||
@ -224,7 +224,7 @@ class TestMatchPartition(unittest2.TestCase):
|
|||||||
class TestDiskPartition(unittest2.TestCase):
|
class TestDiskPartition(unittest2.TestCase):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(TestDiskPartition, self).__init__(*args, **kwargs)
|
super(TestDiskPartition, self).__init__(*args, **kwargs)
|
||||||
self.driver = ironic.Ironic(None)
|
self.driver = ironic.Ironic({})
|
||||||
self.driver._match_data_by_pattern = \
|
self.driver._match_data_by_pattern = \
|
||||||
self.mock_match_part = mock.MagicMock()
|
self.mock_match_part = mock.MagicMock()
|
||||||
|
|
||||||
@ -265,7 +265,7 @@ class TestDiskPartition(unittest2.TestCase):
|
|||||||
class TestGetPartitionIds(unittest2.TestCase):
|
class TestGetPartitionIds(unittest2.TestCase):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(TestGetPartitionIds, self).__init__(*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):
|
def test_no_devices(self, mock_ids, mock_partitions):
|
||||||
mock_partitions.return_value = []
|
mock_partitions.return_value = []
|
||||||
@ -306,7 +306,7 @@ class TestFindHwFstab(unittest2.TestCase):
|
|||||||
fss = [fs(mount='/', type='ext4', device='/dev/sda', os_id='1'),
|
fss = [fs(mount='/', type='ext4', device='/dev/sda', os_id='1'),
|
||||||
fs(mount='/usr', type='ext4', device='/dev/sdb', 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 = ironic.objects.PartitionScheme()
|
||||||
data_driver.partition_scheme.fss = fss
|
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='/usr', type='ext4', device='/dev/sdb', os_id='1'),
|
||||||
fs(mount='/', type='ext4', device='/dev/sda', os_id='2')]
|
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 = ironic.objects.PartitionScheme()
|
||||||
data_driver.partition_scheme.fss = fss
|
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'),
|
fss = [fs(mount='/etc', type='ext4', device='/dev/sda', os_id='1'),
|
||||||
fs(mount='/', 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 = ironic.objects.PartitionScheme()
|
||||||
data_driver.partition_scheme.fss = fss
|
data_driver.partition_scheme.fss = fss
|
||||||
exec_mock.side_effect = [('stdout', 'stderr'),
|
exec_mock.side_effect = [('stdout', 'stderr'),
|
||||||
@ -863,7 +863,7 @@ class TestConvertPercentSizes(unittest2.TestCase):
|
|||||||
class TestProcessPartition(unittest2.TestCase):
|
class TestProcessPartition(unittest2.TestCase):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(TestProcessPartition, self).__init__(*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._partition_data = self.mock_part_data = mock.MagicMock()
|
||||||
self.driver._add_partition = self.mock_add_part = mock.MagicMock()
|
self.driver._add_partition = self.mock_add_part = mock.MagicMock()
|
||||||
self.mock_add_part.return_value = self.mock_part = mock.MagicMock()
|
self.mock_add_part.return_value = self.mock_part = mock.MagicMock()
|
||||||
|
Loading…
Reference in New Issue
Block a user