def test_inconsistent_scale_state_inconsistent_scaling_nodes(self):
        """
        Inconsistent scale state: different scaling actions at a time are not allowed.
        In case node instance is not in a terminal state (as set on the NodeInstance object),
        we check the state directly on the Run (via CloudWrapper._get_runtime_parameter()).

        Inconsistent scaling nodes: only one node type at a time is allowed to be scaled.
        """
        def _get_runtime_parameter(key):
            if key.startswith('n.'):
                return CloudWrapper.SCALE_STATE_RESIZING
            elif key.startswith('m.'):
                return CloudWrapper.SCALE_STATE_DISK_ATTACHING
            else:
                return 'unknown'

        node_instances = {
            'n.1': NodeInstance({NodeDecorator.NODE_INSTANCE_NAME_KEY: 'n.1',
                                 NodeDecorator.NODE_NAME_KEY: 'n',
                                 NodeDecorator.SCALE_STATE_KEY: 'not terminal'}),
            'n.2': NodeInstance({NodeDecorator.NODE_INSTANCE_NAME_KEY: 'n.2',
                                 NodeDecorator.NODE_NAME_KEY: 'n',
                                 NodeDecorator.SCALE_STATE_KEY: CloudWrapper.SCALE_STATE_OPERATIONAL}),
            'm.1': NodeInstance({NodeDecorator.NODE_INSTANCE_NAME_KEY: 'm.1',
                                 NodeDecorator.NODE_NAME_KEY: 'm',
                                 NodeDecorator.SCALE_STATE_KEY: 'not terminal'})
        }

        cw = CloudWrapper(self.config_holder)
        cw._get_runtime_parameter = Mock(side_effect=_get_runtime_parameter)
        cw._get_nodes_instances = Mock(return_value=node_instances)

        scale_states = cw._get_effective_scale_states()

        assert 2 == len(scale_states)

        assert CloudWrapper.SCALE_STATE_RESIZING in scale_states
        assert 1 == len(scale_states[CloudWrapper.SCALE_STATE_RESIZING])
        assert ['n.1'] == scale_states[CloudWrapper.SCALE_STATE_RESIZING]

        assert CloudWrapper.SCALE_STATE_DISK_ATTACHING in scale_states
        assert 1 == len(scale_states[CloudWrapper.SCALE_STATE_DISK_ATTACHING])
        assert ['m.1'] == scale_states[CloudWrapper.SCALE_STATE_DISK_ATTACHING]

        self.failUnlessRaises(InconsistentScaleStateError, cw._get_global_scale_state)
        self.failUnlessRaises(InconsistentScaleStateError, cw.is_vertical_scaling)
        self.failUnlessRaises(InconsistentScaleStateError, cw.check_scale_state_consistency)
        self.failUnlessRaises(InconsistentScalingNodesError, cw.get_scaling_node_and_instance_names)
    def test_consistent_scale_state_consistent_scaling_nodes(self):
        """
        Consistent scale state: different scaling actions at a time are not allowed.
        In case node instance is not in a terminal state (as set on the NodeInstance object),
        we check the state directly on the Run (via CloudWrapper._get_runtime_parameter()).

        Consistent scaling nodes: only one node type at a time is allowed to be scaled.
        """
        def _get_runtime_parameter(key):
            if key.endswith(NodeDecorator.NODE_PROPERTY_SEPARATOR + NodeDecorator.SCALE_STATE_KEY):
                return CloudWrapper.SCALE_STATE_RESIZING
            else:
                return 'unknown'

        node_instances = {
            'n.1': NodeInstance({NodeDecorator.NODE_INSTANCE_NAME_KEY: 'n.1',
                                 NodeDecorator.NODE_NAME_KEY: 'n',
                                 NodeDecorator.SCALE_STATE_KEY: 'not terminal'}),
            'n.2': NodeInstance({NodeDecorator.NODE_INSTANCE_NAME_KEY: 'n.2',
                                 NodeDecorator.NODE_NAME_KEY: 'n',
                                 NodeDecorator.SCALE_STATE_KEY: 'not terminal'}),
            'm.1': NodeInstance({NodeDecorator.NODE_INSTANCE_NAME_KEY: 'm.1',
                                 NodeDecorator.NODE_NAME_KEY: 'm',
                                 NodeDecorator.SCALE_STATE_KEY: CloudWrapper.SCALE_STATE_OPERATIONAL})
        }

        cw = CloudWrapper(self.config_holder)
        cw._get_runtime_parameter = Mock(side_effect=_get_runtime_parameter)
        cw._get_nodes_instances = Mock(return_value=node_instances)

        scale_states = cw._get_effective_scale_states()

        assert 1 == len(scale_states)

        assert CloudWrapper.SCALE_STATE_RESIZING in scale_states
        assert 2 == len(scale_states[CloudWrapper.SCALE_STATE_RESIZING])
        assert ['n.1', 'n.2'] == sorted(scale_states[CloudWrapper.SCALE_STATE_RESIZING])

        assert CloudWrapper.SCALE_STATE_RESIZING == cw._get_global_scale_state()

        try:
            cw.check_scale_state_consistency()
        except InconsistentScaleStateError as ex:
            self.fail('Should not have failed with: %s' % str(ex))

        node_and_instances = cw.get_scaling_node_and_instance_names()
        assert 'n' == node_and_instances[0]
        assert ['n.1', 'n.2'] == sorted(node_and_instances[1])
    def test_consistent_scale_state_inconsistent_scaling_nodes(self):
        """
        Consistent scale state: only one scaling action at a time on different node instances.
        In case node instance is not in a terminal state (as set on the NodeInstance object),
        we check the state directly on the Run (via CloudWrapper._get_runtime_parameter()).

        Inconsistent scaling nodes: only one node type at a time is allowed to be scaled.
        """
        def _get_runtime_parameter(key):
            if key.endswith(NodeDecorator.NODE_PROPERTY_SEPARATOR + NodeDecorator.SCALE_STATE_KEY):
                return CloudWrapper.SCALE_STATE_RESIZING
            else:
                return 'unknown'

        node_instances = {
            'n.1': NodeInstance({NodeDecorator.NODE_INSTANCE_NAME_KEY: 'n.1',
                                 NodeDecorator.NODE_NAME_KEY: 'n',
                                 NodeDecorator.SCALE_STATE_KEY: 'not terminal'}),
            'n.2': NodeInstance({NodeDecorator.NODE_INSTANCE_NAME_KEY: 'n.2',
                                 NodeDecorator.NODE_NAME_KEY: 'n',
                                 NodeDecorator.SCALE_STATE_KEY: CloudWrapper.SCALE_STATE_OPERATIONAL}),
            'm.1': NodeInstance({NodeDecorator.NODE_INSTANCE_NAME_KEY: 'm.1',
                                 NodeDecorator.NODE_NAME_KEY: 'm',
                                 NodeDecorator.SCALE_STATE_KEY: 'not terminal'}),
            'm.2': NodeInstance({NodeDecorator.NODE_INSTANCE_NAME_KEY: 'm.2',
                                 NodeDecorator.NODE_NAME_KEY: 'm',
                                 NodeDecorator.SCALE_STATE_KEY: CloudWrapper.SCALE_STATE_GONE}),
        }

        cw = CloudWrapper(self.config_holder)
        cw._get_runtime_parameter = Mock(side_effect=_get_runtime_parameter)
        cw._get_nodes_instances = Mock(return_value=node_instances)

        scale_states = cw._get_effective_scale_states()

        assert 1 == len(scale_states)
        assert CloudWrapper.SCALE_STATE_RESIZING in scale_states
        assert 2 == len(scale_states[CloudWrapper.SCALE_STATE_RESIZING])
        assert ['m.1', 'n.1'] == sorted(scale_states[CloudWrapper.SCALE_STATE_RESIZING])

        assert CloudWrapper.SCALE_STATE_RESIZING == cw._get_global_scale_state()

        assert True == cw.is_vertical_scaling()

        self.failUnlessRaises(InconsistentScalingNodesError, cw.get_scaling_node_and_instance_names)
    def test_vertically_scalle_instances_nowait(self):
        _scale_state = None

        def _get_runtime_parameter(key):
            if key.endswith(NodeDecorator.NODE_PROPERTY_SEPARATOR + NodeDecorator.SCALE_STATE_KEY):
                return _scale_state
            else:
                return 'unknown'

        node_instances = {
            'n.1': NodeInstance({NodeDecorator.NODE_INSTANCE_NAME_KEY: 'n.1',
                                 NodeDecorator.NODE_NAME_KEY: 'n',
                                 NodeDecorator.SCALE_STATE_KEY: CloudWrapper.SCALE_STATE_RESIZING}),
            'n.2': NodeInstance({NodeDecorator.NODE_INSTANCE_NAME_KEY: 'n.2',
                                 NodeDecorator.NODE_NAME_KEY: 'n',
                                 NodeDecorator.SCALE_STATE_KEY: CloudWrapper.SCALE_STATE_DISK_ATTACHING}),
            'n.3': NodeInstance({NodeDecorator.NODE_INSTANCE_NAME_KEY: 'n.3',
                                 NodeDecorator.NODE_NAME_KEY: 'n',
                                 NodeDecorator.SCALE_STATE_KEY: CloudWrapper.SCALE_STATE_DISK_DETACHING}),
            'm.1': NodeInstance({NodeDecorator.NODE_INSTANCE_NAME_KEY: 'm.1',
                                 NodeDecorator.NODE_NAME_KEY: 'm',
                                 NodeDecorator.SCALE_STATE_KEY: CloudWrapper.SCALE_STATE_OPERATIONAL})
        }

        self.config_holder.set('verboseLevel', 3)
        setattr(self.config_holder, 'cloud', 'local')
        setattr(self.config_holder, CONFIGPARAM_CONNECTOR_MODULE_NAME,
                'slipstream.cloudconnectors.dummy.DummyClientCloud')

        cw = CloudWrapper(self.config_holder)
        cw._get_nodes_instances = Mock(return_value=node_instances)
        cw.initCloudConnector(self.config_holder)
        cw._set_runtime_parameter = Mock()

        cw._get_user_timeout = Mock(return_value=2)

        # No waiting.
        cw._wait_pre_scale_done = Mock()
        cw._wait_scale_state = Mock()

        _scale_state = 'resizing'
        cw._get_runtime_parameter = Mock(side_effect=_get_runtime_parameter)
        cw._cloud_client.resize = Mock(wraps=cw._cloud_client.resize)
        cw.vertically_scale_instances()
        assert True == cw._cloud_client.resize.called
        node_instance = cw._cloud_client.resize.call_args[0][0][0]
        assert 'n.1' in node_instance.get_name()
        assert cw._set_runtime_parameter.called_with('n.1:' + NodeDecorator.SCALE_IAAS_DONE, 'true')

        _scale_state = 'disk_attaching'
        cw._get_runtime_parameter = Mock(side_effect=_get_runtime_parameter)
        cw._cloud_client.attach_disk = Mock(wraps=cw._cloud_client.attach_disk)
        cw.vertically_scale_instances()
        assert True == cw._cloud_client.attach_disk.called
        node_instance = cw._cloud_client.attach_disk.call_args[0][0][0]
        assert 'n.2' in node_instance.get_name()
        assert cw._set_runtime_parameter.called_with('n.2:' + NodeDecorator.SCALE_IAAS_DONE, 'true')

        _scale_state = 'disk_detaching'
        cw._get_runtime_parameter = Mock(side_effect=_get_runtime_parameter)
        cw._cloud_client.detach_disk = Mock(wraps=cw._cloud_client.detach_disk)
        cw.vertically_scale_instances()
        assert True == cw._cloud_client.detach_disk.called
        node_instance = cw._cloud_client.detach_disk.call_args[0][0][0]
        assert 'n.3' in node_instance.get_name()
        assert cw._set_runtime_parameter.called_with('n.3:' + NodeDecorator.SCALE_IAAS_DONE, 'true')
    def test_vertically_scalle_instances_nowait(self):
        _scale_state = None

        def _get_runtime_parameter(key):
            if key.endswith(NodeDecorator.NODE_PROPERTY_SEPARATOR +
                            NodeDecorator.SCALE_STATE_KEY):
                return _scale_state
            else:
                return 'unknown'

        node_instances = {
            'n.1':
            NodeInstance({
                NodeDecorator.NODE_INSTANCE_NAME_KEY:
                'n.1',
                NodeDecorator.NODE_NAME_KEY:
                'n',
                NodeDecorator.SCALE_STATE_KEY:
                CloudWrapper.SCALE_STATE_RESIZING
            }),
            'n.2':
            NodeInstance({
                NodeDecorator.NODE_INSTANCE_NAME_KEY:
                'n.2',
                NodeDecorator.NODE_NAME_KEY:
                'n',
                NodeDecorator.SCALE_STATE_KEY:
                CloudWrapper.SCALE_STATE_DISK_ATTACHING
            }),
            'n.3':
            NodeInstance({
                NodeDecorator.NODE_INSTANCE_NAME_KEY:
                'n.3',
                NodeDecorator.NODE_NAME_KEY:
                'n',
                NodeDecorator.SCALE_STATE_KEY:
                CloudWrapper.SCALE_STATE_DISK_DETACHING
            }),
            'm.1':
            NodeInstance({
                NodeDecorator.NODE_INSTANCE_NAME_KEY:
                'm.1',
                NodeDecorator.NODE_NAME_KEY:
                'm',
                NodeDecorator.SCALE_STATE_KEY:
                CloudWrapper.SCALE_STATE_OPERATIONAL
            })
        }

        self.config_holder.set('verboseLevel', 3)
        setattr(self.config_holder, 'cloud', 'local')
        setattr(self.config_holder, CONFIGPARAM_CONNECTOR_MODULE_NAME,
                'slipstream.cloudconnectors.dummy.DummyClientCloud')

        cw = CloudWrapper(self.config_holder)
        cw._get_nodes_instances = Mock(return_value=node_instances)
        cw.initCloudConnector(self.config_holder)
        cw._set_runtime_parameter = Mock()

        cw._get_user_timeout = Mock(return_value=2)

        # No waiting.
        cw._wait_pre_scale_done = Mock()
        cw._wait_scale_state = Mock()

        _scale_state = 'resizing'
        cw._get_runtime_parameter = Mock(side_effect=_get_runtime_parameter)
        cw._cloud_client.resize = Mock(wraps=cw._cloud_client.resize)
        cw.vertically_scale_instances()
        assert True == cw._cloud_client.resize.called
        node_instance = cw._cloud_client.resize.call_args[0][0][0]
        assert 'n.1' in node_instance.get_name()
        assert cw._set_runtime_parameter.called_with(
            'n.1:' + NodeDecorator.SCALE_IAAS_DONE, 'true')

        _scale_state = 'disk_attaching'
        cw._get_runtime_parameter = Mock(side_effect=_get_runtime_parameter)
        cw._cloud_client.attach_disk = Mock(wraps=cw._cloud_client.attach_disk)
        cw.vertically_scale_instances()
        assert True == cw._cloud_client.attach_disk.called
        node_instance = cw._cloud_client.attach_disk.call_args[0][0][0]
        assert 'n.2' in node_instance.get_name()
        assert cw._set_runtime_parameter.called_with(
            'n.2:' + NodeDecorator.SCALE_IAAS_DONE, 'true')

        _scale_state = 'disk_detaching'
        cw._get_runtime_parameter = Mock(side_effect=_get_runtime_parameter)
        cw._cloud_client.detach_disk = Mock(wraps=cw._cloud_client.detach_disk)
        cw.vertically_scale_instances()
        assert True == cw._cloud_client.detach_disk.called
        node_instance = cw._cloud_client.detach_disk.call_args[0][0][0]
        assert 'n.3' in node_instance.get_name()
        assert cw._set_runtime_parameter.called_with(
            'n.3:' + NodeDecorator.SCALE_IAAS_DONE, 'true')
    def test_consistent_scale_state_consistent_scaling_nodes(self):
        """
        Consistent scale state: different scaling actions at a time are not allowed.
        In case node instance is not in a terminal state (as set on the NodeInstance object),
        we check the state directly on the Run (via CloudWrapper._get_runtime_parameter()).

        Consistent scaling nodes: only one node type at a time is allowed to be scaled.
        """
        def _get_runtime_parameter(key):
            if key.endswith(NodeDecorator.NODE_PROPERTY_SEPARATOR +
                            NodeDecorator.SCALE_STATE_KEY):
                return CloudWrapper.SCALE_STATE_RESIZING
            else:
                return 'unknown'

        node_instances = {
            'n.1':
            NodeInstance({
                NodeDecorator.NODE_INSTANCE_NAME_KEY: 'n.1',
                NodeDecorator.NODE_NAME_KEY: 'n',
                NodeDecorator.SCALE_STATE_KEY: 'not terminal'
            }),
            'n.2':
            NodeInstance({
                NodeDecorator.NODE_INSTANCE_NAME_KEY: 'n.2',
                NodeDecorator.NODE_NAME_KEY: 'n',
                NodeDecorator.SCALE_STATE_KEY: 'not terminal'
            }),
            'm.1':
            NodeInstance({
                NodeDecorator.NODE_INSTANCE_NAME_KEY:
                'm.1',
                NodeDecorator.NODE_NAME_KEY:
                'm',
                NodeDecorator.SCALE_STATE_KEY:
                CloudWrapper.SCALE_STATE_OPERATIONAL
            })
        }

        cw = CloudWrapper(self.config_holder)
        cw._get_runtime_parameter = Mock(side_effect=_get_runtime_parameter)
        cw._get_nodes_instances = Mock(return_value=node_instances)

        scale_states = cw._get_effective_scale_states()

        assert 1 == len(scale_states)

        assert CloudWrapper.SCALE_STATE_RESIZING in scale_states
        assert 2 == len(scale_states[CloudWrapper.SCALE_STATE_RESIZING])
        assert ['n.1', 'n.2'
                ] == sorted(scale_states[CloudWrapper.SCALE_STATE_RESIZING])

        assert CloudWrapper.SCALE_STATE_RESIZING == cw._get_global_scale_state(
        )

        try:
            cw.check_scale_state_consistency()
        except InconsistentScaleStateError as ex:
            self.fail('Should not have failed with: %s' % str(ex))

        node_and_instances = cw.get_scaling_node_and_instance_names()
        assert 'n' == node_and_instances[0]
        assert ['n.1', 'n.2'] == sorted(node_and_instances[1])
    def test_inconsistent_scale_state_inconsistent_scaling_nodes(self):
        """
        Inconsistent scale state: different scaling actions at a time are not allowed.
        In case node instance is not in a terminal state (as set on the NodeInstance object),
        we check the state directly on the Run (via CloudWrapper._get_runtime_parameter()).

        Inconsistent scaling nodes: only one node type at a time is allowed to be scaled.
        """
        def _get_runtime_parameter(key):
            if key.startswith('n.'):
                return CloudWrapper.SCALE_STATE_RESIZING
            elif key.startswith('m.'):
                return CloudWrapper.SCALE_STATE_DISK_ATTACHING
            else:
                return 'unknown'

        node_instances = {
            'n.1':
            NodeInstance({
                NodeDecorator.NODE_INSTANCE_NAME_KEY: 'n.1',
                NodeDecorator.NODE_NAME_KEY: 'n',
                NodeDecorator.SCALE_STATE_KEY: 'not terminal'
            }),
            'n.2':
            NodeInstance({
                NodeDecorator.NODE_INSTANCE_NAME_KEY:
                'n.2',
                NodeDecorator.NODE_NAME_KEY:
                'n',
                NodeDecorator.SCALE_STATE_KEY:
                CloudWrapper.SCALE_STATE_OPERATIONAL
            }),
            'm.1':
            NodeInstance({
                NodeDecorator.NODE_INSTANCE_NAME_KEY: 'm.1',
                NodeDecorator.NODE_NAME_KEY: 'm',
                NodeDecorator.SCALE_STATE_KEY: 'not terminal'
            })
        }

        cw = CloudWrapper(self.config_holder)
        cw._get_runtime_parameter = Mock(side_effect=_get_runtime_parameter)
        cw._get_nodes_instances = Mock(return_value=node_instances)

        scale_states = cw._get_effective_scale_states()

        assert 2 == len(scale_states)

        assert CloudWrapper.SCALE_STATE_RESIZING in scale_states
        assert 1 == len(scale_states[CloudWrapper.SCALE_STATE_RESIZING])
        assert ['n.1'] == scale_states[CloudWrapper.SCALE_STATE_RESIZING]

        assert CloudWrapper.SCALE_STATE_DISK_ATTACHING in scale_states
        assert 1 == len(scale_states[CloudWrapper.SCALE_STATE_DISK_ATTACHING])
        assert ['m.1'] == scale_states[CloudWrapper.SCALE_STATE_DISK_ATTACHING]

        self.failUnlessRaises(InconsistentScaleStateError,
                              cw._get_global_scale_state)
        self.failUnlessRaises(InconsistentScaleStateError,
                              cw.is_vertical_scaling)
        self.failUnlessRaises(InconsistentScaleStateError,
                              cw.check_scale_state_consistency)
        self.failUnlessRaises(InconsistentScalingNodesError,
                              cw.get_scaling_node_and_instance_names)
    def test_consistent_scale_state_inconsistent_scaling_nodes(self):
        """
        Consistent scale state: only one scaling action at a time on different node instances.
        In case node instance is not in a terminal state (as set on the NodeInstance object),
        we check the state directly on the Run (via CloudWrapper._get_runtime_parameter()).

        Inconsistent scaling nodes: only one node type at a time is allowed to be scaled.
        """
        def _get_runtime_parameter(key):
            if key.endswith(NodeDecorator.NODE_PROPERTY_SEPARATOR +
                            NodeDecorator.SCALE_STATE_KEY):
                return CloudWrapper.SCALE_STATE_RESIZING
            else:
                return 'unknown'

        node_instances = {
            'n.1':
            NodeInstance({
                NodeDecorator.NODE_INSTANCE_NAME_KEY: 'n.1',
                NodeDecorator.NODE_NAME_KEY: 'n',
                NodeDecorator.SCALE_STATE_KEY: 'not terminal'
            }),
            'n.2':
            NodeInstance({
                NodeDecorator.NODE_INSTANCE_NAME_KEY:
                'n.2',
                NodeDecorator.NODE_NAME_KEY:
                'n',
                NodeDecorator.SCALE_STATE_KEY:
                CloudWrapper.SCALE_STATE_OPERATIONAL
            }),
            'm.1':
            NodeInstance({
                NodeDecorator.NODE_INSTANCE_NAME_KEY: 'm.1',
                NodeDecorator.NODE_NAME_KEY: 'm',
                NodeDecorator.SCALE_STATE_KEY: 'not terminal'
            }),
            'm.2':
            NodeInstance({
                NodeDecorator.NODE_INSTANCE_NAME_KEY:
                'm.2',
                NodeDecorator.NODE_NAME_KEY:
                'm',
                NodeDecorator.SCALE_STATE_KEY:
                CloudWrapper.SCALE_STATE_GONE
            }),
        }

        cw = CloudWrapper(self.config_holder)
        cw._get_runtime_parameter = Mock(side_effect=_get_runtime_parameter)
        cw._get_nodes_instances = Mock(return_value=node_instances)

        scale_states = cw._get_effective_scale_states()

        assert 1 == len(scale_states)
        assert CloudWrapper.SCALE_STATE_RESIZING in scale_states
        assert 2 == len(scale_states[CloudWrapper.SCALE_STATE_RESIZING])
        assert ['m.1', 'n.1'
                ] == sorted(scale_states[CloudWrapper.SCALE_STATE_RESIZING])

        assert CloudWrapper.SCALE_STATE_RESIZING == cw._get_global_scale_state(
        )

        assert True == cw.is_vertical_scaling()

        self.failUnlessRaises(InconsistentScalingNodesError,
                              cw.get_scaling_node_and_instance_names)