diff --git a/etc/ironic/rootwrap.conf b/etc/ironic/rootwrap.conf new file mode 100644 index 00000000..345280a4 --- /dev/null +++ b/etc/ironic/rootwrap.conf @@ -0,0 +1,27 @@ +# Configuration for ironic-rootwrap +# This file should be owned by (and only-writeable by) the root user + +[DEFAULT] +# List of directories to load filter definitions from (separated by ','). +# These directories MUST all be only writeable by root ! +filters_path=/etc/ironic/rootwrap.d,/usr/share/ironic/rootwrap + +# List of directories to search executables in, in case filters do not +# explicitely specify a full path (separated by ',') +# If not specified, defaults to system PATH environment variable. +# These directories MUST all be only writeable by root ! +exec_dirs=/sbin,/usr/sbin,/bin,/usr/bin + +# Enable logging to syslog +# Default value is False +use_syslog=False + +# Which syslog facility to use. +# Valid values include auth, authpriv, syslog, user0, user1... +# Default value is 'syslog' +syslog_log_facility=syslog + +# Which messages to log. +# INFO means log all usage +# ERROR means only log unsuccessful attempts +syslog_log_level=ERROR diff --git a/etc/ironic/rootwrap.d/ironic-deploy-helper.filters b/etc/ironic/rootwrap.d/ironic-deploy-helper.filters new file mode 100644 index 00000000..2ead572c --- /dev/null +++ b/etc/ironic/rootwrap.d/ironic-deploy-helper.filters @@ -0,0 +1,10 @@ +# ironic-rootwrap command filters for ironic-deploy-helper +# This file should be owned by (and only-writeable by) the root user + +[Filters] +# ironic-deploy-helper +iscsiadm: CommandFilter, /sbin/iscsiadm, root +sfdisk: CommandFilter, /sbin/sfdisk, root +dd: CommandFilter, /bin/dd, root +mkswap: CommandFilter, /sbin/mkswap, root +blkid: CommandFilter, /sbin/blkid, root diff --git a/etc/ironic/rootwrap.d/ironic-manage-ipmi.filters b/etc/ironic/rootwrap.d/ironic-manage-ipmi.filters new file mode 100644 index 00000000..34f29080 --- /dev/null +++ b/etc/ironic/rootwrap.d/ironic-manage-ipmi.filters @@ -0,0 +1,9 @@ +# ironic-rootwrap command filters for manager nodes +# This file should be owned by (and only-writeable by) the root user + +[Filters] +# ironic/manager/ipmi.py: 'ipmitool', .. +ipmitool: CommandFilter, /usr/bin/ipmitool, root + +# ironic/manager/ipmi.py: 'kill', '-TERM', str(console_pid) +kill_shellinaboxd: KillFilter, root, /usr/local/bin/shellinaboxd, -15, -TERM diff --git a/ironic/netconf.py b/ironic/netconf.py new file mode 100644 index 00000000..78939d58 --- /dev/null +++ b/ironic/netconf.py @@ -0,0 +1,62 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# All Rights Reserved. +# Copyright 2012 Red Hat, Inc. +# +# 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. + +import socket + +from oslo.config import cfg + +CONF = cfg.CONF + + +def _get_my_ip(): + """ + Returns the actual ip of the local machine. + + This code figures out what source address would be used if some traffic + were to be sent out to some well known address on the Internet. In this + case, a Google DNS server is used, but the specific address does not + matter much. No traffic is actually sent. + """ + try: + csock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + csock.connect(('8.8.8.8', 80)) + (addr, port) = csock.getsockname() + csock.close() + return addr + except socket.error: + return "127.0.0.1" + + +netconf_opts = [ + cfg.StrOpt('my_ip', + default=_get_my_ip(), + help='ip address of this host'), + cfg.StrOpt('host', + default=socket.gethostname(), + help='Name of this node. This can be an opaque identifier. ' + 'It is not necessarily a hostname, FQDN, or IP address. ' + 'However, the node name must be valid within ' + 'an AMQP key, and if using ZeroMQ, a valid ' + 'hostname, FQDN, or IP address'), + cfg.BoolOpt('use_ipv6', + default=False, + help='use ipv6'), +] + +CONF.register_opts(netconf_opts) diff --git a/ironic/tests/conf_fixture.py b/ironic/tests/conf_fixture.py new file mode 100644 index 00000000..697f4ed4 --- /dev/null +++ b/ironic/tests/conf_fixture.py @@ -0,0 +1,76 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# 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. + +import fixtures +from oslo.config import cfg + +from nova import config +from nova import ipv6 +from nova import paths +from nova.tests import utils + +CONF = cfg.CONF +CONF.import_opt('use_ipv6', 'nova.netconf') +CONF.import_opt('host', 'nova.netconf') +CONF.import_opt('scheduler_driver', 'nova.scheduler.manager') +CONF.import_opt('fake_network', 'nova.network.manager') +CONF.import_opt('network_size', 'nova.network.manager') +CONF.import_opt('num_networks', 'nova.network.manager') +CONF.import_opt('floating_ip_dns_manager', 'nova.network.floating_ips') +CONF.import_opt('instance_dns_manager', 'nova.network.floating_ips') +CONF.import_opt('policy_file', 'nova.policy') +CONF.import_opt('compute_driver', 'nova.virt.driver') +CONF.import_opt('api_paste_config', 'nova.wsgi') + + +class ConfFixture(fixtures.Fixture): + """Fixture to manage global conf settings.""" + + def __init__(self, conf): + self.conf = conf + + def setUp(self): + super(ConfFixture, self).setUp() + + self.conf.set_default('api_paste_config', + paths.state_path_def('etc/nova/api-paste.ini')) + self.conf.set_default('host', 'fake-mini') + self.conf.set_default('compute_driver', 'nova.virt.fake.FakeDriver') + self.conf.set_default('fake_network', True) + self.conf.set_default('fake_rabbit', True) + self.conf.set_default('flat_network_bridge', 'br100') + self.conf.set_default('floating_ip_dns_manager', + 'nova.tests.utils.dns_manager') + self.conf.set_default('instance_dns_manager', + 'nova.tests.utils.dns_manager') + self.conf.set_default('lock_path', None) + self.conf.set_default('network_size', 8) + self.conf.set_default('num_networks', 2) + self.conf.set_default('rpc_backend', + 'nova.openstack.common.rpc.impl_fake') + self.conf.set_default('rpc_cast_timeout', 5) + self.conf.set_default('rpc_response_timeout', 5) + self.conf.set_default('sql_connection', "sqlite://") + self.conf.set_default('sqlite_synchronous', False) + self.conf.set_default('use_ipv6', True) + self.conf.set_default('verbose', True) + self.conf.set_default('vlan_interface', 'eth0') + config.parse_args([], default_config_files=[]) + self.addCleanup(self.conf.reset) + self.addCleanup(utils.cleanup_dns_managers) + self.addCleanup(ipv6.api.reset_backend) diff --git a/ironic/tests/fake_policy.py b/ironic/tests/fake_policy.py new file mode 100644 index 00000000..104c1d82 --- /dev/null +++ b/ironic/tests/fake_policy.py @@ -0,0 +1,23 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2012 OpenStack Foundation +# +# 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. + + +policy_data = """ +{ + "admin_api": "role:admin", + "context_is_admin": "role:admin or role:administrator", +} +""" diff --git a/ironic/tests/policy_fixture.py b/ironic/tests/policy_fixture.py new file mode 100644 index 00000000..91813def --- /dev/null +++ b/ironic/tests/policy_fixture.py @@ -0,0 +1,44 @@ +# Copyright 2012 Hewlett-Packard Development Company, L.P. +# +# 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. + +import os + +import fixtures +from oslo.config import cfg + +from nova.openstack.common import policy as common_policy +import nova.policy +from nova.tests import fake_policy + +CONF = cfg.CONF + + +class PolicyFixture(fixtures.Fixture): + + def setUp(self): + super(PolicyFixture, self).setUp() + self.policy_dir = self.useFixture(fixtures.TempDir()) + self.policy_file_name = os.path.join(self.policy_dir.path, + 'policy.json') + with open(self.policy_file_name, 'w') as policy_file: + policy_file.write(fake_policy.policy_data) + CONF.set_override('policy_file', self.policy_file_name) + nova.policy.reset() + nova.policy.init() + self.addCleanup(nova.policy.reset) + + def set_rules(self, rules): + common_policy.set_rules(common_policy.Rules( + dict((k, common_policy.parse_rule(v)) + for k, v in rules.items()))) diff --git a/ironic/version.py b/ironic/version.py new file mode 100644 index 00000000..743941f2 --- /dev/null +++ b/ironic/version.py @@ -0,0 +1,47 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 OpenStack Foundation +# 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. + +try: + from ironic.vcsversion import version_info +except ImportError: + version_info = {'branch_nick': u'LOCALBRANCH', + 'revision_id': 'LOCALREVISION', + 'revno': 0} + +IRONIC_VERSION = ['2013', '1'] +YEAR, COUNT = IRONIC_VERSION + +FINAL = False # This becomes true at Release Candidate time + + +def canonical_version_string(): + return '.'.join([YEAR, COUNT]) + + +def version_string(): + if FINAL: + return canonical_version_string() + else: + return '%s-dev' % (canonical_version_string(),) + + +def vcs_version_string(): + return "%s:%s" % (version_info['branch_nick'], version_info['revision_id']) + + +def version_string_with_vcs(): + return "%s-%s" % (canonical_version_string(), vcs_version_string()) diff --git a/tools/flakes.py b/tools/flakes.py new file mode 100644 index 00000000..191bd6ea --- /dev/null +++ b/tools/flakes.py @@ -0,0 +1,24 @@ +""" + wrapper for pyflakes to ignore gettext based warning: + "undefined name '_'" + + Synced in from openstack-common +""" + +__all__ = ['main'] + +import __builtin__ as builtins +import sys + +import pyflakes.api +from pyflakes import checker + + +def main(): + checker.Checker.builtIns = (set(dir(builtins)) | + set(['_']) | + set(checker._MAGIC_GLOBALS)) + sys.exit(pyflakes.api.main()) + +if __name__ == "__main__": + main() diff --git a/tools/install_venv_common.py b/tools/install_venv_common.py index f0a1722c..914fcf17 100644 --- a/tools/install_venv_common.py +++ b/tools/install_venv_common.py @@ -18,10 +18,15 @@ """Provides methods needed by installation script for OpenStack development virtual environments. +Since this script is used to bootstrap a virtualenv from the system's Python +environment, it should be kept strictly compatible with Python 2.6. + Synced in from openstack-common """ -import argparse +from __future__ import print_function + +import optparse import os import subprocess import sys @@ -39,7 +44,7 @@ class InstallVenv(object): self.project = project def die(self, message, *args): - print >> sys.stderr, message % args + print(message % args, file=sys.stderr) sys.exit(1) def check_python_version(self): @@ -86,20 +91,20 @@ class InstallVenv(object): virtual environment. """ if not os.path.isdir(self.venv): - print 'Creating venv...', + print('Creating venv...', end=' ') if no_site_packages: self.run_command(['virtualenv', '-q', '--no-site-packages', self.venv]) else: self.run_command(['virtualenv', '-q', self.venv]) - print 'done.' - print 'Installing pip in venv...', + print('done.') + print('Installing pip in venv...', end=' ') if not self.run_command(['tools/with_venv.sh', 'easy_install', 'pip>1.0']).strip(): self.die("Failed to install pip.") - print 'done.' + print('done.') else: - print "venv already exists..." + print("venv already exists...") pass def pip_install(self, *args): @@ -108,7 +113,7 @@ class InstallVenv(object): redirect_output=False) def install_dependencies(self): - print 'Installing dependencies with pip (this can take a while)...' + print('Installing dependencies with pip (this can take a while)...') # First things first, make sure our venv has the latest pip and # distribute. @@ -131,12 +136,12 @@ class InstallVenv(object): def parse_args(self, argv): """Parses command-line arguments.""" - parser = argparse.ArgumentParser() - parser.add_argument('-n', '--no-site-packages', - action='store_true', - help="Do not inherit packages from global Python " - "install") - return parser.parse_args(argv[1:]) + parser = optparse.OptionParser() + parser.add_option('-n', '--no-site-packages', + action='store_true', + help="Do not inherit packages from global Python " + "install") + return parser.parse_args(argv[1:])[0] class Distro(InstallVenv): @@ -150,12 +155,12 @@ class Distro(InstallVenv): return if self.check_cmd('easy_install'): - print 'Installing virtualenv via easy_install...', + print('Installing virtualenv via easy_install...', end=' ') if self.run_command(['easy_install', 'virtualenv']): - print 'Succeeded' + print('Succeeded') return else: - print 'Failed' + print('Failed') self.die('ERROR: virtualenv not found.\n\n%s development' ' requires virtualenv, please install it using your'