Example #1
0
class RoleManagerTests(TestCase):

    def setUp(self):
        super(RoleManagerTests, self).setUp()
        self.role_manager = RoleManager()

        self.template_store = TemplateStore()

    def test_list_roles(self):
        # Test
        self._populate_roles()
        all_roles = self.role_manager.list_roles()

        # Verify
        self.assertEqual(3, len(all_roles))
        self.assertTrue(isinstance(all_roles[0], Role))
        all_roles.sort(key=lambda x: '%s-%s' % (x.name, x.version))

        self.assertEqual('r1', all_roles[0].name)
        self.assertEqual(1, all_roles[0].version)

        self.assertEqual('r1', all_roles[1].name)
        self.assertEqual(2, all_roles[1].version)

        self.assertEqual('r2', all_roles[2].name)
        self.assertEqual(1, all_roles[2].version)

    def test_list_roles_only_latest(self):
        # Setup
        list_mock = mock.MagicMock()
        self.role_manager.template_store.list = list_mock
        list_mock.return_value = []

        # Test
        self.role_manager.list_roles(only_latest=True)

        # Verify
        list_mock.assert_called_once_with(only_latest=True)

    def test_retrieve_role_by_uuid(self):
        # Test
        added_roles = self._populate_roles()
        found = self.role_manager.retrieve_role_by_uuid(added_roles[0].uuid)

        # Verify
        self.assertTrue(found is not None)
        self.assertTrue(isinstance(found, Role))
        self.assertEqual(found.name, 'r1')
        self.assertEqual(found.version, 2)

    def test_retrieve_role_by_fake_uuid(self):
        self.assertRaises(UnknownUUID,
                          self.role_manager.retrieve_role_by_uuid,
                          'fake')

    def _populate_roles(self):
        r1 = self.template_store.create('r1', TEST_TEMPLATE)
        r1 = self.template_store.update(r1.uuid, TEST_TEMPLATE)
        r2 = self.template_store.create('r2', TEST_TEMPLATE)
        return [r1, r2]
Example #2
0
    def setUp(self):
        super(BaseDriverTests, self).setUp()

        self.mock_client = Mock()
        self.mock_stdout = Mock()
        self.driver = _DummyDriver(self.mock_client, out=self.mock_stdout)
        self.store = TemplateStore(self.driver)
Example #3
0
def _check_roles_exist(role_ids):
    store = TemplateStore()
    for i in role_ids:
        try:
            store.retrieve(i)
        except UnknownUUID:
            sys.stderr.write("No role with id %s " % i)
            raise
Example #4
0
def _check_roles_exist(role_ids):
    store = TemplateStore()
    for i in role_ids:
        try:
            store.retrieve(i)
        except UnknownUUID:
            sys.stderr.write("No role with id %s " % i)
            raise
Example #5
0
 def __init__(self):
     super(PlansManager, self).__init__()
     self.plan_store = DeploymentPlanStore()
     self.seed_store = MasterSeedStore()
     self.registry_store = ResourceRegistryStore()
     self.template_store = TemplateStore()
     self.master_template_store = MasterTemplateStore()
     self.environment_store = EnvironmentFileStore()
Example #6
0
    def setUp(self):
        super(PlansManagerTestCase, self).setUp()
        self.plans_manager = PlansManager()

        self.plan_store = DeploymentPlanStore()
        self.template_store = TemplateStore()
        self.seed_store = MasterSeedStore()
        self.registry_store = ResourceRegistryStore()
Example #7
0
class RoleManagerTests(TestCase):
    def setUp(self):
        super(RoleManagerTests, self).setUp()
        self.role_manager = RoleManager()

        self.template_store = TemplateStore()

    def test_list_roles(self):
        # Test
        self._populate_roles()
        all_roles = self.role_manager.list_roles()

        # Verify
        self.assertEqual(3, len(all_roles))
        self.assertTrue(isinstance(all_roles[0], Role))
        all_roles.sort(key=lambda x: '%s-%s' % (x.name, x.version))

        self.assertEqual('r1', all_roles[0].name)
        self.assertEqual(1, all_roles[0].version)

        self.assertEqual('r1', all_roles[1].name)
        self.assertEqual(2, all_roles[1].version)

        self.assertEqual('r2', all_roles[2].name)
        self.assertEqual(1, all_roles[2].version)

    def test_list_roles_only_latest(self):
        # Setup
        list_mock = mock.MagicMock()
        self.role_manager.template_store.list = list_mock
        list_mock.return_value = []

        # Test
        self.role_manager.list_roles(only_latest=True)

        # Verify
        list_mock.assert_called_once_with(only_latest=True)

    def test_retrieve_role_by_uuid(self):
        # Test
        added_roles = self._populate_roles()
        found = self.role_manager.retrieve_role_by_uuid(added_roles[0].uuid)

        # Verify
        self.assertTrue(found is not None)
        self.assertTrue(isinstance(found, Role))
        self.assertEqual(found.name, 'r1')
        self.assertEqual(found.version, 2)

    def test_retrieve_role_by_fake_uuid(self):
        self.assertRaises(UnknownUUID, self.role_manager.retrieve_role_by_uuid,
                          'fake')

    def _populate_roles(self):
        r1 = self.template_store.create('r1', TEST_TEMPLATE)
        r1 = self.template_store.update(r1.uuid, TEST_TEMPLATE)
        r2 = self.template_store.create('r2', TEST_TEMPLATE)
        return [r1, r2]
Example #8
0
class BaseDriverTests(TestCase):
    def setUp(self):
        super(BaseDriverTests, self).setUp()

        self.mock_client = Mock()
        self.mock_stdout = Mock()
        self.driver = _DummyDriver(self.mock_client, out=self.mock_stdout)
        self.store = TemplateStore(self.driver)

    def test_create(self):
        self.driver.create(self.store, "swift.yaml", "YAML")
        self.mock_client.create.assert_called_once_with(
            self.store, "swift.yaml", "YAML")
        self.mock_client.create.assert_called_once_with(
            self.store, "swift.yaml", "YAML")

    def test_retrieve(self):

        self.driver.retrieve(self.store, "uuid")
        self.mock_client.retrieve.assert_called_once_with(self.store, "uuid")

    def test_update(self):
        self.driver.update(self.store, "uuid", "swift2.yaml", "YAML2")
        self.mock_client.update.assert_called_once_with(
            self.store, "uuid", "swift2.yaml", "YAML2")

    def test_delete(self):
        self.driver.delete(self.store, "uuid")
        self.mock_client.delete.assert_called_once_with(self.store, "uuid")

    def test_list(self):
        self.driver.list(self.store)
        self.mock_client.list.assert_called_once_with(self.store,
                                                      only_latest=False)

        self.mock_client.list.reset_mock()

        self.store.list(only_latest=True)
        self.mock_client.list.assert_called_once_with(self.store,
                                                      only_latest=True)

    def test_retrieve_by_name(self):

        self.driver.retrieve_by_name(self.store, "name")
        self.mock_client.retrieve_by_name.assert_called_once_with(self.store,
                                                                  "name",
                                                                  version=None)

        self.mock_client.retrieve_by_name.reset_mock()

        self.driver.retrieve_by_name(self.store, "name", version=2)
        self.mock_client.retrieve_by_name.assert_called_once_with(self.store,
                                                                  "name",
                                                                  version=2)
Example #9
0
class BaseDriverTests(TestCase):

    def setUp(self):
        super(BaseDriverTests, self).setUp()

        self.mock_client = Mock()
        self.mock_stdout = Mock()
        self.driver = _DummyDriver(self.mock_client, out=self.mock_stdout)
        self.store = TemplateStore(self.driver)

    def test_create(self):
        self.driver.create(self.store, "swift.yaml", "YAML")
        self.mock_client.create.assert_called_once_with(
            self.store, "swift.yaml", "YAML")
        self.mock_client.create.assert_called_once_with(
            self.store, "swift.yaml", "YAML")

    def test_retrieve(self):

        self.driver.retrieve(self.store, "uuid")
        self.mock_client.retrieve.assert_called_once_with(self.store, "uuid")

    def test_update(self):
        self.driver.update(self.store, "uuid", "swift2.yaml", "YAML2")
        self.mock_client.update.assert_called_once_with(
            self.store, "uuid", "swift2.yaml", "YAML2")

    def test_delete(self):
        self.driver.delete(self.store, "uuid")
        self.mock_client.delete.assert_called_once_with(self.store, "uuid")

    def test_list(self):
        self.driver.list(self.store)
        self.mock_client.list.assert_called_once_with(
            self.store, only_latest=False)

        self.mock_client.list.reset_mock()

        self.store.list(only_latest=True)
        self.mock_client.list.assert_called_once_with(
            self.store, only_latest=True)

    def test_retrieve_by_name(self):

        self.driver.retrieve_by_name(self.store, "name")
        self.mock_client.retrieve_by_name.assert_called_once_with(
            self.store, "name", version=None)

        self.mock_client.retrieve_by_name.reset_mock()

        self.driver.retrieve_by_name(self.store, "name", version=2)
        self.mock_client.retrieve_by_name.assert_called_once_with(
            self.store, "name", version=2)
Example #10
0
def create_or_update(name, contents, store=None, relative_path='',
                     registry_path=''):
    if store is None:
        store = TemplateStore()
    try:
        role = store.retrieve_by_name(name)
        if role.contents != contents:
            role = store.update(role.uuid, contents, relative_path,
                                registry_path)

        return False, role
    except UnknownName:
        return True, store.create(name, contents, relative_path, registry_path)
Example #11
0
def _create_or_update(name, contents, store=None):

    if store is None:
        store = TemplateStore()

    try:
        role = store.retrieve_by_name(name)

        if role.contents != contents:
            role = store.update(role.uuid, contents)

        return False, role
    except UnknownName:
        return True, store.create(name, contents)
Example #12
0
    def setUp(self):
        super(BaseDriverTests, self).setUp()

        self.mock_client = Mock()
        self.mock_stdout = Mock()
        self.driver = _DummyDriver(self.mock_client, out=self.mock_stdout)
        self.store = TemplateStore(self.driver)
Example #13
0
def create_or_update(name,
                     contents,
                     store=None,
                     relative_path='',
                     registry_path=''):
    if store is None:
        store = TemplateStore()
    try:
        role = store.retrieve_by_name(name)
        if role.contents != contents:
            role = store.update(role.uuid, contents, relative_path,
                                registry_path)

        return False, role
    except UnknownName:
        return True, store.create(name, contents, relative_path, registry_path)
Example #14
0
    def setUp(self):
        super(PlansManagerTestCase, self).setUp()
        self.plans_manager = PlansManager()

        self.plan_store = DeploymentPlanStore()
        self.template_store = TemplateStore()
        self.seed_store = MasterSeedStore()
        self.registry_store = ResourceRegistryStore()
        self.registry_mapping_store = ResourceRegistryMappingStore()
Example #15
0
def load_role(name, file_path, extra_data=None, relative_path=''):
    name = role_name_from_path(file_path) if (name == '') else name
    all_roles, created, updated = load_roles(roles=[],
                                             seed_file=None,
                                             resource_registry_path=None,
                                             role_extra=extra_data)
    process_role(file_path, name, TemplateStore(), all_roles, created, updated,
                 relative_path)
    return created, updated
Example #16
0
 def __init__(self):
     super(PlansManager, self).__init__()
     self.plan_store = DeploymentPlanStore()
     self.seed_store = MasterSeedStore()
     self.registry_store = ResourceRegistryStore()
     self.registry_mapping_store = ResourceRegistryMappingStore()
     self.template_store = TemplateStore()
     self.template_extra_store = TemplateExtraStore()
     self.master_template_store = MasterTemplateStore()
     self.environment_store = EnvironmentFileStore()
Example #17
0
class RoleManager(object):

    def __init__(self):
        super(RoleManager, self).__init__()
        self.template_store = TemplateStore()

    def list_roles(self, only_latest=False):
        """Returns a list of all roles known to Tuskar.

        :param only_latest: if true, only the highest version of each role
               will be returned
        :type  only_latest: bool
        :return: list of tuskar model instances for each role
        :rtype:  [tuskar.manager.models.Role]
        """
        db_roles = self.template_store.list(only_latest=only_latest)
        roles = [self._role_to_tuskar_object(r) for r in db_roles]
        return roles

    def retrieve_role_by_uuid(self, role_uuid):
        """Returns the role with the given UUID.

        :type role_uuid: str
        :rtype: tuskar.manager.models.Role
        :raises tuskar.storage.exceptions.UnknownUUID: if there is no role with
                the given ID
        """
        db_role = self.template_store.retrieve(role_uuid)
        role = self._role_to_tuskar_object(db_role)
        return role

    @staticmethod
    def _role_to_tuskar_object(db_role):
        parsed = parser.parse_template(db_role.contents)
        role = models.Role(db_role.uuid, db_role.name, db_role.version,
                           parsed.description, parsed)
        return role
Example #18
0
class RoleManager(object):
    def __init__(self):
        super(RoleManager, self).__init__()
        self.template_store = TemplateStore()

    def list_roles(self, only_latest=False):
        """Returns a list of all roles known to Tuskar.

        :param only_latest: if true, only the highest version of each role
               will be returned
        :type  only_latest: bool
        :return: list of tuskar model instances for each role
        :rtype:  [tuskar.manager.models.Role]
        """
        db_roles = self.template_store.list(only_latest=only_latest)
        roles = [self._role_to_tuskar_object(r) for r in db_roles]
        return roles

    def retrieve_role_by_uuid(self, role_uuid):
        """Returns the role with the given UUID.

        :type role_uuid: str
        :rtype: tuskar.manager.models.Role
        :raises tuskar.storage.exceptions.UnknownUUID: if there is no role with
                the given ID
        """
        db_role = self.template_store.retrieve(role_uuid)
        role = self._role_to_tuskar_object(db_role)
        return role

    @staticmethod
    def _role_to_tuskar_object(db_role):
        parsed = parser.parse_template(db_role.contents)
        role = models.Role(db_role.uuid, db_role.name, db_role.version,
                           parsed.description, parsed)
        return role
Example #19
0
class PlansManager(object):

    def __init__(self):
        super(PlansManager, self).__init__()
        self.plan_store = DeploymentPlanStore()
        self.seed_store = MasterSeedStore()
        self.registry_store = ResourceRegistryStore()
        self.registry_mapping_store = ResourceRegistryMappingStore()
        self.template_store = TemplateStore()
        self.template_extra_store = TemplateExtraStore()
        self.master_template_store = MasterTemplateStore()
        self.environment_store = EnvironmentFileStore()

    def create_plan(self, name, description):
        """Creates a new plan, persisting it to Tuskar's storage backend.

        :type name: str
        :type description: str
        :return: domain model instance of the created plan
        :rtype: tuskar.manager.models.DeploymentPlan
        :raises tuskar.storage.exceptions.NameAlreadyUsed: if there is a plan
                with the given name
        """

        # Create the plan using the template generation code first to
        # stub out the master template and environment files correctly.
        new_plan = plan.DeploymentPlan(description=description)

        # Save the newly created master template and environment to the
        # storage layer so they can be associated with the plan.
        master_template_contents = composer.compose_template(
            new_plan.master_template
        )
        master_template_file = self.master_template_store.create(
            name_utils.master_template_filename(name),
            master_template_contents,
        )

        environment_contents = composer.compose_environment(
            new_plan.environment
        )
        environment_file = self.environment_store.create(
            environment_contents
        )

        # Create the plan in storage, seeding it with the stored files for
        # the template and environment.
        db_plan = self.plan_store.create(
            name,
            master_template_uuid=master_template_file.uuid,
            environment_uuid=environment_file.uuid,
        )

        # Return the created plan.
        created_plan = self.retrieve_plan(db_plan.uuid)
        return created_plan

    def delete_plan(self, plan_uuid):
        """Deletes an existing plan.

        :type plan_uuid: str
        :raises tuskar.storage.exceptions.UnknownUUID: if there is no plan
                with the given UUID
        """
        self.plan_store.delete(plan_uuid)

    def add_role_to_plan(self, plan_uuid, role_uuid):
        """Adds a role to the given plan, storing the changes in Tuskar's
        storage.

        :type plan_uuid: str
        :type role_uuid: str
        :return: updated plan model instance
        :rtype: tuskar.manager.models.DeploymentPlan
        :raises tuskar.storage.exceptions.UnknownUUID: if either the plan
                or the role cannot be found
        """
        # Load the plan and role from storage
        db_plan = self.plan_store.retrieve(plan_uuid)
        db_role = self.template_store.retrieve(role_uuid)

        # Parse the plan and role template into template objects.
        deployment_plan = self._plan_to_template_object(db_plan)
        role_template = self._role_to_template_object(db_role)

        # See if a master seed template has been set.
        try:
            db_master_seed = self.seed_store.retrieve_by_name(MASTER_SEED_NAME)
            master_seed = parser.parse_template(db_master_seed.contents)
        except UnknownName:
            master_seed = None
            special_properties = None

        def _find_role_type(registry):
            for path in registry.keys():
                if path in db_role.registry_path:
                    return registry[path]

        if master_seed is not None:
            try:
                db_registry_env = self.registry_store.retrieve_by_name(
                    RESOURCE_REGISTRY_NAME).contents
            except UnknownName:
                LOG.error("Could not load resource_registry. Make sure you "
                          "pass --resource-registry to tuskar-load-roles.")
                raise

            parsed_registry_env = parser.parse_environment(db_registry_env)
            registry = dict((e.filename, e.alias)
                            for e in parsed_registry_env.registry_entries)
            role_type = _find_role_type(registry)
            special_properties = template_seed.get_property_map_for_role(
                master_seed, role_type)

        # Use the combination logic to perform the addition.
        role_namespace = name_utils.generate_role_namespace(db_role.name,
                                                            db_role.version)
        template_filename = (
            name_utils.role_template_filename(db_role.name, db_role.version,
                                              db_role.relative_path))
        deployment_plan.add_template(role_namespace, role_template,
                                     template_filename,
                                     override_properties=special_properties)

        # If there is a master seed, add its top-level elements to the plan.
        # These calls are idempotent, so it's safe to call each time a role
        # is added.
        if master_seed is not None:
            template_seed.add_top_level_parameters(
                master_seed,
                deployment_plan.master_template,
                deployment_plan.environment)
            template_seed.add_top_level_resources(
                master_seed, deployment_plan.master_template)
            template_seed.add_top_level_outputs(
                master_seed, deployment_plan.master_template)

            if role_type is None:
                LOG.error(
                    "Role '%s' not found in seed template." % db_role.name)
                raise ValueError(db_role.name)
            seed_role = template_seed.find_role_from_type(
                master_seed.resources, role_type)
            if seed_role is None:
                LOG.error(
                    "Role '%s' of type '%s' not found in seed template." %
                    (db_role.name, role_type))
                raise ValueError(db_role.name)

            # These calls are idempotent, but must be called on each role as
            # new references may have been added.
            template_seed.update_role_resource_references(
                deployment_plan.master_template,
                seed_role,
                db_role.name)

            template_seed.update_role_property_references(
                deployment_plan.master_template,
                seed_role,
                role_namespace)

            # Update environment file to add top level mappings, which is made
            # up of all non-role files present in the resource registry, plus
            # required aliases
            reg_mapping = self.registry_mapping_store.list()

            environment = deployment_plan.environment
            for entry in parsed_registry_env.registry_entries:
                # check if registry_mapping is in database, if so add to
                # environment (later will become environment.yaml)
                if any(x.name == entry.filename for x in reg_mapping):
                    additem = RegistryEntry(entry.alias, entry.filename)
                    environment.add_registry_entry(additem, unique=True)

        # Save the updated plan.
        updated = self._save_updated_plan(plan_uuid, deployment_plan)

        return updated

    def remove_role_from_plan(self, plan_uuid, role_uuid):
        """Removes a role from the given plan.

        :type plan_uuid: str
        :type role_uuid: str
        :raise tuskar.storage.exceptions.UnknownUUID: if the plan or role
               doesn't exist
        """

        # Load the objects from storage.
        db_plan = self.plan_store.retrieve(plan_uuid)
        db_role = self.template_store.retrieve(role_uuid)

        # Parse the plan into template objects.
        deployment_plan = self._plan_to_template_object(db_plan)

        # Delete the role from the plan by it's namespace.
        role_namespace = name_utils.generate_role_namespace(db_role.name,
                                                            db_role.version)
        deployment_plan.remove_template(role_namespace)

        # Save the updated plan.
        updated = self._save_updated_plan(plan_uuid, deployment_plan)

        return updated

    def retrieve_plan(self, plan_uuid):
        """Loads the given plan.

        :type plan_uuid: str
        :rtype: tuskar.manager.models.DeploymentPlan
        :raises tuskar.storage.exceptions.UnknownUUID: if there is no plan
                with the given UUID
        """

        # Load the plan from the database.
        db_plan = self.plan_store.retrieve(plan_uuid)

        # Parse the plan into the template model.
        master_template = parser.parse_template(
            db_plan.master_template.contents
        )
        environment = parser.parse_environment(
            db_plan.environment_file.contents
        )

        # Create the Tuskar model for the plan.
        deployment_plan = models.DeploymentPlan(
            plan_uuid,
            db_plan.name,
            master_template.description,
            created_at=db_plan.created_at,
            updated_at=db_plan.updated_at,
        )

        roles = self._find_roles(environment)
        deployment_plan.add_roles(*roles)

        params = self._find_parameters(master_template, environment)
        deployment_plan.add_parameters(*params)

        return deployment_plan

    def list_plans(self):
        """Returns a list of all plans stored in Tuskar.

        :return: list of plan instances; empty list if there are no plans
        :rtype: [tuskar.manager.models.DeploymentPlan]
        """

        # Given the expected number of plans being managed by Tuskar (in the
        # tens), this should be sufficient. If our scale gets larger, we may
        # need a smarter batch operation here than simply iterating over the
        # list of all plans. jdob, Aug 7, 2014

        plan_uuids = [p.uuid for p in self.plan_store.list()]
        plans = [self.retrieve_plan(p) for p in plan_uuids]

        return plans

    def set_parameter_values(self, plan_uuid, params):
        """Sets the values for a plan's parameters.

        :type plan_uuid: str
        :type params: [tuskar.manager.models.ParameterValue]

        :return: plan instance with the updated values
        :rtype:  tuskar.manager.models.DeploymentPlan
        """

        # Load the plan from the database.
        db_plan = self.plan_store.retrieve(plan_uuid)

        # Save the values to the parsed environment.
        environment = parser.parse_environment(
            db_plan.environment_file.contents
        )

        non_existent_params = []
        for param_value in params:
            p = environment.find_parameter_by_name(param_value.name)
            if p:
                p.value = param_value.value
            else:
                non_existent_params.append(param_value.name)

        if non_existent_params:
            param_names = ', '.join(non_existent_params)
            LOG.error(
                'There are no parameters named %(param_names)s'
                ' in plan %(plan_uuid)s.' %
                {'param_names': param_names, 'plan_uuid': plan_uuid})
            raise exception.PlanParametersNotExist(
                plan_uuid=plan_uuid,
                param_names=param_names
            )

        # Save the updated environment.
        env_contents = composer.compose_environment(environment)
        self.plan_store.update_environment(plan_uuid, env_contents)

        updated_plan = self.retrieve_plan(plan_uuid)
        return updated_plan

    def package_templates(self, plan_uuid):
        """Packages and returns all of the templates related to the given plan.
        The returned dictionary is keyed by filename and contains the contents
        of that file (a template or an environment file).

        :type plan_uuid: str

        :return: mapping of filename to contents for each file in the plan
        :rtype:  dict

        :raises tuskar.storage.exceptions.UnknownUUID: if there is no plan
                with the given UUID
        """

        # Load and parse the plan.
        db_plan = self.plan_store.retrieve(plan_uuid)
        master_template = parser.parse_template(
            db_plan.master_template.contents
        )
        environment = parser.parse_environment(
            db_plan.environment_file.contents
        )

        # Compose the plan files and all plan roles and package them into
        # a single dictionary.
        plan_contents = composer.compose_template(master_template)
        env_contents = composer.compose_environment(environment)

        files_dict = {
            'plan.yaml': plan_contents,
            'environment.yaml': env_contents,
        }

        plan_roles = self._find_roles(environment)
        manager = RoleManager()
        for role in plan_roles:
            contents = composer.compose_template(role.template)
            filename = name_utils.role_template_filename(role.name,
                                                         role.version,
                                                         role.relative_path)
            files_dict[filename] = contents

        def _add_template_extra_data_for(templates, template_store):
            template_extra_data = manager.retrieve_db_role_extra()
            for template in templates:
                db_template = template_store.retrieve_by_name(template.name)
                prefix = os_path.split(db_template.name)[0]
                template_extra_paths = utils.resolve_template_extra_data(
                    db_template, template_extra_data)
                extra_data_output = manager.template_extra_data_for_output(
                    template_extra_paths, prefix)
                files_dict.update(extra_data_output)

        # also grab any extradata files for the role
        _add_template_extra_data_for(plan_roles, self.template_store)

        # in addition to provider roles above, return non-role template files
        reg_mapping = self.registry_mapping_store.list()
        for entry in reg_mapping:
            if os_path.splitext(entry.name)[1] in ('.yaml', '.yml'):
                # if entry is an alias, don't include it
                files_dict[entry.name] = entry.contents

        # similarly, also grab extradata files for the non role templates
        _add_template_extra_data_for(reg_mapping, self.registry_mapping_store)

        return files_dict

    def _find_roles(self, environment):
        """Returns a list of roles for a plan (identified by the given
        environment).

        :type environment: tuskar.templates.heat.Environment
        :return: list of role instances; empty list if none are in the
                 environment
        :rtype: [tuskar.manager.models.Role]
        """

        def load_role(entry):
            # Figure out the role name/version for the given entry.
            namespace = ns_utils.remove_resource_alias_namespace(entry.alias)
            name, version = name_utils.parse_role_namespace(namespace)

            # Load the role from the database and parse into the
            # template objects.
            db_role = self.template_store.retrieve_by_name(name, int(version))
            role = self._role_to_template_object(db_role)

            # Convert to the Tuskar domain model.
            tuskar_role = models.Role(db_role.uuid, name, version,
                                      role.description, role,
                                      relative_path=db_role.relative_path)
            return tuskar_role

        reg_mapping = self.registry_mapping_store.list()
        roles = [load_role(e) for e in environment.registry_entries
                 if not any(x.name == e.filename for x in reg_mapping)]
        return roles

    @staticmethod
    def _find_parameters(template, environment):
        """Returns a list of parameters for a plan. The parameters will contain
        both metadata about the parameter itself (name, label, description,
        etc.) as well as it's current value for the plan.

        :type template: tuskar.templates.heat.Template
        :type environment: tuskar.templates.heat.Environment
        :return: list of parameter instances; empty list if there are no
                 parameters in the plan
        :rtype: [tuskar.manager.models.PlanParameter]
        """

        def generate_param(p):
            env_param = environment.find_parameter_by_name(p.name)
            return models.PlanParameter(
                p.name, env_param.value, p.param_type,
                p.description, p.label, p.default, p.hidden, p.constraints
            )
        params = [generate_param(tp) for tp in template.parameters]
        return params

    @staticmethod
    def _plan_to_template_object(db_plan):
        master_template = parser.parse_template(
            db_plan.master_template.contents
        )
        environment = parser.parse_environment(
            db_plan.environment_file.contents
        )
        deployment_plan = plan.DeploymentPlan(master_template=master_template,
                                              environment=environment)
        return deployment_plan

    @staticmethod
    def _role_to_template_object(db_role):
        role_template = parser.parse_template(db_role.contents)
        return role_template

    def _save_updated_plan(self, plan_uuid, deployment_plan):
        new_template_contents = composer.compose_template(
            deployment_plan.master_template
        )
        new_env_contents = composer.compose_environment(
            deployment_plan.environment
        )

        self.plan_store.update_master_template(plan_uuid,
                                               new_template_contents)
        self.plan_store.update_environment(plan_uuid,
                                           new_env_contents)

        # Retrieve and return the updated plan
        updated_plan = self.retrieve_plan(plan_uuid)
        return updated_plan
Example #20
0
class RoleManager(object):
    def __init__(self):
        super(RoleManager, self).__init__()
        self.template_store = TemplateStore()
        self.template_extra_store = TemplateExtraStore()

    def list_roles(self, only_latest=False):
        """Returns a list of all roles known to Tuskar.

        :param only_latest: if true, only the highest version of each role
               will be returned
        :type  only_latest: bool
        :return: list of tuskar model instances for each role
        :rtype:  [tuskar.manager.models.Role]
        """
        db_roles = self.template_store.list(only_latest=only_latest)
        roles = [self._role_to_tuskar_object(r) for r in db_roles]
        return roles

    def retrieve_role_by_uuid(self, role_uuid):
        """Returns the role with the given UUID.

        :type role_uuid: str
        :rtype: tuskar.manager.models.Role
        :raises tuskar.storage.exceptions.UnknownUUID: if there is no role with
                the given ID
        """
        db_role = self.template_store.retrieve(role_uuid)
        role = self._role_to_tuskar_object(db_role)
        return role

    def retrieve_db_role_by_uuid(self, role_uuid):
        return self.template_store.retrieve(role_uuid)

    def retrieve_db_role_extra(self):
        return self.template_extra_store.list(only_latest=False)

    def template_extra_data_for_output(self, template_extra_paths, prefix=''):
        """Compile and return role-extra data for output as a string

            :param template_extra_paths: a list of {k,v} (name=>path)
            :type template_extra_paths: list of dict

            :param prefix: a prefix path
            :type prefix: string

            :return: a dict of path=>contents
            :rtype: dict

            The keys in template_extra_paths correspond to the names of stored
            role-extra data and the values are the paths at which the
            corresponding files ares expected to be. This list is returned by
            common.utils.resolve_template_extra_data for example:

                [{'extra_common_yaml': 'hieradata/common.yaml'},
                 {'extra_object_yaml': 'hieradata/object.yaml'}]

            Using this create a new dict that maps the path (values above) as
            key to the contents of the corresponding stored role-extra object
            (using the name above to retrieve it). For the example input
            above, the output would be like:

            {
                "hieradata/common.yaml": "CONTENTS",
                "hieradata/object.yaml": "CONTENTS"
            }

            In those cases that the template_extra_paths were generated for a
            non Role template (i.e. those templates read from the resource
            registry), include their path prefix - so that the extra data files
            are created relative to the template. For example the template
            'path/to/some_template.yaml' has a reference to the extra-data file
            'hieradata/common.yaml'. The resulting extra-data file returned by
            tuskar must then be:

            {
                "path/to/hieradata/common.yaml": "CONTENTS",
            }

        """
        res = {}
        for path in template_extra_paths:
            role_extra_name = path.keys()[0]
            role_extra_path = path[role_extra_name]
            db_role_extra = self.template_extra_store.retrieve_by_name(
                role_extra_name)
            role_extra_path = os_path.join(prefix, role_extra_path)
            res[role_extra_path] = db_role_extra.contents
        return res

    @staticmethod
    def _role_to_tuskar_object(db_role):
        parsed = parser.parse_template(db_role.contents)
        role = models.Role(db_role.uuid, db_role.name, db_role.version,
                           parsed.description, parsed)
        return role
Example #21
0
    def setUp(self):
        super(RoleManagerTests, self).setUp()
        self.role_manager = RoleManager()

        self.template_store = TemplateStore()
Example #22
0
    def setUp(self):
        super(ModelsTests, self).setUp()

        self.driver = Mock()
        self.store = TemplateStore(self.driver)
Example #23
0
 def __init__(self):
     super(RoleManager, self).__init__()
     self.template_store = TemplateStore()
     self.template_extra_store = TemplateExtraStore()
Example #24
0
class RoleManager(object):

    def __init__(self):
        super(RoleManager, self).__init__()
        self.template_store = TemplateStore()
        self.template_extra_store = TemplateExtraStore()

    def list_roles(self, only_latest=False):
        """Returns a list of all roles known to Tuskar.

        :param only_latest: if true, only the highest version of each role
               will be returned
        :type  only_latest: bool
        :return: list of tuskar model instances for each role
        :rtype:  [tuskar.manager.models.Role]
        """
        db_roles = self.template_store.list(only_latest=only_latest)
        roles = [self._role_to_tuskar_object(r) for r in db_roles]
        return roles

    def retrieve_role_by_uuid(self, role_uuid):
        """Returns the role with the given UUID.

        :type role_uuid: str
        :rtype: tuskar.manager.models.Role
        :raises tuskar.storage.exceptions.UnknownUUID: if there is no role with
                the given ID
        """
        db_role = self.template_store.retrieve(role_uuid)
        role = self._role_to_tuskar_object(db_role)
        return role

    def retrieve_db_role_by_uuid(self, role_uuid):
        return self.template_store.retrieve(role_uuid)

    def retrieve_db_role_extra(self):
        return self.template_extra_store.list(only_latest=False)

    def template_extra_data_for_output(self, template_extra_paths, prefix=''):
        """Compile and return role-extra data for output as a string

            :param template_extra_paths: a list of {k,v} (name=>path)
            :type template_extra_paths: list of dict

            :param prefix: a prefix path
            :type prefix: string

            :return: a dict of path=>contents
            :rtype: dict

            The keys in template_extra_paths correspond to the names of stored
            role-extra data and the values are the paths at which the
            corresponding files ares expected to be. This list is returned by
            common.utils.resolve_template_extra_data for example:

                [{'extra_common_yaml': 'hieradata/common.yaml'},
                 {'extra_object_yaml': 'hieradata/object.yaml'}]

            Using this create a new dict that maps the path (values above) as
            key to the contents of the corresponding stored role-extra object
            (using the name above to retrieve it). For the example input
            above, the output would be like:

            {
                "hieradata/common.yaml": "CONTENTS",
                "hieradata/object.yaml": "CONTENTS"
            }

            In those cases that the template_extra_paths were generated for a
            non Role template (i.e. those templates read from the resource
            registry), include their path prefix - so that the extra data files
            are created relative to the template. For example the template
            'path/to/some_template.yaml' has a reference to the extra-data file
            'hieradata/common.yaml'. The resulting extra-data file returned by
            tuskar must then be:

            {
                "path/to/hieradata/common.yaml": "CONTENTS",
            }

        """
        res = {}
        for path in template_extra_paths:
            role_extra_name = path.keys()[0]
            role_extra_path = path[role_extra_name]
            db_role_extra = self.template_extra_store.retrieve_by_name(
                role_extra_name)
            role_extra_path = os_path.join(prefix, role_extra_path)
            res[role_extra_path] = db_role_extra.contents
        return res

    @staticmethod
    def _role_to_tuskar_object(db_role):
        parsed = parser.parse_template(db_role.contents)
        role = models.Role(db_role.uuid, db_role.name, db_role.version,
                           parsed.description, parsed)
        return role
Example #25
0
    def setUp(self):
        super(RoleManagerTests, self).setUp()
        self.role_manager = RoleManager()

        self.template_store = TemplateStore()
Example #26
0
def _delete_role(role_id):
    TemplateStore().delete(role_id)
Example #27
0
class PlansManagerTestCase(TestCase):

    def setUp(self):
        super(PlansManagerTestCase, self).setUp()
        self.plans_manager = PlansManager()

        self.plan_store = DeploymentPlanStore()
        self.template_store = TemplateStore()
        self.seed_store = MasterSeedStore()
        self.registry_store = ResourceRegistryStore()
        self.registry_mapping_store = ResourceRegistryMappingStore()

    def test_create_plan(self):
        # Tests
        created = self.plans_manager.create_plan('p1', 'desc-1')

        # Verify
        self.assertTrue(created is not None)
        self.assertTrue(isinstance(created, DeploymentPlan))
        self.assertTrue(created.uuid is not None)
        self.assertEqual('p1', created.name)
        self.assertEqual('desc-1', created.description)
        self.assertEqual(0, len(created.roles))
        self.assertEqual(0, len(created.parameters))

        found = self.plans_manager.retrieve_plan(created.uuid)
        self.assertTrue(found is not None)

    def test_delete_plan(self):
        # Setup
        created = self.plans_manager.create_plan('p1', 'desc-1')
        db_plan = self.plan_store.retrieve(created.uuid)

        # Test
        self.plans_manager.delete_plan(created.uuid)

        # Verify
        self.assertRaises(UnknownUUID,
                          self.plans_manager.retrieve_plan,
                          created.uuid)

        env_store = EnvironmentFileStore()
        self.assertRaises(UnknownUUID, env_store.retrieve,
                          db_plan.environment_file.uuid)

        master_store = MasterTemplateStore()
        self.assertRaises(UnknownUUID, master_store.retrieve,
                          db_plan.master_template.uuid)

    def test_add_role_to_plan(self):
        # Setup
        test_role = self._add_test_role()
        test_plan = self.plans_manager.create_plan('p1', 'd1')

        # Test
        self.plans_manager.add_role_to_plan(test_plan.uuid, test_role.uuid)

        # Verify
        db_plan = self.plan_store.retrieve(test_plan.uuid)
        parsed_plan = parser.parse_template(db_plan.master_template.contents)
        self.assertEqual(1, len(parsed_plan.resources))

    def test_add_role_to_seeded_plan(self):
        # Setup
        self.seed_store.create(MASTER_SEED_NAME, TEST_SEED)
        self.registry_store.create(RESOURCE_REGISTRY_NAME, RESOURCE_REGISTRY)
        # more setup (this is normally called in load_roles)
        self.registry_mapping_store.create('required_file.yaml',
                                           'some fake template data')
        test_role = self._add_test_role()
        test_plan = self.plans_manager.create_plan('p1', 'd1')

        # Test
        self.plans_manager.add_role_to_plan(test_plan.uuid, test_role.uuid)

        # Verify
        db_plan = self.plan_store.retrieve(test_plan.uuid)
        parsed_plan = parser.parse_template(db_plan.master_template.contents)
        self.assertEqual(2, len(parsed_plan.resources))

        # The role generated in the plan has a different name:
        my_role = parsed_plan.find_resource_by_id('r1')
        self.assertIsNot(my_role, None)

        # The reference to the role in some_config should be updated:
        some_config = parsed_plan.find_resource_by_id('some_config')
        self.assertIsNot(some_config, None)
        config_property = some_config.find_property_by_name('config')
        self.assertIsNot(config_property, None)
        self.assertEqual(config_property.value,
                         {'ip_addresses':
                          {'get_attr': ['r1', 'foo_ip']}})

        # verify both entries are present from RESOURCE_REGISTRY
        parsed_env = parser.parse_environment(
            db_plan.environment_file.contents
        )
        self.assertEqual(2, len(parsed_env.registry_entries))

    def test_add_unknown_role_to_seeded_plan(self):
        # Setup
        self.seed_store.create(MASTER_SEED_NAME, TEST_SEED)
        self.registry_store.create(RESOURCE_REGISTRY_NAME, RESOURCE_REGISTRY)
        test_role = self.template_store.create('unknown_role', TEST_TEMPLATE)
        test_plan = self.plans_manager.create_plan('p1', 'd1')

        # Test
        self.assertRaises(ValueError, self.plans_manager.add_role_to_plan,
                          test_plan.uuid, test_role.uuid)

    def test_add_role_of_unknown_type_to_seeded_plan(self):
        # Setup
        self.seed_store.create(MASTER_SEED_NAME, TEST_SEED)
        self.registry_store.create(RESOURCE_REGISTRY_NAME,
                                   RESOURCE_REGISTRY_WRONG_TYPE)
        test_role = self._add_test_role()
        test_plan = self.plans_manager.create_plan('p1', 'd1')

        # Test
        self.assertRaises(ValueError, self.plans_manager.add_role_to_plan,
                          test_plan.uuid, test_role.uuid)

    def test_add_role_to_seeded_plan_without_registry(self):
        # Setup
        self.seed_store.create(MASTER_SEED_NAME, TEST_SEED)
        test_role = self._add_test_role()
        test_plan = self.plans_manager.create_plan('p1', 'd1')

        # Resource registry is missing, adding role should fail
        self.assertRaises(UnknownName,
                          self.plans_manager.add_role_to_plan,
                          test_plan.uuid, test_role.uuid)

    def test_remove_role_from_plan(self):
        # Setup
        test_role = self._add_test_role()
        test_plan = self.plans_manager.create_plan('p1', 'd1')
        self.plans_manager.add_role_to_plan(test_plan.uuid, test_role.uuid)

        # Test
        self.plans_manager.remove_role_from_plan(test_plan.uuid,
                                                 test_role.uuid)

        # Verify
        db_plan = self.plan_store.retrieve(test_plan.uuid)
        parsed_plan = parser.parse_template(db_plan.master_template.contents)
        self.assertEqual(0, len(parsed_plan.resources))

    def test_retrieve_plan(self):
        # Setup
        test_role = self._add_test_role()
        test_plan = self.plans_manager.create_plan('p1', 'd1')
        self.plans_manager.add_role_to_plan(test_plan.uuid, test_role.uuid)

        # Test
        found = self.plans_manager.retrieve_plan(test_plan.uuid)

        # Verify
        self.assertTrue(found is not None)
        self.assertTrue(isinstance(found, DeploymentPlan))
        self.assertEqual(test_plan.uuid, found.uuid)
        self.assertEqual('p1', found.name)
        self.assertEqual('d1', found.description)
        self.assertEqual(1, len(found.roles))
        self.assertTrue(isinstance(found.roles[0], Role))
        self.assertEqual(4, len(found.parameters))  # 3 + 1 for scaling
        self.assertTrue(isinstance(found.parameters[0], PlanParameter))

    def test_list_plans(self):
        # Setup
        self.plans_manager.create_plan('p1', 'd1')
        self.plans_manager.create_plan('p2', 'd2')

        # Test
        all_plans = self.plans_manager.list_plans()

        # Verify
        self.assertEqual(2, len(all_plans))
        all_plans.sort(key=lambda x: x.name)
        self.assertEqual('p1', all_plans[0].name)
        self.assertEqual('p2', all_plans[1].name)

    def test_set_parameter_values(self):
        # Setup
        test_role = self._add_test_role()
        test_plan = self.plans_manager.create_plan('p1', 'd1')
        self.plans_manager.add_role_to_plan(test_plan.uuid, test_role.uuid)

        # Test
        ns = name_utils.generate_role_namespace(test_role.name,
                                                test_role.version)
        update_us = [
            ParameterValue(ns_utils.apply_template_namespace(ns, 'key_name'),
                           'test-key'),
            ParameterValue(ns_utils.apply_template_namespace(ns, 'image_id'),
                           'test-image'),
        ]
        updated_plan = self.plans_manager.set_parameter_values(test_plan.uuid,
                                                               update_us)

        # Verify
        self.assertTrue(updated_plan is not None)
        self.assertTrue(isinstance(updated_plan, DeploymentPlan))

        # Pull it from the database again to make sure it was saved
        found = self.plans_manager.retrieve_plan(test_plan.uuid)
        found_params = sorted(found.parameters, key=lambda x: x.name)
        self.assertEqual(4, len(found_params))  # 3 + 1 for scaling
        self.assertEqual(found_params[0].value, '1')
        self.assertEqual(found_params[1].value, 'test-image')
        self.assertEqual(found_params[2].value, 'm1.small')
        self.assertEqual(found_params[3].value, 'test-key')

    def test_set_non_existent_parameters(self):
        # Setup
        test_role = self._add_test_role()
        test_plan = self.plans_manager.create_plan('p1', 'd1')
        self.plans_manager.add_role_to_plan(test_plan.uuid, test_role.uuid)

        # Test
        ns = name_utils.generate_role_namespace(test_role.name,
                                                test_role.version)
        not_present_in_role_1_name = ns_utils.apply_template_namespace(
            ns, 'not_present_in_role_1')
        not_present_in_role_2_name = ns_utils.apply_template_namespace(
            ns, 'not_present_in_role_2')
        update_us = [
            ParameterValue(ns_utils.apply_template_namespace(ns, 'key_name'),
                           'test-key'),
            ParameterValue(ns_utils.apply_template_namespace(ns, 'image_id'),
                           'test-image'),
            ParameterValue(not_present_in_role_1_name,
                           'not-present-in-role-1-value'),
            ParameterValue(not_present_in_role_2_name,
                           'not-present-in-role-2-value'),
        ]

        # Verify
        exc = self.assertRaises(exception.PlanParametersNotExist,
                                self.plans_manager.set_parameter_values,
                                test_plan.uuid,
                                update_us)
        self.assertIn(not_present_in_role_1_name, str(exc))
        self.assertIn(not_present_in_role_2_name, str(exc))

        # Pull it from the database to make sure it was modified
        found = self.plans_manager.retrieve_plan(test_plan.uuid)
        found_params = sorted(found.parameters, key=lambda x: x.name)
        self.assertEqual(4, len(found_params))  # 3 + 1 for scaling
        self.assertEqual(found_params[0].value, '1')
        self.assertEqual(found_params[1].value,
                         '3e6270da-fbf7-4aef-bc78-6d0cfc3ad11b')
        self.assertEqual(found_params[2].value, 'm1.small')
        self.assertEqual(found_params[3].value, '')

    def test_package_templates(self):
        # Setup
        test_role = self._add_test_role()
        test_plan = self.plans_manager.create_plan('p1', 'd1')
        self.plans_manager.add_role_to_plan(test_plan.uuid, test_role.uuid)

        # Test
        templates = self.plans_manager.package_templates(test_plan.uuid)

        # Verify
        self.assertTrue(isinstance(templates, dict))
        self.assertEqual(3, len(templates))

        self.assertTrue('plan.yaml' in templates)
        parsed_plan = parser.parse_template(templates['plan.yaml'])
        self.assertEqual(parsed_plan.description, 'd1')

        self.assertTrue('environment.yaml' in templates)
        parsed_env = parser.parse_environment(templates['environment.yaml'])
        self.assertEqual(1, len(parsed_env.registry_entries))

        role_filename = name_utils.role_template_filename('r1', '1', None)
        self.assertTrue(role_filename in templates)
        parsed_role = parser.parse_template(templates[role_filename])
        self.assertEqual(parsed_role.description, 'Test provider resource foo')

    def test_package_templates_seeded_plan(self):
        # Setup
        self.seed_store.create(MASTER_SEED_NAME, TEST_SEED)
        self.registry_store.create(RESOURCE_REGISTRY_NAME, RESOURCE_REGISTRY)
        # more setup (this is normally called in load_roles)
        self.registry_mapping_store.create('required_file.yaml',
                                           'some fake template data')

        test_role = self._add_test_role()
        test_plan = self.plans_manager.create_plan('p1', 'd1')
        self.plans_manager.add_role_to_plan(test_plan.uuid, test_role.uuid)

        # Test
        templates = self.plans_manager.package_templates(test_plan.uuid)

        # Verify
        self.assertTrue(isinstance(templates, dict))
        self.assertEqual(4, len(templates))

        self.assertTrue('plan.yaml' in templates)
        parsed_plan = parser.parse_template(templates['plan.yaml'])
        self.assertEqual(parsed_plan.description, 'd1')

        self.assertTrue('environment.yaml' in templates)
        self.assertTrue('required_file.yaml' in templates)
        parsed_env = parser.parse_environment(templates['environment.yaml'])
        self.assertEqual(2, len(parsed_env.registry_entries))

        role_filename = name_utils.role_template_filename('r1', '1', None)
        self.assertTrue(role_filename in templates)
        parsed_role = parser.parse_template(templates[role_filename])
        self.assertEqual(parsed_role.description, 'Test provider resource foo')

    def test_find_roles(self):
        # Setup
        self.seed_store.create(MASTER_SEED_NAME, TEST_SEED)
        self.registry_store.create(RESOURCE_REGISTRY_NAME, RESOURCE_REGISTRY)
        # more setup (this is normally called in load_roles)
        self.registry_mapping_store.create('required_file.yaml',
                                           'some fake template data')
        test_role = self._add_test_role()
        test_plan = self.plans_manager.create_plan('p1', 'd1')

        # Test
        self.plans_manager.add_role_to_plan(test_plan.uuid, test_role.uuid)

        # Verify only one role is found
        db_plan = self.plan_store.retrieve(test_plan.uuid)
        parsed_env = parser.parse_environment(
            db_plan.environment_file.contents
        )
        roles = self.plans_manager._find_roles(parsed_env)
        self.assertEqual(1, len(roles))

    def _add_test_role(self):
        return self.template_store.create('r1', TEST_TEMPLATE,
                                          registry_path='r1.yaml')
Example #28
0
 def __init__(self):
     super(PlansManager, self).__init__()
     self.plan_store = DeploymentPlanStore()
     self.template_store = TemplateStore()
     self.master_template_store = MasterTemplateStore()
     self.environment_store = EnvironmentFileStore()
Example #29
0
class PlansManager(object):

    def __init__(self):
        super(PlansManager, self).__init__()
        self.plan_store = DeploymentPlanStore()
        self.template_store = TemplateStore()
        self.master_template_store = MasterTemplateStore()
        self.environment_store = EnvironmentFileStore()

    def create_plan(self, name, description):
        """Creates a new plan, persisting it to Tuskar's storage backend.

        :type name: str
        :type description: str
        :return: domain model instance of the created plan
        :rtype: tuskar.manager.models.DeploymentPlan
        :raises tuskar.storage.exceptions.NameAlreadyUsed: if there is a plan
                with the given name
        """

        # Create the plan using the template generation code first to
        # stub out the master template and environment files correctly.
        new_plan = plan.DeploymentPlan(description=description)

        # Save the newly created master template and environment to the
        # storage layer so they can be associated with the plan.
        master_template_contents = composer.compose_template(
            new_plan.master_template
        )
        master_template_file = self.master_template_store.create(
            name_utils.master_template_filename(name),
            master_template_contents,
        )

        environment_contents = composer.compose_environment(
            new_plan.environment
        )
        environment_file = self.environment_store.create(
            environment_contents
        )

        # Create the plan in storage, seeding it with the stored files for
        # the template and environment.
        db_plan = self.plan_store.create(
            name,
            master_template_uuid=master_template_file.uuid,
            environment_uuid=environment_file.uuid,
        )

        # Return the created plan.
        created_plan = self.retrieve_plan(db_plan.uuid)
        return created_plan

    def delete_plan(self, plan_uuid):
        """Deletes an existing plan.

        :type plan_uuid: str
        :raises tuskar.storage.exceptions.UnknownUUID: if there is no plan
                with the given UUID
        """
        self.plan_store.delete(plan_uuid)

    def add_role_to_plan(self, plan_uuid, role_uuid):
        """Adds a role to the given plan, storing the changes in Tuskar's
        storage.

        :type plan_uuid: str
        :type role_uuid: str
        :return: updated plan model instance
        :rtype: tuskar.manager.models.DeploymentPlan
        :raises tuskar.storage.exceptions.UnknownUUID: if either the plan
                or the role cannot be found
        """

        # Load the plan and role from storage
        db_plan = self.plan_store.retrieve(plan_uuid)
        db_role = self.template_store.retrieve(role_uuid)

        # Parse the plan and role template into template objects.
        deployment_plan = self._plan_to_template_object(db_plan)
        role_template = self._role_to_template_object(db_role)

        # Use the combination logic to perform the addition.
        role_namespace = name_utils.generate_role_namespace(db_role.name,
                                                            db_role.version)
        template_filename = name_utils.role_template_filename(db_role.name,
                                                              db_role.version)
        deployment_plan.add_template(role_namespace, role_template,
                                     template_filename)

        # Save the updated plan.
        updated = self._save_updated_plan(plan_uuid, deployment_plan)

        return updated

    def remove_role_from_plan(self, plan_uuid, role_uuid):
        """Removes a role from the given plan.

        :type plan_uuid: str
        :type role_uuid: str
        :raise tuskar.storage.exceptions.UnknownUUID: if the plan or role
               doesn't exist
        """

        # Load the objects from storage.
        db_plan = self.plan_store.retrieve(plan_uuid)
        db_role = self.template_store.retrieve(role_uuid)

        # Parse the plan into template objects.
        deployment_plan = self._plan_to_template_object(db_plan)

        # Delete the role from the plan by it's namespace.
        role_namespace = name_utils.generate_role_namespace(db_role.name,
                                                            db_role.version)
        deployment_plan.remove_template(role_namespace)

        # Save the updated plan.
        updated = self._save_updated_plan(plan_uuid, deployment_plan)

        return updated

    def retrieve_plan(self, plan_uuid):
        """Loads the given plan.

        :type plan_uuid: str
        :rtype: tuskar.manager.models.DeploymentPlan
        :raises tuskar.storage.exceptions.UnknownUUID: if there is no plan
                with the given UUID
        """

        # Load the plan from the database.
        db_plan = self.plan_store.retrieve(plan_uuid)

        # Parse the plan into the template model.
        master_template = parser.parse_template(
            db_plan.master_template.contents
        )
        environment = parser.parse_environment(
            db_plan.environment_file.contents
        )

        # Create the Tuskar model for the plan.
        deployment_plan = models.DeploymentPlan(
            plan_uuid,
            db_plan.name,
            master_template.description,
            created_at=db_plan.created_at,
            updated_at=db_plan.updated_at,
        )

        roles = self._find_roles(environment)
        deployment_plan.add_roles(*roles)

        params = self._find_parameters(master_template, environment)
        deployment_plan.add_parameters(*params)

        return deployment_plan

    def list_plans(self):
        """Returns a list of all plans stored in Tuskar.

        :return: list of plan instances; empty list if there are no plans
        :rtype: [tuskar.manager.models.DeploymentPlan]
        """

        # Given the expected number of plans being managed by Tuskar (in the
        # tens), this should be sufficient. If our scale gets larger, we may
        # need a smarter batch operation here than simply iterating over the
        # list of all plans. jdob, Aug 7, 2014

        plan_uuids = [p.uuid for p in self.plan_store.list()]
        plans = [self.retrieve_plan(p) for p in plan_uuids]

        return plans

    def set_parameter_values(self, plan_uuid, params):
        """Sets the values for a plan's parameters.

        :type plan_uuid: str
        :type params: [tuskar.manager.models.ParameterValue]

        :return: plan instance with the updated values
        :rtype:  tuskar.manager.models.DeploymentPlan
        """

        # Load the plan from the database.
        db_plan = self.plan_store.retrieve(plan_uuid)

        # Save the values to the parsed environment.
        environment = parser.parse_environment(
            db_plan.environment_file.contents
        )

        for param_value in params:
            p = environment.find_parameter_by_name(param_value.name)
            p.value = param_value.value

        # Save the updated environment.
        env_contents = composer.compose_environment(environment)
        self.plan_store.update_environment(plan_uuid, env_contents)

        updated_plan = self.retrieve_plan(plan_uuid)
        return updated_plan

    def package_templates(self, plan_uuid):
        """Packages and returns all of the templates related to the given plan.
        The returned dictionary is keyed by filename and contains the contents
        of that file (a template or an environment file).

        :type plan_uuid: str

        :return: mapping of filename to contents for each file in the plan
        :rtype:  dict

        :raises tuskar.storage.exceptions.UnknownUUID: if there is no plan
                with the given UUID
        """

        # Load and parse the plan.
        db_plan = self.plan_store.retrieve(plan_uuid)
        master_template = parser.parse_template(
            db_plan.master_template.contents
        )
        environment = parser.parse_environment(
            db_plan.environment_file.contents
        )

        # Compose the plan files and all plan roles and package them into
        # a single dictionary.
        plan_contents = composer.compose_template(master_template)
        env_contents = composer.compose_environment(environment)

        files_dict = {
            'plan.yaml': plan_contents,
            'environment.yaml': env_contents,
        }

        plan_roles = self._find_roles(environment)

        for role in plan_roles:
            contents = composer.compose_template(role.template)
            filename = name_utils.role_template_filename(role.name,
                                                         role.version)
            files_dict[filename] = contents

        return files_dict

    def _find_roles(self, environment):
        """Returns a list of roles for a plan (identified by the given
        environment).

        :type environment: tuskar.templates.heat.Environment
        :return: list of role instances; empty list if none are in the
                 environment
        :rtype: [tuskar.manager.models.Role]
        """

        def load_role(entry):
            # Figure out the role name/version for the given entry.
            namespace = ns_utils.remove_resource_alias_namespace(entry.alias)
            name, version = name_utils.parse_role_namespace(namespace)

            # Load the role from the database and parse into the
            # template objects.
            db_role = self.template_store.retrieve_by_name(name, int(version))
            role = self._role_to_template_object(db_role)

            # Convert to the Tuskar domain model.
            tuskar_role = models.Role(db_role.uuid, name, version,
                                      role.description, role)
            return tuskar_role

        roles = [load_role(e) for e in environment.registry_entries]
        return roles

    @staticmethod
    def _find_parameters(template, environment):
        """Returns a list of parameters for a plan. The parameters will contain
        both metadata about the parameter itself (name, label, description,
        etc.) as well as it's current value for the plan.

        :type template: tuskar.templates.heat.Template
        :type environment: tuskar.templates.heat.Environment
        :return: list of parameter instances; empty list if there are no
                 parameters in the plan
        :rtype: [tuskar.manager.models.PlanParameter]
        """

        def generate_param(p):
            env_param = environment.find_parameter_by_name(p.name)
            return models.PlanParameter(
                p.name, env_param.value, p.param_type,
                p.description, p.label, p.default, p.hidden
            )

        params = [generate_param(tp) for tp in template.parameters]
        return params

    @staticmethod
    def _plan_to_template_object(db_plan):
        master_template = parser.parse_template(
            db_plan.master_template.contents
        )
        environment = parser.parse_environment(
            db_plan.environment_file.contents
        )
        deployment_plan = plan.DeploymentPlan(master_template=master_template,
                                              environment=environment)
        return deployment_plan

    @staticmethod
    def _role_to_template_object(db_role):
        role_template = parser.parse_template(db_role.contents)
        return role_template

    def _save_updated_plan(self, plan_uuid, deployment_plan):
        new_template_contents = composer.compose_template(
            deployment_plan.master_template
        )
        new_env_contents = composer.compose_environment(
            deployment_plan.environment
        )

        self.plan_store.update_master_template(plan_uuid,
                                               new_template_contents)
        self.plan_store.update_environment(plan_uuid,
                                           new_env_contents)

        # Retrieve and return the updated plan
        updated_plan = self.retrieve_plan(plan_uuid)
        return updated_plan
Example #30
0
    def setUp(self):
        super(SQLAlchemyDriverTestCase, self).setUp()

        self.driver = SQLAlchemyDriver()
        self.store = TemplateStore(self.driver)
Example #31
0
class PlansManager(object):

    def __init__(self):
        super(PlansManager, self).__init__()
        self.plan_store = DeploymentPlanStore()
        self.seed_store = MasterSeedStore()
        self.registry_store = ResourceRegistryStore()
        self.template_store = TemplateStore()
        self.master_template_store = MasterTemplateStore()
        self.environment_store = EnvironmentFileStore()

    def create_plan(self, name, description):
        """Creates a new plan, persisting it to Tuskar's storage backend.

        :type name: str
        :type description: str
        :return: domain model instance of the created plan
        :rtype: tuskar.manager.models.DeploymentPlan
        :raises tuskar.storage.exceptions.NameAlreadyUsed: if there is a plan
                with the given name
        """

        # Create the plan using the template generation code first to
        # stub out the master template and environment files correctly.
        new_plan = plan.DeploymentPlan(description=description)

        # Save the newly created master template and environment to the
        # storage layer so they can be associated with the plan.
        master_template_contents = composer.compose_template(
            new_plan.master_template
        )
        master_template_file = self.master_template_store.create(
            name_utils.master_template_filename(name),
            master_template_contents,
        )

        environment_contents = composer.compose_environment(
            new_plan.environment
        )
        environment_file = self.environment_store.create(
            environment_contents
        )

        # Create the plan in storage, seeding it with the stored files for
        # the template and environment.
        db_plan = self.plan_store.create(
            name,
            master_template_uuid=master_template_file.uuid,
            environment_uuid=environment_file.uuid,
        )

        # Return the created plan.
        created_plan = self.retrieve_plan(db_plan.uuid)
        return created_plan

    def delete_plan(self, plan_uuid):
        """Deletes an existing plan.

        :type plan_uuid: str
        :raises tuskar.storage.exceptions.UnknownUUID: if there is no plan
                with the given UUID
        """
        self.plan_store.delete(plan_uuid)

    def add_role_to_plan(self, plan_uuid, role_uuid):
        """Adds a role to the given plan, storing the changes in Tuskar's
        storage.

        :type plan_uuid: str
        :type role_uuid: str
        :return: updated plan model instance
        :rtype: tuskar.manager.models.DeploymentPlan
        :raises tuskar.storage.exceptions.UnknownUUID: if either the plan
                or the role cannot be found
        """

        # Load the plan and role from storage
        db_plan = self.plan_store.retrieve(plan_uuid)
        db_role = self.template_store.retrieve(role_uuid)

        # Parse the plan and role template into template objects.
        deployment_plan = self._plan_to_template_object(db_plan)
        role_template = self._role_to_template_object(db_role)

        # See if a master seed template has been set.
        try:
            db_master_seed = self.seed_store.retrieve_by_name(MASTER_SEED_NAME)
            master_seed = parser.parse_template(db_master_seed.contents)
        except UnknownName:
            master_seed = None
            special_properties = None

        if master_seed is not None:
            try:
                db_registry_env = self.registry_store.retrieve_by_name(
                    RESOURCE_REGISTRY_NAME).contents
            except UnknownName:
                LOG.error("Could not load resource_registry. Make sure you "
                          "pass --resource-registry to tuskar-load-roles.")
                raise
            parsed_registry_env = parser.parse_environment(db_registry_env)
            registry = dict((role_name_from_path(e.filename), e.alias)
                            for e in parsed_registry_env.registry_entries)
            special_properties = template_seed.get_property_map_for_role(
                master_seed, registry[db_role.name])

        # Use the combination logic to perform the addition.
        role_namespace = name_utils.generate_role_namespace(db_role.name,
                                                            db_role.version)
        template_filename = name_utils.role_template_filename(db_role.name,
                                                              db_role.version)
        deployment_plan.add_template(role_namespace, role_template,
                                     template_filename,
                                     override_properties=special_properties)

        # If there is a master seed, add its top-level elements to the plan.
        # These calls are idempotent, so it's safe to call each time a role
        # is added.
        if master_seed is not None:
            template_seed.add_top_level_parameters(
                master_seed,
                deployment_plan.master_template,
                deployment_plan.environment)
            template_seed.add_top_level_resources(
                master_seed, deployment_plan.master_template)
            template_seed.add_top_level_outputs(
                master_seed, deployment_plan.master_template)

            try:
                role_type = registry[db_role.name]
            except KeyError:
                LOG.error(
                    "Role '%s' not found in seed template." % db_role.name)
                raise
            seed_role = template_seed.find_role_from_type(
                master_seed.resources, role_type)
            if seed_role is None:
                LOG.error(
                    "Role '%s' of type '%s' not found in seed template." %
                    (db_role.name, role_type))
                raise ValueError(db_role.name)

            # These calls are idempotent, but must be called on each role as
            # new references may have been added.
            template_seed.update_role_resource_references(
                deployment_plan.master_template,
                seed_role,
                plan.generate_group_id(role_namespace))

            template_seed.update_role_property_references(
                deployment_plan.master_template,
                seed_role,
                role_namespace)

        # Save the updated plan.
        updated = self._save_updated_plan(plan_uuid, deployment_plan)

        return updated

    def remove_role_from_plan(self, plan_uuid, role_uuid):
        """Removes a role from the given plan.

        :type plan_uuid: str
        :type role_uuid: str
        :raise tuskar.storage.exceptions.UnknownUUID: if the plan or role
               doesn't exist
        """

        # Load the objects from storage.
        db_plan = self.plan_store.retrieve(plan_uuid)
        db_role = self.template_store.retrieve(role_uuid)

        # Parse the plan into template objects.
        deployment_plan = self._plan_to_template_object(db_plan)

        # Delete the role from the plan by it's namespace.
        role_namespace = name_utils.generate_role_namespace(db_role.name,
                                                            db_role.version)
        deployment_plan.remove_template(role_namespace)

        # Save the updated plan.
        updated = self._save_updated_plan(plan_uuid, deployment_plan)

        return updated

    def retrieve_plan(self, plan_uuid):
        """Loads the given plan.

        :type plan_uuid: str
        :rtype: tuskar.manager.models.DeploymentPlan
        :raises tuskar.storage.exceptions.UnknownUUID: if there is no plan
                with the given UUID
        """

        # Load the plan from the database.
        db_plan = self.plan_store.retrieve(plan_uuid)

        # Parse the plan into the template model.
        master_template = parser.parse_template(
            db_plan.master_template.contents
        )
        environment = parser.parse_environment(
            db_plan.environment_file.contents
        )

        # Create the Tuskar model for the plan.
        deployment_plan = models.DeploymentPlan(
            plan_uuid,
            db_plan.name,
            master_template.description,
            created_at=db_plan.created_at,
            updated_at=db_plan.updated_at,
        )

        roles = self._find_roles(environment)
        deployment_plan.add_roles(*roles)

        params = self._find_parameters(master_template, environment)
        deployment_plan.add_parameters(*params)

        return deployment_plan

    def list_plans(self):
        """Returns a list of all plans stored in Tuskar.

        :return: list of plan instances; empty list if there are no plans
        :rtype: [tuskar.manager.models.DeploymentPlan]
        """

        # Given the expected number of plans being managed by Tuskar (in the
        # tens), this should be sufficient. If our scale gets larger, we may
        # need a smarter batch operation here than simply iterating over the
        # list of all plans. jdob, Aug 7, 2014

        plan_uuids = [p.uuid for p in self.plan_store.list()]
        plans = [self.retrieve_plan(p) for p in plan_uuids]

        return plans

    def set_parameter_values(self, plan_uuid, params):
        """Sets the values for a plan's parameters.

        :type plan_uuid: str
        :type params: [tuskar.manager.models.ParameterValue]

        :return: plan instance with the updated values
        :rtype:  tuskar.manager.models.DeploymentPlan
        """

        # Load the plan from the database.
        db_plan = self.plan_store.retrieve(plan_uuid)

        # Save the values to the parsed environment.
        environment = parser.parse_environment(
            db_plan.environment_file.contents
        )

        non_existent_params = []
        for param_value in params:
            p = environment.find_parameter_by_name(param_value.name)
            if p:
                p.value = param_value.value
            else:
                non_existent_params.append(param_value.name)

        if non_existent_params:
            param_names = ', '.join(non_existent_params)
            LOG.error(
                'There are no parameters named %(param_names)s'
                ' in plan %(plan_uuid)s.' %
                {'param_names': param_names, 'plan_uuid': plan_uuid})
            raise exception.PlanParametersNotExist(
                plan_uuid=plan_uuid,
                param_names=param_names
            )

        # Save the updated environment.
        env_contents = composer.compose_environment(environment)
        self.plan_store.update_environment(plan_uuid, env_contents)

        updated_plan = self.retrieve_plan(plan_uuid)
        return updated_plan

    def package_templates(self, plan_uuid):
        """Packages and returns all of the templates related to the given plan.
        The returned dictionary is keyed by filename and contains the contents
        of that file (a template or an environment file).

        :type plan_uuid: str

        :return: mapping of filename to contents for each file in the plan
        :rtype:  dict

        :raises tuskar.storage.exceptions.UnknownUUID: if there is no plan
                with the given UUID
        """

        # Load and parse the plan.
        db_plan = self.plan_store.retrieve(plan_uuid)
        master_template = parser.parse_template(
            db_plan.master_template.contents
        )
        environment = parser.parse_environment(
            db_plan.environment_file.contents
        )

        # Compose the plan files and all plan roles and package them into
        # a single dictionary.
        plan_contents = composer.compose_template(master_template)
        env_contents = composer.compose_environment(environment)

        files_dict = {
            'plan.yaml': plan_contents,
            'environment.yaml': env_contents,
        }

        plan_roles = self._find_roles(environment)

        for role in plan_roles:
            contents = composer.compose_template(role.template)
            filename = name_utils.role_template_filename(role.name,
                                                         role.version)
            files_dict[filename] = contents

        return files_dict

    def _find_roles(self, environment):
        """Returns a list of roles for a plan (identified by the given
        environment).

        :type environment: tuskar.templates.heat.Environment
        :return: list of role instances; empty list if none are in the
                 environment
        :rtype: [tuskar.manager.models.Role]
        """

        def load_role(entry):
            # Figure out the role name/version for the given entry.
            namespace = ns_utils.remove_resource_alias_namespace(entry.alias)
            name, version = name_utils.parse_role_namespace(namespace)

            # Load the role from the database and parse into the
            # template objects.
            db_role = self.template_store.retrieve_by_name(name, int(version))
            role = self._role_to_template_object(db_role)

            # Convert to the Tuskar domain model.
            tuskar_role = models.Role(db_role.uuid, name, version,
                                      role.description, role)
            return tuskar_role

        roles = [load_role(e) for e in environment.registry_entries]
        return roles

    @staticmethod
    def _find_parameters(template, environment):
        """Returns a list of parameters for a plan. The parameters will contain
        both metadata about the parameter itself (name, label, description,
        etc.) as well as it's current value for the plan.

        :type template: tuskar.templates.heat.Template
        :type environment: tuskar.templates.heat.Environment
        :return: list of parameter instances; empty list if there are no
                 parameters in the plan
        :rtype: [tuskar.manager.models.PlanParameter]
        """

        def generate_param(p):
            env_param = environment.find_parameter_by_name(p.name)
            return models.PlanParameter(
                p.name, env_param.value, p.param_type,
                p.description, p.label, p.default, p.hidden
            )

        params = [generate_param(tp) for tp in template.parameters]
        return params

    @staticmethod
    def _plan_to_template_object(db_plan):
        master_template = parser.parse_template(
            db_plan.master_template.contents
        )
        environment = parser.parse_environment(
            db_plan.environment_file.contents
        )
        deployment_plan = plan.DeploymentPlan(master_template=master_template,
                                              environment=environment)
        return deployment_plan

    @staticmethod
    def _role_to_template_object(db_role):
        role_template = parser.parse_template(db_role.contents)
        return role_template

    def _save_updated_plan(self, plan_uuid, deployment_plan):
        new_template_contents = composer.compose_template(
            deployment_plan.master_template
        )
        new_env_contents = composer.compose_environment(
            deployment_plan.environment
        )

        self.plan_store.update_master_template(plan_uuid,
                                               new_template_contents)
        self.plan_store.update_environment(plan_uuid,
                                           new_env_contents)

        # Retrieve and return the updated plan
        updated_plan = self.retrieve_plan(plan_uuid)
        return updated_plan
Example #32
0
 def __init__(self):
     super(RoleManager, self).__init__()
     self.template_store = TemplateStore()
Example #33
0
class PlansManagerTestCase(TestCase):
    def setUp(self):
        super(PlansManagerTestCase, self).setUp()
        self.plans_manager = PlansManager()

        self.plan_store = DeploymentPlanStore()
        self.template_store = TemplateStore()
        self.seed_store = MasterSeedStore()
        self.registry_store = ResourceRegistryStore()
        self.registry_mapping_store = ResourceRegistryMappingStore()

    def test_create_plan(self):
        # Tests
        created = self.plans_manager.create_plan('p1', 'desc-1')

        # Verify
        self.assertTrue(created is not None)
        self.assertTrue(isinstance(created, DeploymentPlan))
        self.assertTrue(created.uuid is not None)
        self.assertEqual('p1', created.name)
        self.assertEqual('desc-1', created.description)
        self.assertEqual(0, len(created.roles))
        self.assertEqual(0, len(created.parameters))

        found = self.plans_manager.retrieve_plan(created.uuid)
        self.assertTrue(found is not None)

    def test_delete_plan(self):
        # Setup
        created = self.plans_manager.create_plan('p1', 'desc-1')
        db_plan = self.plan_store.retrieve(created.uuid)

        # Test
        self.plans_manager.delete_plan(created.uuid)

        # Verify
        self.assertRaises(UnknownUUID, self.plans_manager.retrieve_plan,
                          created.uuid)

        env_store = EnvironmentFileStore()
        self.assertRaises(UnknownUUID, env_store.retrieve,
                          db_plan.environment_file.uuid)

        master_store = MasterTemplateStore()
        self.assertRaises(UnknownUUID, master_store.retrieve,
                          db_plan.master_template.uuid)

    def test_add_role_to_plan(self):
        # Setup
        test_role = self._add_test_role()
        test_plan = self.plans_manager.create_plan('p1', 'd1')

        # Test
        self.plans_manager.add_role_to_plan(test_plan.uuid, test_role.uuid)

        # Verify
        db_plan = self.plan_store.retrieve(test_plan.uuid)
        parsed_plan = parser.parse_template(db_plan.master_template.contents)
        self.assertEqual(1, len(parsed_plan.resources))

    def test_add_role_to_seeded_plan(self):
        # Setup
        self.seed_store.create(MASTER_SEED_NAME, TEST_SEED)
        self.registry_store.create(RESOURCE_REGISTRY_NAME, RESOURCE_REGISTRY)
        # more setup (this is normally called in load_roles)
        self.registry_mapping_store.create('required_file.yaml',
                                           'some fake template data')
        test_role = self._add_test_role()
        test_plan = self.plans_manager.create_plan('p1', 'd1')

        # Test
        self.plans_manager.add_role_to_plan(test_plan.uuid, test_role.uuid)

        # Verify
        db_plan = self.plan_store.retrieve(test_plan.uuid)
        parsed_plan = parser.parse_template(db_plan.master_template.contents)
        self.assertEqual(2, len(parsed_plan.resources))

        # The role generated in the plan has a different name:
        my_role = parsed_plan.find_resource_by_id('r1')
        self.assertIsNot(my_role, None)

        # The reference to the role in some_config should be updated:
        some_config = parsed_plan.find_resource_by_id('some_config')
        self.assertIsNot(some_config, None)
        config_property = some_config.find_property_by_name('config')
        self.assertIsNot(config_property, None)
        self.assertEqual(config_property.value,
                         {'ip_addresses': {
                             'get_attr': ['r1', 'foo_ip']
                         }})

        # verify both entries are present from RESOURCE_REGISTRY
        parsed_env = parser.parse_environment(
            db_plan.environment_file.contents)
        self.assertEqual(2, len(parsed_env.registry_entries))

    def test_add_unknown_role_to_seeded_plan(self):
        # Setup
        self.seed_store.create(MASTER_SEED_NAME, TEST_SEED)
        self.registry_store.create(RESOURCE_REGISTRY_NAME, RESOURCE_REGISTRY)
        test_role = self.template_store.create('unknown_role', TEST_TEMPLATE)
        test_plan = self.plans_manager.create_plan('p1', 'd1')

        # Test
        self.assertRaises(ValueError, self.plans_manager.add_role_to_plan,
                          test_plan.uuid, test_role.uuid)

    def test_add_role_of_unknown_type_to_seeded_plan(self):
        # Setup
        self.seed_store.create(MASTER_SEED_NAME, TEST_SEED)
        self.registry_store.create(RESOURCE_REGISTRY_NAME,
                                   RESOURCE_REGISTRY_WRONG_TYPE)
        test_role = self._add_test_role()
        test_plan = self.plans_manager.create_plan('p1', 'd1')

        # Test
        self.assertRaises(ValueError, self.plans_manager.add_role_to_plan,
                          test_plan.uuid, test_role.uuid)

    def test_add_role_to_seeded_plan_without_registry(self):
        # Setup
        self.seed_store.create(MASTER_SEED_NAME, TEST_SEED)
        test_role = self._add_test_role()
        test_plan = self.plans_manager.create_plan('p1', 'd1')

        # Resource registry is missing, adding role should fail
        self.assertRaises(UnknownName, self.plans_manager.add_role_to_plan,
                          test_plan.uuid, test_role.uuid)

    def test_remove_role_from_plan(self):
        # Setup
        test_role = self._add_test_role()
        test_plan = self.plans_manager.create_plan('p1', 'd1')
        self.plans_manager.add_role_to_plan(test_plan.uuid, test_role.uuid)

        # Test
        self.plans_manager.remove_role_from_plan(test_plan.uuid,
                                                 test_role.uuid)

        # Verify
        db_plan = self.plan_store.retrieve(test_plan.uuid)
        parsed_plan = parser.parse_template(db_plan.master_template.contents)
        self.assertEqual(0, len(parsed_plan.resources))

    def test_retrieve_plan(self):
        # Setup
        test_role = self._add_test_role()
        test_plan = self.plans_manager.create_plan('p1', 'd1')
        self.plans_manager.add_role_to_plan(test_plan.uuid, test_role.uuid)

        # Test
        found = self.plans_manager.retrieve_plan(test_plan.uuid)

        # Verify
        self.assertTrue(found is not None)
        self.assertTrue(isinstance(found, DeploymentPlan))
        self.assertEqual(test_plan.uuid, found.uuid)
        self.assertEqual('p1', found.name)
        self.assertEqual('d1', found.description)
        self.assertEqual(1, len(found.roles))
        self.assertTrue(isinstance(found.roles[0], Role))
        self.assertEqual(4, len(found.parameters))  # 3 + 1 for scaling
        self.assertTrue(isinstance(found.parameters[0], PlanParameter))

    def test_list_plans(self):
        # Setup
        self.plans_manager.create_plan('p1', 'd1')
        self.plans_manager.create_plan('p2', 'd2')

        # Test
        all_plans = self.plans_manager.list_plans()

        # Verify
        self.assertEqual(2, len(all_plans))
        all_plans.sort(key=lambda x: x.name)
        self.assertEqual('p1', all_plans[0].name)
        self.assertEqual('p2', all_plans[1].name)

    def test_set_parameter_values(self):
        # Setup
        test_role = self._add_test_role()
        test_plan = self.plans_manager.create_plan('p1', 'd1')
        self.plans_manager.add_role_to_plan(test_plan.uuid, test_role.uuid)

        # Test
        ns = name_utils.generate_role_namespace(test_role.name,
                                                test_role.version)
        update_us = [
            ParameterValue(ns_utils.apply_template_namespace(ns, 'key_name'),
                           'test-key'),
            ParameterValue(ns_utils.apply_template_namespace(ns, 'image_id'),
                           'test-image'),
        ]
        updated_plan = self.plans_manager.set_parameter_values(
            test_plan.uuid, update_us)

        # Verify
        self.assertTrue(updated_plan is not None)
        self.assertTrue(isinstance(updated_plan, DeploymentPlan))

        # Pull it from the database again to make sure it was saved
        found = self.plans_manager.retrieve_plan(test_plan.uuid)
        found_params = sorted(found.parameters, key=lambda x: x.name)
        self.assertEqual(4, len(found_params))  # 3 + 1 for scaling
        self.assertEqual(found_params[0].value, '1')
        self.assertEqual(found_params[1].value, 'test-image')
        self.assertEqual(found_params[2].value, 'm1.small')
        self.assertEqual(found_params[3].value, 'test-key')

    def test_set_non_existent_parameters(self):
        # Setup
        test_role = self._add_test_role()
        test_plan = self.plans_manager.create_plan('p1', 'd1')
        self.plans_manager.add_role_to_plan(test_plan.uuid, test_role.uuid)

        # Test
        ns = name_utils.generate_role_namespace(test_role.name,
                                                test_role.version)
        not_present_in_role_1_name = ns_utils.apply_template_namespace(
            ns, 'not_present_in_role_1')
        not_present_in_role_2_name = ns_utils.apply_template_namespace(
            ns, 'not_present_in_role_2')
        update_us = [
            ParameterValue(ns_utils.apply_template_namespace(ns, 'key_name'),
                           'test-key'),
            ParameterValue(ns_utils.apply_template_namespace(ns, 'image_id'),
                           'test-image'),
            ParameterValue(not_present_in_role_1_name,
                           'not-present-in-role-1-value'),
            ParameterValue(not_present_in_role_2_name,
                           'not-present-in-role-2-value'),
        ]

        # Verify
        exc = self.assertRaises(exception.PlanParametersNotExist,
                                self.plans_manager.set_parameter_values,
                                test_plan.uuid, update_us)
        self.assertIn(not_present_in_role_1_name, str(exc))
        self.assertIn(not_present_in_role_2_name, str(exc))

        # Pull it from the database to make sure it was modified
        found = self.plans_manager.retrieve_plan(test_plan.uuid)
        found_params = sorted(found.parameters, key=lambda x: x.name)
        self.assertEqual(4, len(found_params))  # 3 + 1 for scaling
        self.assertEqual(found_params[0].value, '1')
        self.assertEqual(found_params[1].value,
                         '3e6270da-fbf7-4aef-bc78-6d0cfc3ad11b')
        self.assertEqual(found_params[2].value, 'm1.small')
        self.assertEqual(found_params[3].value, '')

    def test_package_templates(self):
        # Setup
        test_role = self._add_test_role()
        test_plan = self.plans_manager.create_plan('p1', 'd1')
        self.plans_manager.add_role_to_plan(test_plan.uuid, test_role.uuid)

        # Test
        templates = self.plans_manager.package_templates(test_plan.uuid)

        # Verify
        self.assertTrue(isinstance(templates, dict))
        self.assertEqual(3, len(templates))

        self.assertTrue('plan.yaml' in templates)
        parsed_plan = parser.parse_template(templates['plan.yaml'])
        self.assertEqual(parsed_plan.description, 'd1')

        self.assertTrue('environment.yaml' in templates)
        parsed_env = parser.parse_environment(templates['environment.yaml'])
        self.assertEqual(1, len(parsed_env.registry_entries))

        role_filename = name_utils.role_template_filename('r1', '1', None)
        self.assertTrue(role_filename in templates)
        parsed_role = parser.parse_template(templates[role_filename])
        self.assertEqual(parsed_role.description, 'Test provider resource foo')

    def test_package_templates_seeded_plan(self):
        # Setup
        self.seed_store.create(MASTER_SEED_NAME, TEST_SEED)
        self.registry_store.create(RESOURCE_REGISTRY_NAME, RESOURCE_REGISTRY)
        # more setup (this is normally called in load_roles)
        self.registry_mapping_store.create('required_file.yaml',
                                           'some fake template data')

        test_role = self._add_test_role()
        test_plan = self.plans_manager.create_plan('p1', 'd1')
        self.plans_manager.add_role_to_plan(test_plan.uuid, test_role.uuid)

        # Test
        templates = self.plans_manager.package_templates(test_plan.uuid)

        # Verify
        self.assertTrue(isinstance(templates, dict))
        self.assertEqual(4, len(templates))

        self.assertTrue('plan.yaml' in templates)
        parsed_plan = parser.parse_template(templates['plan.yaml'])
        self.assertEqual(parsed_plan.description, 'd1')

        self.assertTrue('environment.yaml' in templates)
        self.assertTrue('required_file.yaml' in templates)
        parsed_env = parser.parse_environment(templates['environment.yaml'])
        self.assertEqual(2, len(parsed_env.registry_entries))

        role_filename = name_utils.role_template_filename('r1', '1', None)
        self.assertTrue(role_filename in templates)
        parsed_role = parser.parse_template(templates[role_filename])
        self.assertEqual(parsed_role.description, 'Test provider resource foo')

    def test_find_roles(self):
        # Setup
        self.seed_store.create(MASTER_SEED_NAME, TEST_SEED)
        self.registry_store.create(RESOURCE_REGISTRY_NAME, RESOURCE_REGISTRY)
        # more setup (this is normally called in load_roles)
        self.registry_mapping_store.create('required_file.yaml',
                                           'some fake template data')
        test_role = self._add_test_role()
        test_plan = self.plans_manager.create_plan('p1', 'd1')

        # Test
        self.plans_manager.add_role_to_plan(test_plan.uuid, test_role.uuid)

        # Verify only one role is found
        db_plan = self.plan_store.retrieve(test_plan.uuid)
        parsed_env = parser.parse_environment(
            db_plan.environment_file.contents)
        roles = self.plans_manager._find_roles(parsed_env)
        self.assertEqual(1, len(roles))

    def _add_test_role(self):
        return self.template_store.create('r1',
                                          TEST_TEMPLATE,
                                          registry_path='r1.yaml')