Tuskar command to import a set of roles

This change adds a command to import a set of roles into the Tuskar
storage. This will typically be ran during the installation and setup to
provide a set of initial roles.

Change-Id: I3395363c709c89072025083ca3da3189aae9e5d9
This commit is contained in:
Dougal Matthews 2014-08-11 10:32:37 +01:00
parent b43e6af3df
commit 5f1df10369
5 changed files with 301 additions and 0 deletions

View File

@ -26,6 +26,7 @@ packages =
console_scripts = console_scripts =
tuskar-api = tuskar.cmd.api:main tuskar-api = tuskar.cmd.api:main
tuskar-dbsync = tuskar.cmd.dbsync:main tuskar-dbsync = tuskar.cmd.dbsync:main
tuskar-load-roles = tuskar.cmd.load_roles:main
[build_sphinx] [build_sphinx]
all_files = 1 all_files = 1

56
tuskar/cmd/load_roles.py Normal file
View File

@ -0,0 +1,56 @@
#!/usr/bin/env python
#
# Copyright 2013 Hewlett-Packard Development Company, L.P.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import print_function
import sys
from oslo.config import cfg
from tuskar.common import service
from tuskar.storage.load_roles import load_roles
def _print_names(message, names):
print("{0}: \n {1}".format(message, '\n '.join(names)))
cfg.CONF.register_cli_opt(cfg.BoolOpt('dry-run'))
cfg.CONF.register_cli_opt(cfg.StrOpt('directory', positional=True))
def main(argv=None):
if argv is None:
argv = sys.argv
service.prepare_service(argv)
all_roles, created, updated = load_roles(cfg.CONF.directory,
cfg.CONF.dry_run)
if len(created):
_print_names("Created", created)
if len(updated):
_print_names("Updated", updated)
if not cfg.CONF.dry_run:
print("Imported {0} roles".format(len(all_roles)))
else:
_print_names("Found", all_roles)
print("Imported 0 roles")

View File

@ -0,0 +1,104 @@
#!/usr/bin/env python
#
# Copyright 2013 Hewlett-Packard Development Company, L.P.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import print_function
from os import listdir
from os import path
from tuskar.storage.exceptions import UnknownName
from tuskar.storage.stores import TemplateStore
def _list_roles(directory):
"""Scan a directory and yield a tuple for all the roles containing the
role name and the full path to the role.
"""
if not path.isdir(directory):
raise ValueError("The given path is not a valid directory.")
directory = path.abspath(directory)
for filename in listdir(directory):
if not filename.endswith("yaml") and not filename.endswith("yml"):
continue
yield filename, path.join(directory, filename)
def _read_role(role_path):
with open(role_path) as role_file:
return role_file.read()
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)
def load_roles(directory, dry_run=False):
"""Given a directory path, import the YAML role files into the
TemplateStore. When dry_run=True is passed, run through the roles but don't
add any to the store.
The returned tuple contains all the role names and then the names split
over where were created and updated. On a dry run the first item will
contain all of the roles found while the second two will be empty lists as
no files were updated or created.
:param directory: Directory name containing the roles
:type directory: str
:return: Summary of the results as a tuple with the total count and then
the names of the created and updated roles.
:rtype: tuple(list, list, list)
"""
all_roles, created, updated = [], [], []
roles = _list_roles(directory)
for name, role_path in roles:
contents = _read_role(role_path)
all_roles.append(name)
if dry_run:
continue
role_created, _ = _create_or_update(name, contents)
if role_created:
created.append(name)
else:
updated.append(name)
return all_roles, created, updated

View File

@ -0,0 +1,36 @@
# -*- encoding: utf-8 -*-
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from mock import call
from mock import patch
from tuskar.cmd import load_roles
from tuskar.tests.base import TestCase
class LoadRoleTests(TestCase):
@patch('tuskar.storage.load_roles._list_roles',
return_value=[['role_name.yaml', '/path/role_name.yaml']])
@patch('tuskar.storage.load_roles._read_role', return_value="YAML")
@patch('tuskar.cmd.load_roles._print_names')
def test_main(self, mock_print, mock_read, mock_list):
# test
load_roles.main("/path/".split())
# verify
self.assertEqual([
call('Created', ['role_name.yaml'])
], mock_print.call_args_list)

View File

@ -0,0 +1,104 @@
# -*- encoding: utf-8 -*-
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from os import path
from shutil import rmtree
from tempfile import mkdtemp
from tuskar.storage.load_roles import _create_or_update
from tuskar.storage.load_roles import _list_roles
from tuskar.storage.load_roles import load_roles
from tuskar.storage.stores import TemplateStore
from tuskar.tests.base import TestCase
class LoadRoleTests(TestCase):
def setUp(self):
super(LoadRoleTests, self).setUp()
self.directory = mkdtemp()
self.store = TemplateStore()
roles = ['role1.yaml', 'rubbish', 'role2.yml']
for role in roles:
self._create_role(role)
def tearDown(self):
super(LoadRoleTests, self).tearDown()
rmtree(self.directory)
def _create_role(self, role):
"""Create a mock role file which simple contains it's own name as
the file contents.
"""
with open(path.join(self.directory, role), 'w') as f:
f.write("CONTENTS FOR {0}".format(role))
def test_list_roles(self):
# test
roles = sorted(_list_roles(self.directory))
# verify
self.assertEqual([
('role1.yaml', path.join(self.directory, "role1.yaml")),
('role2.yml', path.join(self.directory, "role2.yml")),
], roles)
def test_list_roles_invalid(self):
# setup
invalid_path = path.join(self.directory, "FAKEPATH/")
self.assertFalse(path.isdir(invalid_path))
# test
list_call = _list_roles(invalid_path)
# verify
self.assertRaises(ValueError, list, list_call)
def test_dry_run(self):
# test
total, created, updated = load_roles(
self.directory, dry_run=True)
# verify
self.assertEqual(['role1.yaml', 'role2.yml'], sorted(total))
self.assertEqual([], created)
self.assertEqual([], updated)
def test_import(self):
# test
total, created, updated = load_roles(self.directory)
# verify
self.assertEqual(['role1.yaml', 'role2.yml'], sorted(total))
self.assertEqual(['role1.yaml', 'role2.yml'], sorted(created))
self.assertEqual([], updated)
def test_import_update(self):
# setup
_create_or_update("role2.yml", "contents")
# test
total, created, updated = load_roles(self.directory)
# verify
self.assertEqual(['role1.yaml', 'role2.yml'], sorted(total))
self.assertEqual(['role1.yaml', ], created)
self.assertEqual(['role2.yml', ], updated)