def setUp(self):
     self.mock_multi_installer = MagicMock()
     self.mock_display_controller = MagicMock()
     self.loop = MagicMock()
     with NamedTemporaryFile(mode='w+', encoding='utf-8') as tempf:
         # Override config file to save to
         self.conf = Config({}, tempf.name)
Exemple #2
0
 def setUp(self):
     self._temp_conf = Config(GOOD_CONFIG, save_backups=False)
     with NamedTemporaryFile(mode='w+', encoding='utf-8') as tempf:
         # Override config file to save to
         self.conf = Config(self._temp_conf._config,
                            tempf.name,
                            save_backups=False)
    def setUp(self):
        with NamedTemporaryFile(mode='w+', encoding='utf-8') as tempf:
            # Override config file to save to
            self.conf = Config({}, tempf.name, save_backups=False)

        self.bad_states_int = [5, 6, 7]
        self.good_states_int = [0, 1, 2]
class LandscapeInstall:

    def __init__(self, opts, display_controller):
        self.config = Config()
        self.opts = opts
        self.display_controller = display_controller
        # Sets install type
        utils.spew(os.path.join(self.config.cfg_path,
                                'landscape'),
                   'auto-generated')

        self.landscape_tasks = ["Preparing Landscape",
                                "Deploying Landscape",
                                "Registering against Landscape"]

    def _do_install_existing_maas(self):
        """ Performs the landscape deployment with existing MAAS
        """
        MultiInstallExistingMaas(
            self.opts, self.display_controller,
            post_tasks=self.landscape_tasks).run()

    def _do_install_new_maas(self):
        """ Prepare new maas environment for landscape
        """
        MultiInstallNewMaas(self.opts, self.display_controller,
                            post_tasks=self.landscape_tasks).run()

    def _save_lds_creds(self, creds):
        admin_name = creds['admin_name'].value
        admin_email = creds['admin_email'].value
        system_email = creds['system_email'].value
        maas_server = creds['maas_server'].value
        maas_apikey = creds['maas_apikey'].value
        self.config.save_landscape_creds(
            admin_name, admin_email, system_email,
            maas_server, maas_apikey)

        self.display_controller.ui.hide_widget_on_top()
        self.display_controller.info_message("Running ..")
        if not maas_server:
            log.debug("No maas credentials entered, doing a new MAAS install")
            self._do_install_new_maas()
        else:
            log.debug("Existing MAAS defined, doing a LDS "
                      "installation with existing MAAS.")
            self.config.save_maas_creds(maas_server,
                                        maas_apikey)
            self._do_install_existing_maas()

    def run(self):
        self.display_controller.info_message(
            "Please enter your Landscape information and "
            "optionally an existing MAAS Server IP. If MAAS "
            "is not defined a new one will be created for you.")
        self.display_controller.show_landscape_input("Landscape Setup",
                                                     self._save_lds_creds)
Exemple #5
0
    def setUp(self):
        with NamedTemporaryFile(mode='w+', encoding='utf-8') as tempf:
            # Override config file to save to
            self.config = Config({}, tempf.name)

        type(self.config).cfg_path = PropertyMock(return_value='fake_cfg_path')
        self.config.setopt('openstack_password', 'fake_pw')
        self.ltp = patch('cloudinstall.utils.load_template')
        self.mock_load_template = self.ltp.start()
        self.mock_load_template.side_effect = source_tree_template_loader
Exemple #6
0
 def setUp(self):
     self._temp_conf = Config({}, save_backups=False)
     with NamedTemporaryFile(mode='w+', encoding='utf-8') as tempf:
         # Override config file to save to
         self.conf = Config(self._temp_conf._config,
                            tempf.name,
                            save_backups=False)
     self.mock_ui = MagicMock(name='ui')
     self.mock_log = MagicMock(name='log')
     self.mock_loop = MagicMock(name='loop')
Exemple #7
0
    def setUp(self):
        self.conf = Config({})
        self.mock_ui = MagicMock(name='ui')
        self.mock_log = MagicMock(name='log')
        self.mock_loop = MagicMock(name='loop')

        self.conf.setopt('headless', False)
        self.dc = Controller(
            ui=self.mock_ui, config=self.conf,
            loop=self.mock_loop)
        self.dc.initialize = MagicMock()
        self.dc.juju_state = JujuState(juju=MagicMock())
        self.dc.juju_state.all_agents_started = MagicMock()
Exemple #8
0
    def test_gen_single_backends(self):
        "gen_single has no storage backend by default"

        def find_charm(cn, defs):
            allcharms = []
            for mname, ad in defs.items():
                for atype, charmclasses in ad.items():
                    allcharms += charmclasses
            return cn in allcharms

        c = Config()
        pc = PlacementController(config=c)

        # default storage_backend is 'none'
        c.setopt('storage_backend', 'none')
        defaults = pc.gen_single()
        self.assertFalse(find_charm(CharmSwiftProxy, defaults))
        self.assertFalse(find_charm(CharmSwift, defaults))
        self.assertFalse(find_charm(CharmCeph, defaults))
        self.assertFalse(find_charm(CharmCephOSD, defaults))

        c.setopt('storage_backend', 'swift')
        defaults = pc.gen_single()
        self.assertTrue(find_charm(CharmSwiftProxy, defaults))
        self.assertTrue(find_charm(CharmSwift, defaults))
        self.assertFalse(find_charm(CharmCeph, defaults))
        self.assertFalse(find_charm(CharmCephOSD, defaults))

        c.setopt('storage_backend', 'ceph')
        defaults = pc.gen_single()
        self.assertFalse(find_charm(CharmSwiftProxy, defaults))
        self.assertFalse(find_charm(CharmSwift, defaults))
        self.assertTrue(find_charm(CharmCeph, defaults))
        self.assertFalse(find_charm(CharmCephOSD, defaults))
Exemple #9
0
    def setUp(self):
        self.mock_maas_state = MagicMock()

        with NamedTemporaryFile(mode='w+', encoding='utf-8') as tempf:
            utils.spew(tempf.name, yaml.dump(dict()))
            self.conf = Config({}, tempf.name)

        self.conf.setopt('storage_backend', 'none')
        self.pc = PlacementController(self.mock_maas_state, self.conf)

        self.mock_machine = make_fake_machine('machine1')
        self.mock_machine_2 = make_fake_machine('machine2')

        self.mock_machines = [self.mock_machine, self.mock_machine_2]

        self.mock_maas_state.machines.return_value = self.mock_machines
    def setUp(self):
        with NamedTemporaryFile(mode='w+', encoding='utf-8') as tempf:
            # Override config file to save to
            self.conf = Config({}, tempf.name, save_backups=False)

        self.bad_states_int = [5, 6, 7]
        self.good_states_int = [0, 1, 2]
 def setUp(self):
     self.mock_multi_installer = MagicMock()
     self.mock_display_controller = MagicMock()
     self.loop = MagicMock()
     with NamedTemporaryFile(mode='w+', encoding='utf-8') as tempf:
         # Override config file to save to
         self.conf = Config({}, tempf.name)
Exemple #12
0
    def test_gen_defaults_uses_only_ready(self):
        """gen_defaults should only use ready machines"""
        mock_maas_state = MagicMock()
        mock_maas_state.machines.return_value = []
        c = Config()
        c.setopt('storage_backend', 'none')
        pc = PlacementController(config=c, maas_state=mock_maas_state)
        # reset the mock to avoid looking at calls from
        # PlacementController.__init__().
        mock_maas_state.reset_mock()

        pc.gen_defaults()
        # we simply check the first call because we know that
        # follow-on calls are from calls to get_assignments and do
        # not affect machines used for defaults
        self.assertEqual(mock_maas_state.machines.mock_calls[0],
                         call(MaasMachineStatus.READY))
Exemple #13
0
    def test_gen_defaults_uses_only_ready(self):
        """gen_defaults should only use ready machines"""
        mock_maas_state = MagicMock()
        mock_maas_state.machines.return_value = []
        c = Config()
        c.setopt('storage_backend', 'none')
        pc = PlacementController(config=c, maas_state=mock_maas_state)
        # reset the mock to avoid looking at calls from
        # PlacementController.__init__().
        mock_maas_state.reset_mock()

        pc.gen_defaults()
        # we simply check the first call because we know that
        # follow-on calls are from calls to get_assignments and do
        # not affect machines used for defaults
        self.assertEqual(mock_maas_state.machines.mock_calls[0],
                         call(MaasMachineStatus.READY))
Exemple #14
0
    def setUp(self):
        with NamedTemporaryFile(mode='w+', encoding='utf-8') as tempf:
            # Override config file to save to
            self.conf = Config({}, tempf.name)

        dc = MagicMock(name="display_controller")
        loop = MagicMock(name="loop")
        self.installer = MultiInstall(loop, dc, self.conf)
Exemple #15
0
class WaitForDeployedServicesReadyCoreTestCase(unittest.TestCase):

    """ Tests core.wait_for_deployed_services_ready to make sure waiting
    for services to start are handled properly.
    """

    def setUp(self):
        self.conf = Config({})
        self.mock_ui = MagicMock(name='ui')
        self.mock_log = MagicMock(name='log')
        self.mock_loop = MagicMock(name='loop')

        self.conf.setopt('headless', False)
        self.dc = Controller(
            ui=self.mock_ui, config=self.conf,
            loop=self.mock_loop)
        self.dc.initialize = MagicMock()
        self.dc.juju_state = JujuState(juju=MagicMock())
        self.dc.juju_state.all_agents_started = MagicMock()

    def test_validate_services_ready(self):
        """ Verifies wait_for_deployed_services_ready

        time.sleep should not be called here as all services
        are in a started state.
        """
        self.dc.juju_state.all_agents_started.return_value = True

        with patch('cloudinstall.core.time.sleep') as mock_sleep:
            self.dc.wait_for_deployed_services_ready()
        self.assertEqual(len(mock_sleep.mock_calls), 0)

    def test_validate_services_some_ready(self):
        """ Verifies wait_for_deployed_services_ready against some of the
        services in started state

        Here we test if time.sleep was called twice due to some services
        being in an installing and allocating state.
        """
        self.dc.juju_state.all_agents_started.side_effect = [
            False, False, True, True]

        with patch('cloudinstall.core.time.sleep') as mock_sleep:
            self.dc.wait_for_deployed_services_ready()
        print(mock_sleep.mock_calls)
        self.assertEqual(len(mock_sleep.mock_calls), 2)
Exemple #16
0
    def test_gen_single_backends(self):
        "gen_single has no storage backend by default"

        def find_charm(cn, defs):
            allcharms = []
            for mname, ad in defs.items():
                for atype, charmclasses in ad.items():
                    allcharms += charmclasses
            return cn in allcharms

        c = Config()
        pc = PlacementController(config=c)

        # default storage_backend is 'none'
        c.setopt('storage_backend', 'none')
        defaults = pc.gen_single()
        self.assertFalse(find_charm(CharmSwiftProxy, defaults))
        self.assertFalse(find_charm(CharmSwift, defaults))
        self.assertFalse(find_charm(CharmCeph, defaults))
        self.assertFalse(find_charm(CharmCephOSD, defaults))

        c.setopt('storage_backend', 'swift')
        defaults = pc.gen_single()
        self.assertTrue(find_charm(CharmSwiftProxy, defaults))
        self.assertTrue(find_charm(CharmSwift, defaults))
        self.assertFalse(find_charm(CharmCeph, defaults))
        self.assertFalse(find_charm(CharmCephOSD, defaults))

        c.setopt('storage_backend', 'ceph')
        defaults = pc.gen_single()
        self.assertFalse(find_charm(CharmSwiftProxy, defaults))
        self.assertFalse(find_charm(CharmSwift, defaults))
        self.assertTrue(find_charm(CharmCeph, defaults))
        self.assertFalse(find_charm(CharmCephOSD, defaults))
 def setUp(self):
     self._temp_conf = Config({}, save_backups=False)
     with NamedTemporaryFile(mode='w+', encoding='utf-8') as tempf:
         # Override config file to save to
         self.conf = Config(self._temp_conf._config, tempf.name,
                            save_backups=False)
     self.mock_ui = MagicMock(name='ui')
     self.mock_log = MagicMock(name='log')
     self.mock_loop = MagicMock(name='loop')
class TestBadConfig(unittest.TestCase):
    def setUp(self):
        self._temp_conf = Config(BAD_CONFIG)
        with NamedTemporaryFile(mode='w+', encoding='utf-8') as tempf:
            # Override config file to save to
            self.conf = Config(self._temp_conf._config, tempf.name)

    def test_no_openstack_password(self):
        """ No openstack password defined """
        self.assertFalse(self.conf.getopt('openstack_password'))

    def test_no_landscape_creds(self):
        """ No landscape creds defined """
        self.assertFalse(self.conf.getopt('landscapecreds'))

    def test_no_installer_type(self):
        """ No installer type defined """
        self.assertFalse(self.conf.is_single)
Exemple #19
0
    def __init__(self, juju):
        """ Builds a JujuState

        :param juju: Juju API connection
        """
        self.config = Config()
        self.juju = juju
        self.start_time = time.time()
        self._juju_status = None
        self.valid_states = ['pending', 'started', 'down']
Exemple #20
0
    def setUp(self):
        self.mock_maas_state = MagicMock()
        with NamedTemporaryFile(mode='w+', encoding='utf-8') as tempf:
            utils.spew(tempf.name, yaml.dump(dict()))
            self.conf = Config({}, tempf.name)

        self.conf.setopt('storage_backend', 'none')
        self.pc = PlacementController(self.mock_maas_state, self.conf)
        self.mock_machine = MagicMock(name='machine1')
        pmid = PropertyMock(return_value='fake-instance-id-1')
        type(self.mock_machine).instance_id = pmid

        self.mock_machine_2 = MagicMock(name='machine2')
        pmid2 = PropertyMock(return_value='fake-instance-id-2')
        type(self.mock_machine_2).instance_id = pmid2

        self.mock_machines = [self.mock_machine, self.mock_machine_2]

        self.mock_maas_state.machines.return_value = self.mock_machines
def juju_state():
    cfg = Config(CONFIGOBJ)
    if not len(cfg.juju_env['state-servers']) > 0:
        state_server = 'localhost:17070'
    else:
        state_server = cfg.juju_env['state-servers'][0]
    juju = JujuClient(url=path.join('wss://', state_server),
                      password=cfg.juju_api_password)
    juju.login()
    return JujuState(juju)
Exemple #22
0
    def setUp(self):
        with NamedTemporaryFile(mode='w+', encoding='utf-8') as tempf:
            # Override config file to save to
            self.config = Config({}, tempf.name)

        type(self.config).cfg_path = PropertyMock(return_value='fake_cfg_path')
        self.config.setopt('openstack_password', 'fake_pw')
        self.ltp = patch('cloudinstall.utils.load_template')
        self.mock_load_template = self.ltp.start()
        self.mock_load_template.side_effect = source_tree_template_loader
class TestBadConfig(unittest.TestCase):

    def setUp(self):
        self._temp_conf = Config(BAD_CONFIG)
        with NamedTemporaryFile(mode='w+', encoding='utf-8') as tempf:
            # Override config file to save to
            self.conf = Config(self._temp_conf._config, tempf.name)

    def test_no_openstack_password(self):
        """ No openstack password defined """
        self.assertFalse(self.conf.getopt('openstack_password'))

    def test_no_landscape_creds(self):
        """ No landscape creds defined """
        self.assertFalse(self.conf.getopt('landscapecreds'))

    def test_no_installer_type(self):
        """ No installer type defined """
        self.assertFalse(self.conf.is_single)
def juju_state():
    cfg = Config(CONFIGOBJ)
    uuid = cfg.juju_env['environ-uuid']
    if not len(cfg.juju_env['state-servers']) > 0:
        state_server = 'localhost:17070'
    else:
        state_server = cfg.juju_env['state-servers'][0]
    url = path.join('wss://', state_server, 'environment', uuid, 'api')
    juju = JujuClient(url=url, password=cfg.juju_api_password)
    juju.login()
    return JujuState(juju)
    def setUp(self):
        self.conf = Config({}, save_backups=False)
        self.mock_ui = MagicMock(name="ui")
        self.mock_log = MagicMock(name="log")
        self.mock_loop = MagicMock(name="loop")

        self.conf.setopt("headless", False)
        self.dc = Controller(ui=self.mock_ui, config=self.conf, loop=self.mock_loop)
        self.dc.initialize = MagicMock()
        self.dc.juju_state = JujuState(juju=MagicMock())
        self.dc.juju_state.all_agents_started = MagicMock()
Exemple #26
0
 def __init__(self, ui=None, opts=None):
     self.ui = ui
     self.opts = opts
     self.config = Config()
     self.juju_state = None
     self.juju = None
     self.maas = None
     self.maas_state = None
     self.nodes = None
     self.machine = None
     self.node_install_wait_alarm = None
    def __init__(self, opts, display_controller):
        self.config = Config()
        self.opts = opts
        self.display_controller = display_controller
        # Sets install type
        utils.spew(os.path.join(self.config.cfg_path,
                                'landscape'),
                   'auto-generated')

        self.landscape_tasks = ["Preparing Landscape",
                                "Deploying Landscape",
                                "Registering against Landscape"]
class MultiInstallNewMaasTestCase(unittest.TestCase):

    def setUp(self):
        with NamedTemporaryFile(mode='w+', encoding='utf-8') as tempf:
            # Override config file to save to
            self.conf = Config({}, tempf.name)
        self.conf.setopt('openstack_password', 'ampersand&')

    def make_installer(self, loop=None, dc=None):

        if dc is None:
            dc = MagicMock(name="display_controller")
        if loop is None:
            loop = MagicMock(name="loop")

        self.installer = MultiInstallNewMaas(
            loop, dc, self.conf)

    def _create_superuser(self, raises):
        expected = ("maas-region-admin createadmin --username root "
                    "--password 'ampersand&' "
                    "--email [email protected]")

        self.make_installer()
        with patch('cloudinstall.multi_install.utils') as mock_utils:
            if raises:
                mock_utils.get_command_output.return_value = {'status': -1}
                self.assertRaises(MaasInstallError,
                                  self.installer.create_superuser)
            else:
                mock_utils.get_command_output.return_value = {'status': 0}
                self.installer.create_superuser()
                mock_utils.get_command_output.assert_called_with(expected)

    def test_create_superuser_raises(self):
        self._create_superuser(True)

    def test_create_superuser_ok(self):
        self._create_superuser(False)
    def __init__(self, juju=None, juju_state=None, machine=None, ui=None):
        """ initialize

        :param state: :class:JujuState
        :param machine: :class:Machine
        """
        self.config = Config()
        self.charm_path = None
        self.exposed = False
        self.machine = machine
        self.juju = juju
        self.juju_state = juju_state
        self.ui = ui
Exemple #30
0
class MultiInstallNewMaasTestCase(unittest.TestCase):
    def setUp(self):
        with NamedTemporaryFile(mode='w+', encoding='utf-8') as tempf:
            # Override config file to save to
            self.conf = Config({}, tempf.name)
        self.conf.setopt('openstack_password', 'ampersand&')

    def make_installer(self, loop=None, dc=None):

        if dc is None:
            dc = MagicMock(name="display_controller")
        if loop is None:
            loop = MagicMock(name="loop")

        self.installer = MultiInstallNewMaas(loop, dc, self.conf)

    def _create_superuser(self, raises):
        expected = ("maas-region-admin createadmin --username root "
                    "--password 'ampersand&' "
                    "--email [email protected]")

        self.make_installer()
        with patch('cloudinstall.multi_install.utils') as mock_utils:
            if raises:
                mock_utils.get_command_output.return_value = {'status': -1}
                self.assertRaises(MaasInstallError,
                                  self.installer.create_superuser)
            else:
                mock_utils.get_command_output.return_value = {'status': 0}
                self.installer.create_superuser()
                mock_utils.get_command_output.assert_called_with(expected)

    def test_create_superuser_raises(self):
        self._create_superuser(True)

    def test_create_superuser_ok(self):
        self._create_superuser(False)
class CoreStateTestCase(unittest.TestCase):
    """ Handles validating current state within the controllers
    core
    """
    def setUp(self):
        with NamedTemporaryFile(mode='w+', encoding='utf-8') as tempf:
            # Override config file to save to
            self.conf = Config({}, tempf.name, save_backups=False)
        self.mock_ui = MagicMock(name='ui')

    @patch('cloudinstall.core.Controller')
    def test_controller_state_init(self, Controller):
        """ Validate controller state in core during class init """
        Controller(self.mock_ui, self.conf)
        self.assertEqual(self.conf.getopt('current_state'),
                         ControllerState.INSTALL_WAIT)
Exemple #32
0
    def setUp(self):
        self.mock_maas_state = MagicMock()
        with NamedTemporaryFile(mode='w+', encoding='utf-8') as tempf:
            utils.spew(tempf.name, yaml.dump(dict()))
            self.conf = Config({}, tempf.name)

        self.pc = PlacementController(self.mock_maas_state, self.conf)
        self.mock_machine = make_fake_machine('machine1', {'cpu_count': 3})
        self.mock_machine2 = make_fake_machine('machine2')
        self.mock_machine3 = make_fake_machine('machine3')

        self.mock_machines = [self.mock_machine]

        self.mock_maas_state.machines.return_value = self.mock_machines

        self.actions = []
class MultiInstallStateTestCase(unittest.TestCase):
    """ Handles validating current state within a
    multi install
    """
    def setUp(self):
        with NamedTemporaryFile(mode='w+', encoding='utf-8') as tempf:
            # Override config file to save to
            self.conf = Config({}, tempf.name, save_backups=False)
        self.mock_ui = MagicMock(name='ui')

    @patch('cloudinstall.controllers.install.MultiInstall')
    def test_do_install_sets_state(self, MultiInstall):
        """ Validate installstate in multi install """
        mi = MultiInstall(self.mock_ui, config=self.conf)
        mi.do_install()
        self.assertEqual(self.conf.getopt('current_state'),
                         InstallState.RUNNING)
Exemple #34
0
    def setUp(self):
        self.mock_maas_state = MagicMock()

        with NamedTemporaryFile(mode='w+', encoding='utf-8') as tempf:
            utils.spew(tempf.name, yaml.dump(dict()))
            self.conf = Config({}, tempf.name)

        self.conf.setopt('storage_backend', 'none')
        self.pc = PlacementController(self.mock_maas_state,
                                      self.conf)

        self.mock_machine = make_fake_machine('machine1')
        self.mock_machine_2 = make_fake_machine('machine2')

        self.mock_machines = [self.mock_machine, self.mock_machine_2]

        self.mock_maas_state.machines.return_value = self.mock_machines
    def setUp(self):
        self.conf = Config({})
        self.mock_ui = MagicMock(name='ui')
        self.mock_log = MagicMock(name='log')
        self.mock_loop = MagicMock(name='loop')

        self.services_ready = [
            Service('keystone', {'Units': {
                'fake': {
                    'AgentState': 'started'
                }
            }}),
            Service('nova-compute',
                    {'Units': {
                        'fake': {
                            'AgentState': 'started'
                        }
                    }}),
        ]

        self.services_some_ready = [
            Service('keystone', {'Units': {
                'fake': {
                    'AgentState': 'started'
                }
            }}),
            Service('nova-compute',
                    {'Units': {
                        'fake2': {
                            'AgentState': 'started'
                        }
                    }}),
            Service('nova-cloud-controller',
                    {'Units': {
                        'fake3': {
                            'AgentState': 'installing'
                        }
                    }}),
            Service('glance',
                    {'Units': {
                        'fake4': {
                            'AgentState': 'allocating'
                        }
                    }}),
        ]
class CoreStateTestCase(unittest.TestCase):

    """ Handles validating current state within the controllers
    core
    """

    def setUp(self):
        with NamedTemporaryFile(mode='w+', encoding='utf-8') as tempf:
            # Override config file to save to
            self.conf = Config({}, tempf.name, save_backups=False)
        self.mock_ui = MagicMock(name='ui')

    @patch('cloudinstall.core.Controller')
    def test_controller_state_init(self, Controller):
        """ Validate controller state in core during class init """
        Controller(self.mock_ui, self.conf)
        self.assertEqual(
            self.conf.getopt('current_state'), ControllerState.INSTALL_WAIT)
Exemple #37
0
    def test_gen_single_backends(self):
        "gen_single has no storage backend by default"

        def find_charm(cn, defs):
            allcharms = []
            for mname, ad in defs.items():
                for atype, charmclasses in ad.items():
                    allcharms += charmclasses
            return cn in allcharms

        c = Config(save_backups=False)
        pc = PlacementController(config=c)

        defaults = pc.gen_single()
        self.assertFalse(find_charm(CharmSwiftProxy, defaults))
        self.assertFalse(find_charm(CharmSwift, defaults))
        self.assertFalse(find_charm(CharmCeph, defaults))
        self.assertFalse(find_charm(CharmCephOSD, defaults))
class MultiInstallStateTestCase(unittest.TestCase):

    """ Handles validating current state within a
    multi install
    """

    def setUp(self):
        with NamedTemporaryFile(mode='w+', encoding='utf-8') as tempf:
            # Override config file to save to
            self.conf = Config({}, tempf.name, save_backups=False)
        self.mock_ui = MagicMock(name='ui')

    @patch('cloudinstall.controllers.install.MultiInstall')
    def test_do_install_sets_state(self, MultiInstall):
        """ Validate installstate in multi install """
        mi = MultiInstall(self.mock_ui, config=self.conf)
        mi.do_install()
        self.assertEqual(
            self.conf.getopt('current_state'), InstallState.RUNNING)
Exemple #39
0
    def setUp(self):
        self.mock_maas_state = MagicMock()
        with NamedTemporaryFile(mode='w+', encoding='utf-8') as tempf:
            utils.spew(tempf.name, yaml.dump(dict()))
            self.conf = Config({}, tempf.name)

        self.conf.setopt('storage_backend', 'none')
        self.pc = PlacementController(self.mock_maas_state,
                                      self.conf)
        self.mock_machine = MagicMock(name='machine1')
        pmid = PropertyMock(return_value='fake-instance-id-1')
        type(self.mock_machine).instance_id = pmid

        self.mock_machine_2 = MagicMock(name='machine2')
        pmid2 = PropertyMock(return_value='fake-instance-id-2')
        type(self.mock_machine_2).instance_id = pmid2

        self.mock_machines = [self.mock_machine, self.mock_machine_2]

        self.mock_maas_state.machines.return_value = self.mock_machines
Exemple #40
0
    def test_load_machines_single(self):
        with NamedTemporaryFile(mode='w+', encoding='utf-8') as tempf:
            utils.spew(tempf.name, yaml.dump(dict()))
            conf = Config({}, tempf.name)

        fake_assignments = {
            'fake_iid': {
                'constraints': {},
                'assignments': {
                    'KVM': ['nova-compute']
                }
            },
            'fake_iid_2': {
                'constraints': {
                    'cpu': 8
                },
                'assignments': {
                    'BareMetal': ['nova-compute']
                }
            }
        }

        singlepc = PlacementController(None, conf)

        with TemporaryFile(mode='w+', encoding='utf-8') as tempf:
            yaml.dump(fake_assignments, tempf)
            tempf.seek(0)
            singlepc.load(tempf)

        self.assertEqual(
            set([m.instance_id for m in singlepc.machines_pending()]),
            set(['fake_iid', 'fake_iid_2']))

        m2 = next((m for m in singlepc.machines_pending()
                   if m.instance_id == 'fake_iid_2'))
        self.assertEqual(m2.constraints, {'cpu': 8})
class InstallStateTestCase(unittest.TestCase):
    def setUp(self):
        with NamedTemporaryFile(mode='w+', encoding='utf-8') as tempf:
            # Override config file to save to
            self.conf = Config({}, tempf.name)

        self.bad_states_int = [5, 6, 7]
        self.good_states_int = [0, 1]

    def test_install_state(self):
        """ Validate config install state """

        for i in self.bad_states_int:
            self.conf.setopt('current_state', i)
            with self.assertRaises(ValueError):
                s = self.conf.getopt('current_state')
                InstallState(s)

        for i in self.good_states_int:
            self.conf.setopt('current_state', i)
            s = self.conf.getopt('current_state')
            self.assertEqual(InstallState(s), i)
class ControllerStateTestCase(unittest.TestCase):
    def setUp(self):
        with NamedTemporaryFile(mode='w+', encoding='utf-8') as tempf:
            # Override config file to save to
            self.conf = Config({}, tempf.name, save_backups=False)

        self.bad_states_int = [5, 6, 7]
        self.good_states_int = [0, 1, 2]

    def test_set_controller_state(self):
        """ Validate config controller state """

        for i in self.bad_states_int:
            self.conf.setopt('current_state', i)
            with self.assertRaises(ValueError):
                s = self.conf.getopt('current_state')
                ControllerState(s)

        for i in self.good_states_int:
            self.conf.setopt('current_state', i)
            s = self.conf.getopt('current_state')
            self.assertEqual(ControllerState(s), i)
class ControllerStateTestCase(unittest.TestCase):

    def setUp(self):
        with NamedTemporaryFile(mode='w+', encoding='utf-8') as tempf:
            # Override config file to save to
            self.conf = Config({}, tempf.name, save_backups=False)

        self.bad_states_int = [5, 6, 7]
        self.good_states_int = [0, 1, 2]

    def test_set_controller_state(self):
        """ Validate config controller state """

        for i in self.bad_states_int:
            self.conf.setopt('current_state', i)
            with self.assertRaises(ValueError):
                s = self.conf.getopt('current_state')
                ControllerState(s)

        for i in self.good_states_int:
            self.conf.setopt('current_state', i)
            s = self.conf.getopt('current_state')
            self.assertEqual(ControllerState(s), i)
class InstallStateTestCase(unittest.TestCase):

    def setUp(self):
        with NamedTemporaryFile(mode='w+', encoding='utf-8') as tempf:
            # Override config file to save to
            self.conf = Config({}, tempf.name)

        self.bad_states_int = [5, 6, 7]
        self.good_states_int = [0, 1]

    def test_install_state(self):
        """ Validate config install state """

        for i in self.bad_states_int:
            self.conf.setopt('current_state', i)
            with self.assertRaises(ValueError):
                s = self.conf.getopt('current_state')
                InstallState(s)

        for i in self.good_states_int:
            self.conf.setopt('current_state', i)
            s = self.conf.getopt('current_state')
            self.assertEqual(InstallState(s), i)
 def setUp(self):
     self.conf = Config({})
     self.mock_ui = MagicMock(name='ui')
     self.mock_log = MagicMock(name='log')
     self.mock_loop = MagicMock(name='loop')
class EventLoopCoreTestCase(unittest.TestCase):

    def setUp(self):
        self.conf = Config({})
        self.mock_ui = MagicMock(name='ui')
        self.mock_log = MagicMock(name='log')
        self.mock_loop = MagicMock(name='loop')

    def make_ev(self, headless=False):
        self.conf.setopt('headless', headless)
        return EventLoop(self.mock_ui, self.conf,
                         self.mock_log)

    def test_validate_loop(self):
        """ Validate eventloop runs """
        self.conf.setopt('headless', False)
        dc = Controller(
            ui=self.mock_ui, config=self.conf,
            loop=self.mock_loop)
        dc.initialize = MagicMock()
        dc.start()
        self.mock_loop.run.assert_called_once_with()

    def test_validate_redraw_screen_commit_placement(self):
        """ Validate redraw_screen on commit_placement """
        self.conf.setopt('headless', False)
        dc = Controller(
            ui=self.mock_ui, config=self.conf,
            loop=self.mock_loop)
        dc.initialize = MagicMock()
        dc.commit_placement()
        self.mock_loop.redraw_screen.assert_called_once_with()

    def test_validate_redraw_screen_enqueue(self):
        """ Validate redraw_screen on enqueue_deployed_charms """
        self.conf.setopt('headless', False)
        dc = Controller(
            ui=self.mock_ui, config=self.conf,
            loop=self.mock_loop)
        dc.initialize = MagicMock()
        dc.enqueue_deployed_charms()
        self.mock_loop.redraw_screen.assert_called_once_with()

    def test_validate_set_alarm_in(self):
        """ Validate set_alarm_in called with eventloop """
        dc = Controller(
            ui=self.mock_ui, config=self.conf,
            loop=self.mock_loop)
        dc.initialize = MagicMock()
        self.conf.node_install_wait_interval = 1
        dc.update(self.conf.node_install_wait_interval, ANY)
        self.mock_loop.set_alarm_in.assert_called_once_with(1, ANY)

    def test_validate_exit(self):
        """ Validate error code set with eventloop """
        ev = self.make_ev()
        dc = Controller(
            ui=self.mock_ui, config=self.conf,
            loop=ev)
        dc.initialize = MagicMock()
        with self.assertRaises(urwid.ExitMainLoop):
            dc.loop.exit(1)
        self.assertEqual(ev.error_code, 1)

    def test_hotkey_exit(self):
        ev = self.make_ev()
        dc = Controller(
            ui=self.mock_ui, config=self.conf,
            loop=ev)
        dc.initialize = MagicMock()
        with self.assertRaises(urwid.ExitMainLoop):
            dc.loop.header_hotkeys('q')
        self.assertEqual(ev.error_code, 0)

    def test_repr_ev(self):
        """ Prints appropriate class string for eventloop """
        ev = self.make_ev()
        dc = Controller(
            ui=self.mock_ui, config=self.conf,
            loop=ev)
        dc.initialize = MagicMock()
        self.assertEqual(str(ev), '<eventloop urwid based on select()>')

    def test_repr_no_ev(self):
        """ Prints appropriate class string for no eventloop """
        ev = self.make_ev(True)
        dc = Controller(
            ui=self.mock_ui, config=self.conf,
            loop=ev)
        dc.initialize = MagicMock()
        self.assertEqual(str(ev), '<eventloop disabled>')

    def test_validate_exit_no_ev(self):
        """ Validate SystemExit with no eventloop """
        ev = self.make_ev(True)
        dc = Controller(
            ui=self.mock_ui, config=self.conf,
            loop=ev)
        dc.initialize = MagicMock()
        with self.assertRaises(SystemExit) as cm:
            dc.loop.exit(1)
        exc = cm.exception
        self.assertEqual(ev.error_code, exc.code, "Found loop")
 def setUp(self):
     self._temp_conf = Config(GOOD_CONFIG)
     with NamedTemporaryFile(mode='w+', encoding='utf-8') as tempf:
         # Override config file to save to
         self.conf = Config(self._temp_conf._config, tempf.name)
class TestGoodConfig(unittest.TestCase):

    def setUp(self):
        self._temp_conf = Config(GOOD_CONFIG)
        with NamedTemporaryFile(mode='w+', encoding='utf-8') as tempf:
            # Override config file to save to
            self.conf = Config(self._temp_conf._config, tempf.name)

    def test_save_openstack_password(self):
        """ Save openstack password to config """
        self.conf.setopt('openstack_password', 'pass')
        self.conf.save()
        self.assertEqual('pass', self.conf.getopt('openstack_password'))

    def test_save_maas_creds(self):
        """ Save maas credentials """
        self.conf.setopt('maascreds', dict(api_host='127.0.0.1',
                                           api_key='1234567'))
        self.conf.save()
        self.assertEqual(
            '127.0.0.1', self.conf.getopt('maascreds')['api_host'])

    def test_save_landscape_creds(self):
        """ Save landscape credentials """
        self.conf.setopt('landscapecreds',
                         dict(admin_name='foo',
                              admin_email='*****@*****.**',
                              system_email='*****@*****.**',
                              maas_server='127.0.0.1',
                              maas_apikey='123457'))
        self.conf.save()
        self.assertEqual(
            '*****@*****.**', self.conf.getopt('landscapecreds')['admin_email'])

    def test_save_installer_type(self):
        """ Save installer type """
        self.conf.setopt("install_type", 'multi')
        self.conf.save()
        self.assertEqual('multi', self.conf.getopt('install_type'))

    @unittest.skip
    def test_cfg_path(self):
        """ Validate current users config path """
        self.assertEqual(
            self.conf.cfg_path, path.join(USER_DIR, '.cloud-install'))

    def test_bin_path(self):
        """ Validate additional tools bin path """
        self.assertEqual(self.conf.bin_path, '/usr/share/openstack/bin')

    @unittest.skip
    def test_juju_environments_path(self):
        """ Validate juju environments path in user dir """
        self.assertEqual(
            self.conf.juju_environments_path,
            path.join(
                USER_DIR, '.cloud-install/juju/environments.yaml'))

    def test_clear_empty_args(self):
        """ Empty cli options are not populated
        in generated config
        """
        cfg_file = path.join(DATA_DIR, 'good_config.yaml')
        cfg = utils.populate_config(parse_opts(['--config', cfg_file]))
        self.assertEqual(True, 'http-proxy' not in cfg)

    def test_config_file_persists(self):
        """ CLI options override options in config file
        """
        cfg_file = path.join(DATA_DIR, 'good_config.yaml')
        cfg = utils.populate_config(
            parse_opts(['--config', cfg_file,
                        '--headless']))
        self.assertEqual(True, cfg['headless'])

    def test_config_file_persists_new_cli_opts(self):
        """ Generated config object appends new options
        passed via cli
        """
        cfg_file = path.join(DATA_DIR, 'good_config.yaml')
        cfg = utils.populate_config(
            parse_opts(['--config', cfg_file,
                        '--install-only',
                        '--killcloud-noprompt']))
        self.assertEqual(True, cfg['install_only'])
        self.assertEqual(True, cfg['killcloud_noprompt'])

    def test_config_overrides_from_cli(self):
        """ Config object item is not overridden
        by unset cli option
        """
        cfg_file = path.join(DATA_DIR, 'good_config.yaml')
        cfg = utils.populate_config(
            parse_opts(['--http-proxy',
                        'http://localhost:2222',
                        '--killcloud-noprompt',
                        '--config', cfg_file]))
        self.assertEqual(cfg['https_proxy'], GOOD_CONFIG['https_proxy'])

    def test_default_opts_not_override_config(self):
        """ Verify that default cli opts that are False
        do not override their config_option whose option
        is True.
        """
        cfg_file = path.join(DATA_DIR, 'good_config.yaml')
        cfg_opts_raw = parse_opts(['--config', cfg_file])
        cfg_opts_raw = vars(cfg_opts_raw)
        self.assertEqual(True, 'headless' not in cfg_opts_raw)

        cfg = utils.populate_config(parse_opts(['--config', cfg_file]))
        self.assertEqual(True, cfg['headless'])

    def test_default_opts_no_config(self):
        """ Verify that default cli opts are sanitized
        and that no options set to False or None exist
        in the config object
        """
        cfg = utils.populate_config(parse_opts([]))
        print(cfg)
        self.assertEqual(True, 'headless' not in cfg)
 def setUp(self):
     with NamedTemporaryFile(mode='w+', encoding='utf-8') as tempf:
         # Override config file to save to
         self.conf = Config({}, tempf.name)
     self.conf.setopt('openstack_password', 'ampersand&')
 def setUp(self):
     self._temp_conf = Config(BAD_CONFIG, save_backups=False)
     with NamedTemporaryFile(mode='w+', encoding='utf-8') as tempf:
         # Override config file to save to
         self.conf = Config(self._temp_conf._config, tempf.name,
                            save_backups=False)
 def setUp(self):
     self.conf = Config({}, save_backups=False)
     self.mock_ui = MagicMock(name='ui')
     self.mock_log = MagicMock(name='log')
     self.mock_loop = MagicMock(name='loop')
Exemple #52
0
class PlacementControllerTestCase(unittest.TestCase):

    def setUp(self):
        self.mock_maas_state = MagicMock()
        with NamedTemporaryFile(mode='w+', encoding='utf-8') as tempf:
            utils.spew(tempf.name, yaml.dump(dict()))
            self.conf = Config({}, tempf.name)

        self.conf.setopt('storage_backend', 'none')
        self.pc = PlacementController(self.mock_maas_state,
                                      self.conf)
        self.mock_machine = MagicMock(name='machine1')
        pmid = PropertyMock(return_value='fake-instance-id-1')
        type(self.mock_machine).instance_id = pmid

        self.mock_machine_2 = MagicMock(name='machine2')
        pmid2 = PropertyMock(return_value='fake-instance-id-2')
        type(self.mock_machine_2).instance_id = pmid2

        self.mock_machines = [self.mock_machine, self.mock_machine_2]

        self.mock_maas_state.machines.return_value = self.mock_machines

    def test_get_assignments_atype(self):
        self.assertEqual(0,
                         len(self.pc.get_assignments(CharmNovaCompute)))
        self.pc.assign(self.mock_machine, CharmNovaCompute, AssignmentType.LXC)
        self.pc.assign(self.mock_machine, CharmNovaCompute, AssignmentType.LXC)
        md = self.pc.get_assignments(CharmNovaCompute)
        self.assertEqual(1, len(md))
        self.assertEqual(2, len(md[AssignmentType.LXC]))

    def _do_test_simple_assign_type(self, assignment_type):
        self.pc.assign(self.mock_machine, CharmNovaCompute, assignment_type)
        print("assignments is {}".format(self.pc.assignments))
        machines = self.pc.get_assignments(CharmNovaCompute)
        print('machines for charm is {}'.format(machines))
        self.assertEqual(machines,
                         {assignment_type: [self.mock_machine]})

        ma = self.pc.assignments_for_machine(self.mock_machine)

        self.assertEqual(ma[assignment_type], [CharmNovaCompute])

    def test_simple_assign_bare(self):
        self._do_test_simple_assign_type(AssignmentType.BareMetal)

    def test_simple_assign_lxc(self):
        self._do_test_simple_assign_type(AssignmentType.LXC)

    def test_simple_assign_kvm(self):
        self._do_test_simple_assign_type(AssignmentType.KVM)

    def test_assign_nonmulti(self):
        self.pc.assign(self.mock_machine, CharmKeystone, AssignmentType.LXC)
        self.assertEqual(self.pc.get_assignments(CharmKeystone),
                         {AssignmentType.LXC: [self.mock_machine]})

        self.pc.assign(self.mock_machine, CharmKeystone, AssignmentType.KVM)
        self.assertEqual(self.pc.get_assignments(CharmKeystone),
                         {AssignmentType.KVM: [self.mock_machine]})

        am = self.pc.assignments_for_machine(self.mock_machine)
        self.assertEqual(am[AssignmentType.KVM], [CharmKeystone])
        self.assertEqual(am[AssignmentType.LXC], [])

    def test_assign_multi(self):
        self.pc.assign(self.mock_machine, CharmNovaCompute, AssignmentType.LXC)
        self.assertEqual(self.pc.get_assignments(CharmNovaCompute),
                         {AssignmentType.LXC: [self.mock_machine]})

        self.pc.assign(self.mock_machine, CharmNovaCompute, AssignmentType.KVM)
        self.assertEqual(self.pc.get_assignments(CharmNovaCompute),
                         {AssignmentType.LXC: [self.mock_machine],
                          AssignmentType.KVM: [self.mock_machine]})

        ma = self.pc.assignments_for_machine(self.mock_machine)
        self.assertEqual(ma[AssignmentType.LXC], [CharmNovaCompute])
        self.assertEqual(ma[AssignmentType.KVM], [CharmNovaCompute])

    def test_remove_assignment_multi(self):
        self.pc.assign(self.mock_machine, CharmNovaCompute, AssignmentType.LXC)
        self.pc.assign(self.mock_machine_2, CharmNovaCompute,
                       AssignmentType.LXC)

        mfc = self.pc.get_assignments(CharmNovaCompute)

        mfc_lxc = set(mfc[AssignmentType.LXC])
        self.assertEqual(mfc_lxc, set(self.mock_machines))

        self.pc.clear_assignments(self.mock_machine)
        self.assertEqual(self.pc.get_assignments(CharmNovaCompute),
                         {AssignmentType.LXC: [self.mock_machine_2]})

    def test_gen_defaults(self):
        satisfies_importstring = 'cloudinstall.placement.controller.satisfies'
        with patch(satisfies_importstring) as mock_satisfies:
            mock_satisfies.return_value = (True, )
            defs = self.pc.gen_defaults(charm_classes=[CharmNovaCompute,
                                                       CharmKeystone],
                                        maas_machines=[self.mock_machine,
                                                       self.mock_machine_2])
            m1_as = defs[self.mock_machine.instance_id]
            m2_as = defs[self.mock_machine_2.instance_id]
            self.assertEqual(m1_as[AssignmentType.BareMetal],
                             [CharmNovaCompute])
            self.assertEqual(m1_as[AssignmentType.LXC], [])
            self.assertEqual(m1_as[AssignmentType.KVM], [])

            self.assertEqual(m2_as[AssignmentType.BareMetal], [])
            self.assertEqual(m2_as[AssignmentType.LXC], [CharmKeystone])
            self.assertEqual(m2_as[AssignmentType.KVM], [])

    def test_remove_one_assignment_sametype(self):
        self.pc.assign(self.mock_machine, CharmNovaCompute, AssignmentType.LXC)
        self.pc.assign(self.mock_machine, CharmNovaCompute, AssignmentType.LXC)

        self.pc.remove_one_assignment(self.mock_machine, CharmNovaCompute)
        md = self.pc.assignments[self.mock_machine.instance_id]
        lxcs = md[AssignmentType.LXC]
        self.assertEqual(lxcs, [CharmNovaCompute])

        self.pc.remove_one_assignment(self.mock_machine, CharmNovaCompute)
        md = self.pc.assignments[self.mock_machine.instance_id]
        lxcs = md[AssignmentType.LXC]
        self.assertEqual(lxcs, [])

    def test_remove_one_assignment_othertype(self):
        self.pc.assign(self.mock_machine, CharmNovaCompute, AssignmentType.LXC)
        self.pc.assign(self.mock_machine, CharmNovaCompute, AssignmentType.KVM)

        self.pc.remove_one_assignment(self.mock_machine, CharmNovaCompute)
        md = self.pc.assignments[self.mock_machine.instance_id]
        lxcs = md[AssignmentType.LXC]
        kvms = md[AssignmentType.KVM]
        self.assertEqual(1, len(lxcs) + len(kvms))

        self.pc.remove_one_assignment(self.mock_machine, CharmNovaCompute)
        md = self.pc.assignments[self.mock_machine.instance_id]
        lxcs = md[AssignmentType.LXC]
        kvms = md[AssignmentType.KVM]
        self.assertEqual(0, len(lxcs) + len(kvms))

    def test_clear_all(self):
        self.pc.assign(self.mock_machine, CharmNovaCompute, AssignmentType.LXC)
        self.pc.assign(self.mock_machine_2,
                       CharmNovaCompute, AssignmentType.KVM)
        self.pc.clear_all_assignments()
        # check that it's empty:
        self.assertEqual(self.pc.assignments, {})
        # and that it's still a defaultdict(lambda: defaultdict(list))
        mid = self.mock_machine.machine_id
        lxcs = self.pc.assignments[mid][AssignmentType.LXC]
        self.assertEqual(lxcs, [])

    def test_unassigned_starts_full(self):
        self.assertEqual(len(self.pc.unassigned_undeployed_services()),
                         len(self.pc.charm_classes()))

    def test_assigned_charm_classes_starts_empty(self):
        self.assertEqual(0, len(self.pc.assigned_charm_classes()))

    def test_reset_unassigned_undeployed_none(self):
        """Assign all charms, ensure that unassigned is empty"""
        for cc in self.pc.charm_classes():
            self.pc.assign(self.mock_machine, cc, AssignmentType.LXC)

        self.pc.reset_assigned_deployed()

        self.assertEqual(0, len(self.pc.unassigned_undeployed_services()))

    def test_reset_unassigned_undeployed_two(self):
        self.pc.assign(self.mock_machine, CharmNovaCompute, AssignmentType.LXC)
        self.pc.assign(self.mock_machine_2, CharmKeystone, AssignmentType.KVM)
        self.pc.reset_assigned_deployed()
        self.assertEqual(len(self.pc.charm_classes()) - 2,
                         len(self.pc.unassigned_undeployed_services()))

    def test_reset_excepting_compute(self):
        for cc in self.pc.charm_classes():
            if cc.charm_name == 'nova-compute':
                continue
            self.pc.assign(self.mock_machine, cc, AssignmentType.LXC)

        self.pc.reset_assigned_deployed()
        self.assertEqual(len(self.pc.unassigned_undeployed_services()), 1)

    def test_unassigned_undeployed(self):
        all_charms = set(self.pc.charm_classes())
        self.pc.assign(self.mock_machine, CharmKeystone, AssignmentType.KVM)
        self.pc.assign(self.mock_machine, CharmNovaCompute, AssignmentType.KVM)
        self.pc.mark_deployed(self.mock_machine, CharmKeystone,
                              AssignmentType.KVM)

        self.assertTrue(CharmKeystone not in
                        self.pc.unassigned_undeployed_services())
        self.assertTrue(CharmNovaCompute not in
                        self.pc.unassigned_undeployed_services())
        self.assertTrue(self.pc.is_deployed(CharmKeystone))
        self.assertTrue(self.pc.is_assigned(CharmNovaCompute))

        self.assertTrue(len(all_charms) - 2,
                        len(self.pc.unassigned_undeployed_services()))

        n_k_as = self.pc.assignment_machine_count_for_charm(CharmKeystone)
        self.assertEqual(n_k_as, 0)
        n_k_dl = self.pc.deployment_machine_count_for_charm(CharmKeystone)
        self.assertEqual(n_k_dl, 1)
        n_nc_as = self.pc.assignment_machine_count_for_charm(CharmNovaCompute)
        self.assertEqual(n_nc_as, 1)
        n_nc_dl = self.pc.deployment_machine_count_for_charm(CharmNovaCompute)
        self.assertEqual(n_nc_dl, 0)

    def test_deployed_charms_starts_empty(self):
        "Initially there are no deployed charms"
        self.assertEqual(0, len(self.pc.deployed_charm_classes()))

    def test_mark_deployed_unsets_assignment(self):
        "Setting a placement to deployed removes it from assignment dict"
        self.pc.assign(self.mock_machine, CharmKeystone, AssignmentType.KVM)
        self.assertEqual([CharmKeystone], self.pc.assigned_charm_classes())
        self.pc.mark_deployed(self.mock_machine, CharmKeystone,
                              AssignmentType.KVM)
        self.assertEqual([CharmKeystone], self.pc.deployed_charm_classes())
        self.assertEqual([], self.pc.assigned_charm_classes())

    def test_set_deployed_unsets_assignment_only_once(self):
        "Setting a placement to deployed removes it from assignment dict"
        self.pc.assign(self.mock_machine, CharmNovaCompute, AssignmentType.KVM)
        self.pc.assign(self.mock_machine_2, CharmNovaCompute,
                       AssignmentType.KVM)
        self.assertEqual([CharmNovaCompute], self.pc.assigned_charm_classes())
        ad = self.pc.get_assignments(CharmNovaCompute)
        dd = self.pc.get_deployments(CharmNovaCompute)
        from pprint import pformat
        print("Assignments is {}".format(pformat(ad)))
        print("Deployments is {}".format(pformat(dd)))
        self.assertEqual(set([self.mock_machine, self.mock_machine_2]),
                         set(ad[AssignmentType.KVM]))
        self.assertEqual(len(dd.items()), 0)

        self.pc.mark_deployed(self.mock_machine, CharmNovaCompute,
                              AssignmentType.KVM)
        self.assertEqual([CharmNovaCompute], self.pc.deployed_charm_classes())
        self.assertEqual([CharmNovaCompute], self.pc.assigned_charm_classes())
        ad = self.pc.get_assignments(CharmNovaCompute)
        dd = self.pc.get_deployments(CharmNovaCompute)
        self.assertEqual([self.mock_machine_2], ad[AssignmentType.KVM])
        self.assertEqual([self.mock_machine], dd[AssignmentType.KVM])

    def test_get_charm_state(self):
        "Test a sampling of required services and special handling for compute"
        self.assertEqual(self.pc.get_charm_state(CharmKeystone)[0],
                         CharmState.REQUIRED)
        self.assertEqual(self.pc.get_charm_state(CharmNovaCompute)[0],
                         CharmState.REQUIRED)

    def test_one_compute_required(self):
        """after being assigned at least once, novacompute is no longer
        considered 'required' (aka required)"""
        self.pc.assign(self.mock_machine, CharmNovaCompute, AssignmentType.LXC)
        self.assertNotEqual(self.pc.get_charm_state(CharmNovaCompute)[0],
                            CharmState.REQUIRED)

    def test_swift_unrequired_then_required_default(self):
        "Swift and swift-proxy are both optional until you add swift"
        self.assertEqual(CharmState.OPTIONAL,
                         self.pc.get_charm_state(CharmSwift)[0])
        self.assertEqual(CharmState.OPTIONAL,
                         self.pc.get_charm_state(CharmSwiftProxy)[0])
        self.pc.assign(self.mock_machine, CharmSwift, AssignmentType.LXC)
        self.assertEqual(CharmState.REQUIRED,
                         self.pc.get_charm_state(CharmSwift)[0])
        self.assertEqual(CharmState.REQUIRED,
                         self.pc.get_charm_state(CharmSwiftProxy)[0])

    def test_swift_unrequired_then_required_swift_backend(self):
        "Swift and swift-proxy are not optional with swift as the backend."
        self.conf.setopt('storage_backend', 'swift')
        self.assertEqual(CharmState.REQUIRED,
                         self.pc.get_charm_state(CharmSwift)[0])
        self.assertEqual(CharmState.REQUIRED,
                         self.pc.get_charm_state(CharmSwiftProxy)[0])
        self.pc.assign(self.mock_machine, CharmSwift, AssignmentType.LXC)
        self.assertEqual(CharmState.REQUIRED,
                         self.pc.get_charm_state(CharmSwift)[0])
        self.assertEqual(CharmState.REQUIRED,
                         self.pc.get_charm_state(CharmSwiftProxy)[0])

    def test_swift_proxy_unrequired_then_required_default(self):
        "Swift and swift-proxy are both optional until you add swift-proxy"
        self.assertEqual(CharmState.OPTIONAL,
                         self.pc.get_charm_state(CharmSwift)[0])
        self.assertEqual(CharmState.OPTIONAL,
                         self.pc.get_charm_state(CharmSwiftProxy)[0])

        self.pc.assign(self.mock_machine, CharmSwiftProxy, AssignmentType.LXC)
        self.assertEqual(CharmState.REQUIRED,
                         self.pc.get_charm_state(CharmSwift)[0])
        # Only one swift-proxy is required, so now that we've added
        # it, it is still not required:
        self.assertEqual(CharmState.OPTIONAL,
                         self.pc.get_charm_state(CharmSwiftProxy)[0])

    def test_swift_proxy_unrequired_then_required_swift_backend(self):
        "Swift and swift-proxy are not optional with swift as the backend"
        self.conf.setopt('storage_backend', 'swift')
        self.assertEqual(CharmState.REQUIRED,
                         self.pc.get_charm_state(CharmSwift)[0])
        self.assertEqual(CharmState.REQUIRED,
                         self.pc.get_charm_state(CharmSwiftProxy)[0])
        self.pc.assign(self.mock_machine, CharmSwiftProxy, AssignmentType.LXC)
        self.assertEqual(CharmState.REQUIRED,
                         self.pc.get_charm_state(CharmSwift)[0])
        # Only one swift-proxy is required, so now that we've added
        # it, it is still not required:
        self.assertEqual(CharmState.OPTIONAL,
                         self.pc.get_charm_state(CharmSwiftProxy)[0])

    def test_storage_backends_in_is_required(self):
        # default is 'none'
        self.assertEqual(CharmState.OPTIONAL,
                         self.pc.get_charm_state(CharmCeph)[0])
        self.assertEqual(CharmState.OPTIONAL,
                         self.pc.get_charm_state(CharmCephOSD)[0])
        self.assertEqual(CharmState.OPTIONAL,
                         self.pc.get_charm_state(CharmSwift)[0])
        self.assertEqual(CharmState.OPTIONAL,
                         self.pc.get_charm_state(CharmSwiftProxy)[0])

        self.conf.setopt('storage_backend', 'swift')
        self.assertEqual(CharmState.OPTIONAL,
                         self.pc.get_charm_state(CharmCeph)[0])
        self.assertEqual(CharmState.OPTIONAL,
                         self.pc.get_charm_state(CharmCephOSD)[0])
        self.assertEqual(CharmState.CONFLICTED,
                         self.pc.get_charm_state(CharmCephRadosGw)[0])

        st = self.pc.get_charm_state(CharmSwift)
        swift_state, swift_cons, swift_deps = st
        self.assertEqual(CharmState.REQUIRED, swift_state)

        st = self.pc.get_charm_state(CharmSwiftProxy)
        swp_state, swp_cons, swp_deps = st
        self.assertEqual(CharmState.REQUIRED, swp_state)
        self.assertEqual([], swp_cons)

        ceph_state, ceph_cons, ceph_deps = self.pc.get_charm_state(CharmCeph)
        self.assertEqual(CharmState.OPTIONAL, ceph_state)

        st = self.pc.get_charm_state(CharmCephRadosGw)
        ceph_rg_state, ceph_rg_cons, ceph_rg_deps = st
        self.assertEqual(CharmState.CONFLICTED, ceph_rg_state)

        self.conf.setopt('storage_backend', 'ceph')
        ceph_state, ceph_cons, ceph_deps = self.pc.get_charm_state(CharmCeph)
        self.assertEqual(CharmState.REQUIRED, ceph_state)
        self.assertEqual(CharmState.OPTIONAL,
                         self.pc.get_charm_state(CharmCephOSD)[0])
        self.assertEqual(CharmState.OPTIONAL,
                         self.pc.get_charm_state(CharmSwift)[0])
        self.assertEqual(CharmState.OPTIONAL,
                         self.pc.get_charm_state(CharmSwiftProxy)[0])

    def test_ceph_num_required(self):
        "3 units of ceph should be required after having been assigned"
        state, cons, deps = self.pc.get_charm_state(CharmCeph)
        self.assertEqual(state, CharmState.OPTIONAL)
        self.pc.assign(self.mock_machine, CharmCeph, AssignmentType.KVM)
        self.assertEqual(self.pc.get_charm_state(CharmCeph)[0],
                         CharmState.REQUIRED)
        self.pc.assign(self.mock_machine, CharmCeph, AssignmentType.KVM)
        self.pc.assign(self.mock_machine, CharmCeph, AssignmentType.KVM)
        self.assertEqual(self.pc.get_charm_state(CharmCeph)[0],
                         CharmState.OPTIONAL)

    def test_persistence(self):
        self.pc.assign(self.mock_machine, CharmNovaCompute, AssignmentType.LXC)
        self.pc.assign(self.mock_machine_2, CharmKeystone, AssignmentType.KVM)
        cons1 = PropertyMock(return_value={})
        type(self.mock_machine).constraints = cons1
        cons2 = PropertyMock(return_value={'cpu': 8})
        type(self.mock_machine_2).constraints = cons2

        with TemporaryFile(mode='w+', encoding='utf-8') as tempf:
            self.pc.save(tempf)
            tempf.seek(0)
            print(tempf.read())
            tempf.seek(0)
            newpc = PlacementController(
                self.mock_maas_state, self.conf)
            newpc.load(tempf)
        self.assertEqual(self.pc.assignments, newpc.assignments)
        self.assertEqual(self.pc.machines_pending(), newpc.machines_pending())
        self.assertEqual(self.pc.assigned_charm_classes(),
                         newpc.assigned_charm_classes())

        m2 = next((m for m in newpc.machines_pending()
                   if m.instance_id == 'fake-instance-id-2'))
        self.assertEqual(m2.constraints, {'cpu': 8})

    def test_load_machines_single(self):
        with NamedTemporaryFile(mode='w+', encoding='utf-8') as tempf:
            utils.spew(tempf.name, yaml.dump(dict()))
            conf = Config({}, tempf.name)

        fake_assignments = {
            'fake_iid': {'constraints': {},
                         'assignments': {'KVM':
                                         ['nova-compute']}},
            'fake_iid_2': {'constraints': {'cpu': 8},
                           'assignments':
                           {'BareMetal': ['nova-compute']}}}

        singlepc = PlacementController(
            None, conf)

        with TemporaryFile(mode='w+', encoding='utf-8') as tempf:
            yaml.dump(fake_assignments, tempf)
            tempf.seek(0)
            singlepc.load(tempf)

        self.assertEqual(set([m.instance_id for m in
                              singlepc.machines_pending()]),
                         set(['fake_iid', 'fake_iid_2']))

        m2 = next((m for m in singlepc.machines_pending()
                   if m.instance_id == 'fake_iid_2'))
        self.assertEqual(m2.constraints, {'cpu': 8})

    def test_load_error_mismatch_charm_name(self):
        """Should safely ignore (and log) a charm name in a placement file
        that can't be matched to a loaded charm class."""
        singlepc = PlacementController(None, self.conf)

        fake_assignments = {
            'fake_iid': {
                'constraints': {},
                'assignments': {'KVM':
                                ['non-existent']}},
            'fake_iid_2': {
                'constraints': {'cpu': 8},
                'assignments':
                {'BareMetal': ['nova-compute']}}}

        with TemporaryFile(mode='w+', encoding='utf-8') as tempf:
            yaml.dump(fake_assignments, tempf)
            tempf.seek(0)
            singlepc.load(tempf)

        self.assertEqual(set([m.instance_id for m in
                              singlepc.machines_pending()]),
                         set(['fake_iid_2']))

        m2 = next((m for m in singlepc.machines_pending()
                   if m.instance_id == 'fake_iid_2'))
        self.assertEqual(m2.constraints, {'cpu': 8})

    def test_is_assigned_to_is_deployed_to(self):
        self.assertFalse(self.pc.is_assigned_to(CharmSwiftProxy,
                                                self.mock_machine))
        self.assertFalse(self.pc.is_deployed_to(CharmSwiftProxy,
                                                self.mock_machine))
        self.pc.assign(self.mock_machine, CharmSwiftProxy, AssignmentType.LXC)
        self.assertFalse(self.pc.is_deployed_to(CharmSwiftProxy,
                                                self.mock_machine))
        self.assertTrue(self.pc.is_assigned_to(CharmSwiftProxy,
                                               self.mock_machine))
        self.pc.mark_deployed(self.mock_machine, CharmSwiftProxy,
                              AssignmentType.LXC)
        self.assertTrue(self.pc.is_deployed_to(CharmSwiftProxy,
                                               self.mock_machine))
        self.assertFalse(self.pc.is_assigned_to(CharmSwiftProxy,
                                                self.mock_machine))

    def test_double_clear_ok(self):
        """clearing assignments for a machine that isn't assigned (anymore) is
        OK and should do nothing
        """
        self.pc.assign(self.mock_machine, CharmSwiftProxy, AssignmentType.LXC)
        self.pc.clear_assignments(self.mock_machine)
        self.pc.clear_assignments(self.mock_machine)
        self.pc.clear_assignments(self.mock_machine_2)

    def test_gen_defaults_raises_with_no_maas_state(self):
        pc = PlacementController(None, self.conf)
        self.assertRaises(PlacementError, pc.gen_defaults)

    def test_gen_defaults_uses_only_ready(self):
        """gen_defaults should only use ready machines"""
        mock_maas_state = MagicMock()
        mock_maas_state.machines.return_value = []
        c = Config()
        c.setopt('storage_backend', 'none')
        pc = PlacementController(config=c, maas_state=mock_maas_state)
        # reset the mock to avoid looking at calls from
        # PlacementController.__init__().
        mock_maas_state.reset_mock()

        pc.gen_defaults()
        # we simply check the first call because we know that
        # follow-on calls are from calls to get_assignments and do
        # not affect machines used for defaults
        self.assertEqual(mock_maas_state.machines.mock_calls[0],
                         call(MaasMachineStatus.READY))

    def test_gen_single_backends(self):
        "gen_single has no storage backend by default"

        def find_charm(cn, defs):
            allcharms = []
            for mname, ad in defs.items():
                for atype, charmclasses in ad.items():
                    allcharms += charmclasses
            return cn in allcharms

        c = Config()
        pc = PlacementController(config=c)

        # default storage_backend is 'none'
        c.setopt('storage_backend', 'none')
        defaults = pc.gen_single()
        self.assertFalse(find_charm(CharmSwiftProxy, defaults))
        self.assertFalse(find_charm(CharmSwift, defaults))
        self.assertFalse(find_charm(CharmCeph, defaults))
        self.assertFalse(find_charm(CharmCephOSD, defaults))

        c.setopt('storage_backend', 'swift')
        defaults = pc.gen_single()
        self.assertTrue(find_charm(CharmSwiftProxy, defaults))
        self.assertTrue(find_charm(CharmSwift, defaults))
        self.assertFalse(find_charm(CharmCeph, defaults))
        self.assertFalse(find_charm(CharmCephOSD, defaults))

        c.setopt('storage_backend', 'ceph')
        defaults = pc.gen_single()
        self.assertFalse(find_charm(CharmSwiftProxy, defaults))
        self.assertFalse(find_charm(CharmSwift, defaults))
        self.assertTrue(find_charm(CharmCeph, defaults))
        self.assertFalse(find_charm(CharmCephOSD, defaults))
Exemple #53
0
class TestRenderCharmConfig(unittest.TestCase):
    def setUp(self):
        with NamedTemporaryFile(mode='w+', encoding='utf-8') as tempf:
            # Override config file to save to
            self.config = Config({}, tempf.name)

        type(self.config).cfg_path = PropertyMock(return_value='fake_cfg_path')
        self.config.setopt('openstack_password', 'fake_pw')
        self.ltp = patch('cloudinstall.utils.load_template')
        self.mock_load_template = self.ltp.start()
        self.mock_load_template.side_effect = source_tree_template_loader

    def tearDown(self):
        self.ltp.stop()

    def _do_test_osrel(self, optsvalue, expected, mockspew):
        "check that opts.openstack_release is rendered correctly"
        self.config.setopt('openstack_release', optsvalue)

        render_charm_config(self.config)
        (fake_path, generated_yaml), kwargs = mockspew.call_args
        d = yaml.load(generated_yaml)
        print(d)
        for oscharmname in [
                'nova-cloud-controller', 'glance', 'openstack-dashboard',
                'keystone', 'swift-proxy'
        ]:
            if expected is None:
                self.assertTrue(oscharmname not in d
                                or 'openstack-origin' not in d[oscharmname])
            else:
                self.assertEqual(d[oscharmname]['openstack-origin'], expected)

    def test_render_openstack_release_given(self, mockspew):
        self._do_test_osrel('klaxon', 'cloud:trusty-klaxon', mockspew)

    def _do_test_multiplier(self, is_single, mockspew, expected=None):
        if is_single:
            self.config.setopt('install_type', 'Single')
        else:
            self.config.setopt('install_type', 'Multi')
        self.config.setopt('openstack_release', 'klaxon')
        render_charm_config(self.config)
        (fake_path, generated_yaml), kwargs = mockspew.call_args
        d = yaml.load(generated_yaml)
        wmul = d['nova-cloud-controller'].get('worker-multiplier', None)
        self.assertEqual(wmul, expected)
        wmul = d['glance'].get('worker-multiplier', None)
        self.assertEqual(wmul, expected)
        wmul = d['keystone'].get('worker-multiplier', None)
        self.assertEqual(wmul, expected)

    def test_render_worker_multiplier_multi(self, mockspew):
        self._do_test_multiplier(False, mockspew)

    def test_render_worker_multiplier_single(self, mockspew):
        self._do_test_multiplier(True, mockspew, expected=1)

    def test_charmconfig_custom_merge(self, mockspew):
        """ Verify rightmost custom charm config dictionary
        does not overwrite untouched items in rendered
        charmconfig
        """
        charm_custom = {
            'swift-proxy': {
                'replicas': 15
            },
            'mysql': {
                'dataset-size': '2048M'
            }
        }
        charm_conf = yaml.load(slurp(os.path.join(DATA_DIR, 'charmconf.yaml')))
        merged_dicts = merge_dicts(charm_conf, charm_custom)
        self.assertEqual(merged_dicts['mysql']['max-connections'], 25000)
        self.assertEqual(merged_dicts['swift-proxy']['zone-assignment'],
                         'auto')
class TestRenderCharmConfig(unittest.TestCase):
    def setUp(self):
        with NamedTemporaryFile(mode="w+", encoding="utf-8") as tempf:
            # Override config file to save to
            self.config = Config({}, tempf.name)

        type(self.config).cfg_path = PropertyMock(return_value="fake_cfg_path")
        self.config.setopt("openstack_password", "fake_pw")
        self.ltp = patch("cloudinstall.utils.load_template")
        self.mock_load_template = self.ltp.start()
        self.mock_load_template.side_effect = source_tree_template_loader

    def tearDown(self):
        self.ltp.stop()

    def _do_test_osrel(self, series, optsvalue, expected, mockspew):
        "check that opts.openstack_release is rendered correctly"
        self.config.setopt("openstack_release", optsvalue)
        self.config.setopt("ubuntu_series", series)

        render_charm_config(self.config)
        (fake_path, generated_yaml), kwargs = mockspew.call_args
        d = yaml.load(generated_yaml)
        print(d)
        for oscharmname in ["nova-cloud-controller", "glance", "openstack-dashboard", "keystone", "swift-proxy"]:
            if expected is None:
                self.assertTrue(oscharmname not in d or "openstack-origin" not in d[oscharmname])
            else:
                self.assertEqual(d[oscharmname]["openstack-origin"], expected)

    def test_render_openstack_release_given(self, mockspew):
        self._do_test_osrel("trusty", "klaxon", "cloud:trusty-klaxon", mockspew)

    def _do_test_multiplier(self, is_single, mockspew, expected=None):
        if is_single:
            self.config.setopt("install_type", "Single")
        else:
            self.config.setopt("install_type", "Multi")
        self.config.setopt("openstack_release", "klaxon")
        render_charm_config(self.config)
        (fake_path, generated_yaml), kwargs = mockspew.call_args
        d = yaml.load(generated_yaml)
        wmul = d["nova-cloud-controller"].get("worker-multiplier", None)
        self.assertEqual(wmul, expected)
        wmul = d["glance"].get("worker-multiplier", None)
        self.assertEqual(wmul, expected)
        wmul = d["keystone"].get("worker-multiplier", None)
        self.assertEqual(wmul, expected)

    def test_render_worker_multiplier_multi(self, mockspew):
        self._do_test_multiplier(False, mockspew)

    def test_render_worker_multiplier_single(self, mockspew):
        self._do_test_multiplier(True, mockspew, expected=1)

    def test_charmconfig_custom_merge(self, mockspew):
        """ Verify rightmost custom charm config dictionary
        does not overwrite untouched items in rendered
        charmconfig
        """
        charm_custom = {"swift-proxy": {"replicas": 15}, "mysql": {"dataset-size": "2048M"}}
        charm_conf = yaml.load(slurp(os.path.join(DATA_DIR, "charmconf.yaml")))
        merged_dicts = merge_dicts(charm_conf, charm_custom)
        self.assertEqual(merged_dicts["mysql"]["max-connections"], 25000)
        self.assertEqual(merged_dicts["swift-proxy"]["zone-assignment"], "auto")
Exemple #55
0
class TestRenderCharmConfig(unittest.TestCase):

    def setUp(self):
        with NamedTemporaryFile(mode='w+', encoding='utf-8') as tempf:
            # Override config file to save to
            self.config = Config({}, tempf.name)

        type(self.config).cfg_path = PropertyMock(return_value='fake_cfg_path')
        self.config.setopt('openstack_password', 'fake_pw')
        self.ltp = patch('cloudinstall.utils.load_template')
        self.mock_load_template = self.ltp.start()
        self.mock_load_template.side_effect = source_tree_template_loader

    def tearDown(self):
        self.ltp.stop()

    def _do_test_osrel(self, optsvalue, expected, mockspew):
        "check that opts.openstack_release is rendered correctly"
        self.config.setopt('openstack_release', optsvalue)

        render_charm_config(self.config)
        (fake_path, generated_yaml), kwargs = mockspew.call_args
        d = yaml.load(generated_yaml)
        print(d)
        for oscharmname in ['nova-cloud-controller', 'glance',
                            'openstack-dashboard', 'keystone', 'swift-proxy']:
            if expected is None:
                self.assertTrue(oscharmname not in d or
                                'openstack-origin' not in d[oscharmname])
            else:
                self.assertEqual(d[oscharmname]['openstack-origin'], expected)

    def test_render_openstack_release_given(self, mockspew):
        self._do_test_osrel('klaxon', 'cloud:trusty-klaxon', mockspew)

    def _do_test_multiplier(self, is_single, mockspew, expected=None):
        if is_single:
            self.config.setopt('install_type', 'Single')
        else:
            self.config.setopt('install_type', 'Multi')
        self.config.setopt('openstack_release', 'klaxon')
        render_charm_config(self.config)
        (fake_path, generated_yaml), kwargs = mockspew.call_args
        d = yaml.load(generated_yaml)
        wmul = d['nova-cloud-controller'].get('worker-multiplier', None)
        self.assertEqual(wmul, expected)
        wmul = d['glance'].get('worker-multiplier', None)
        self.assertEqual(wmul, expected)
        wmul = d['keystone'].get('worker-multiplier', None)
        self.assertEqual(wmul, expected)

    def test_render_worker_multiplier_multi(self, mockspew):
        self._do_test_multiplier(False, mockspew)

    def test_render_worker_multiplier_single(self, mockspew):
        self._do_test_multiplier(True, mockspew, expected=1)

    def test_charmconfig_custom_merge(self, mockspew):
        """ Verify rightmost custom charm config dictionary
        does not overwrite untouched items in rendered
        charmconfig
        """
        charm_custom = {'swift-proxy': {'replicas': 15},
                        'mysql': {'dataset-size': '2048M'}}
        charm_conf = yaml.load(slurp(os.path.join(DATA_DIR, 'charmconf.yaml')))
        merged_dicts = merge_dicts(charm_conf, charm_custom)
        self.assertEqual(merged_dicts['mysql']['max-connections'], 25000)
        self.assertEqual(merged_dicts['swift-proxy']['zone-assignment'],
                         'auto')
 def setUp(self):
     with NamedTemporaryFile(mode='w+', encoding='utf-8') as tempf:
         # Override config file to save to
         self.conf = Config({}, tempf.name)
     self.mock_ui = MagicMock(name='ui')
class EventLoopCoreTestCase(unittest.TestCase):
    def setUp(self):
        self.conf = Config({}, save_backups=False)
        self.mock_ui = MagicMock(name='ui')
        self.mock_log = MagicMock(name='log')
        self.mock_loop = MagicMock(name='loop')

    def make_ev(self, headless=False):
        self.conf.setopt('headless', headless)
        return EventLoop(self.mock_ui, self.conf, self.mock_log)

    def test_validate_loop(self):
        """ Validate eventloop runs """
        self.conf.setopt('headless', False)
        self.conf.setopt('openstack_release', 'kilo')
        dc = Controller(ui=self.mock_ui, config=self.conf, loop=self.mock_loop)
        dc.initialize = MagicMock()
        dc.start()
        self.mock_loop.run.assert_called_once_with()

    def test_validate_redraw_screen_commit_placement(self):
        """ Validate redraw_screen on commit_placement """
        self.conf.setopt('headless', False)
        dc = Controller(ui=self.mock_ui, config=self.conf, loop=self.mock_loop)
        dc.initialize = MagicMock()
        dc.commit_placement()
        self.mock_loop.redraw_screen.assert_called_once_with()

    def test_validate_redraw_screen_enqueue(self):
        """ Validate redraw_screen on enqueue_deployed_charms """
        self.conf.setopt('headless', False)
        dc = Controller(ui=self.mock_ui, config=self.conf, loop=self.mock_loop)
        dc.initialize = MagicMock()
        dc.enqueue_deployed_charms()
        self.mock_loop.redraw_screen.assert_called_once_with()

    def test_validate_set_alarm_in(self):
        """ Validate set_alarm_in called with eventloop """
        dc = Controller(ui=self.mock_ui, config=self.conf, loop=self.mock_loop)
        dc.initialize = MagicMock()
        self.conf.node_install_wait_interval = 1
        dc.update(self.conf.node_install_wait_interval, ANY)
        self.mock_loop.set_alarm_in.assert_called_once_with(1, ANY)

    def test_validate_exit(self):
        """ Validate error code set with eventloop """
        ev = self.make_ev()
        dc = Controller(ui=self.mock_ui, config=self.conf, loop=ev)
        dc.initialize = MagicMock()
        with self.assertRaises(urwid.ExitMainLoop):
            dc.loop.exit(1)
        self.assertEqual(ev.error_code, 1)

    def test_hotkey_exit(self):
        ev = self.make_ev()
        dc = Controller(ui=self.mock_ui, config=self.conf, loop=ev)
        dc.initialize = MagicMock()
        with self.assertRaises(urwid.ExitMainLoop):
            dc.loop.header_hotkeys('q')
        self.assertEqual(ev.error_code, 0)

    def test_repr_ev(self):
        """ Prints appropriate class string for eventloop """
        ev = self.make_ev()
        dc = Controller(ui=self.mock_ui, config=self.conf, loop=ev)
        dc.initialize = MagicMock()
        self.assertEqual(str(ev), '<eventloop urwid based on tornado()>')

    def test_repr_no_ev(self):
        """ Prints appropriate class string for no eventloop """
        ev = self.make_ev(True)
        dc = Controller(ui=self.mock_ui, config=self.conf, loop=ev)
        dc.initialize = MagicMock()
        self.assertEqual(str(ev), '<eventloop disabled>')

    def test_validate_exit_no_ev(self):
        """ Validate SystemExit with no eventloop """
        ev = self.make_ev(True)
        dc = Controller(ui=self.mock_ui, config=self.conf, loop=ev)
        dc.initialize = MagicMock()
        with self.assertRaises(SystemExit) as cm:
            dc.loop.exit(1)
        exc = cm.exception
        self.assertEqual(ev.error_code, exc.code, "Found loop")
class LandscapeInstallFinalTestCase(unittest.TestCase):

    def setUp(self):
        self.mock_multi_installer = MagicMock()
        self.mock_display_controller = MagicMock()
        self.loop = MagicMock()
        with NamedTemporaryFile(mode='w+', encoding='utf-8') as tempf:
            # Override config file to save to
            self.conf = Config({}, tempf.name)

    def make_installer_with_config(self, landscape_creds=None,
                                   maas_creds=None):

        if landscape_creds is None:
            landscape_creds = dict(admin_name="fakeadminname",
                                   admin_email="*****@*****.**",
                                   system_email="*****@*****.**",
                                   maas_server='fake.host',
                                   maas_apikey='fake:keyz:yo')
        self.conf.setopt('maascreds', dict(api_host='fake.host',
                                           api_key='fake:keyz:yo'))
        self.conf.setopt('landscapecreds', landscape_creds)
        pm_binpath = PropertyMock(return_value='mockbinpath')
        type(self.conf).bin_path = pm_binpath
        pm_cfgpath = PropertyMock(return_value='mockcfgpath')
        type(self.conf).cfg_path = pm_cfgpath

        lif = LandscapeInstallFinal(self.mock_multi_installer,
                                    self.mock_display_controller,
                                    self.conf,
                                    self.loop)
        self.installer = lif

    def test_run_configure_not_sudo_user(self, mock_utils):
        """Do not sudo -u $SUDO_USER when running landscape-configure, it will
        be 'root'.
        """
        self.make_installer_with_config()
        mock_utils.get_command_output.return_value = {'status': '',
                                                      'output': ''}
        self.installer.run_configure_script()
        mock_utils.get_command_output.assert_called_with(ANY, timeout=None)

    def test_run_configure_quotes_config_values(self, mock_utils):
        cd = dict(admin_name="fake admin name with spaces",
                  admin_email="[email protected]",
                  system_email="[email protected]")
        self.make_installer_with_config(landscape_creds=cd)
        mock_utils.get_command_output.return_value = {'status': '',
                                                      'output': ''}
        self.installer.run_configure_script()
        expectedcmdstr = ("mockbinpath/configure-landscape "
                          "--admin-email '{}' "
                          "--admin-name '{}' "
                          "--system-email '{}' "
                          "--maas-host fake.host".format(cd['admin_email'],
                                                         cd['admin_name'],
                                                         cd['system_email']))
        mock_utils.get_command_output.assert_called_with(expectedcmdstr,
                                                         timeout=None)

    def test_run_configure_raises_on_error(self, mock_utils):
        self.make_installer_with_config()
        mock_utils.get_command_output.return_value = {'status': 1,
                                                      'output': 'failure'}
        self.assertRaises(Exception, self.installer.run_configure_script)

    def test_run_deployer_raises_on_error(self, mock_utils):
        self.make_installer_with_config()
        mock_utils.get_command_output.return_value = {'status': 1,
                                                      'output': 'failure'}
        self.assertRaises(Exception, self.installer.run_deployer)

    def test_run_deployer_has_no_timeout(self, mock_utils):
        self.make_installer_with_config()
        mock_utils.get_command_output.return_value = {'status': '',
                                                      'output': 'failure'}
        self.installer.run_deployer()
        mock_utils.get_command_output.assert_called_with(ANY, timeout=None,
                                                         user_sudo=ANY)