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:
parent
b43e6af3df
commit
5f1df10369
@ -26,6 +26,7 @@ packages =
|
||||
console_scripts =
|
||||
tuskar-api = tuskar.cmd.api:main
|
||||
tuskar-dbsync = tuskar.cmd.dbsync:main
|
||||
tuskar-load-roles = tuskar.cmd.load_roles:main
|
||||
|
||||
[build_sphinx]
|
||||
all_files = 1
|
||||
|
56
tuskar/cmd/load_roles.py
Normal file
56
tuskar/cmd/load_roles.py
Normal 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")
|
104
tuskar/storage/load_roles.py
Normal file
104
tuskar/storage/load_roles.py
Normal 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
|
36
tuskar/tests/cmd/test_load_roles.py
Normal file
36
tuskar/tests/cmd/test_load_roles.py
Normal 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)
|
104
tuskar/tests/storage/test_load_roles.py
Normal file
104
tuskar/tests/storage/test_load_roles.py
Normal 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)
|
Loading…
Reference in New Issue
Block a user