Implementation of the Baremental import command

Change-Id: I2db67a7deea8367e660275ee637ab766587d223b
This commit is contained in:
Dougal Matthews 2015-03-24 17:16:22 +00:00
parent 7182f50cd2
commit d6a3d98ed9
8 changed files with 300 additions and 26 deletions

View File

@ -15,4 +15,5 @@
import pbr.version
__version__ = pbr.version.VersionInfo('python-rdomanager-oscplugin').version_string()
__version__ = pbr.version.VersionInfo(
'python-rdomanager-oscplugin').version_string()

View File

@ -17,6 +17,7 @@
import logging
from ironicclient import client as ironic_client
from openstackclient.common import utils
@ -27,28 +28,10 @@ DEFAULT_RDOMANAGER_OSCPLUGIN_API_VERSION = '1'
# Required by the OSC plugin interface
API_NAME = 'rdomanager_oscplugin'
API_VERSION_OPTION = 'os_rdomanager_oscplugin_api_version'
API_VERSIONS = {
'1': 'rdomanager_oscplugin.plugin.EmptyClient',
}
# Required by the OSC plugin interface
def make_client(instance):
"""Returns a client to the ClientManager
Called to instantiate the requested client version. instance has
any available auth info that may be required to prepare the client.
:param ClientManager instance: The ClientManager that owns the new client
"""
plugin_client = utils.get_client_class(
API_NAME,
instance._api_version[API_NAME],
API_VERSIONS)
LOG.debug('Instantiating plugin client: %s' % plugin_client)
client = plugin_client()
return client
return ClientWrapper(instance)
# Required by the OSC plugin interface
@ -74,7 +57,24 @@ def build_option_parser(parser):
return parser
class EmptyClient(object):
"""The ultimate placeholder"""
class ClientWrapper(object):
pass
def __init__(self, instace):
self._instace = instace
self._baremetal = None
def baremetal(self):
if self._baremetal is None:
endpoint = self._instace.get_endpoint_for_service_type(
"baremetal",
region_name=self._instace._region_name,
)
token = self._instace.auth.get_token(self._instace.session)
self._baremetal = ironic_client.get_client(
1, os_auth_token=token, ironic_url=endpoint)
return self._baremetal

View File

@ -0,0 +1,39 @@
# Copyright 2015 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 mock
from openstackclient.tests import utils
class FakeClientWrapper(object):
def __init__(self):
self._instace = mock.Mock()
self._baremetal = None
def baremetal(self):
if self._baremetal is None:
self._baremetal = mock.Mock()
return self._baremetal
class TestBaremetal(utils.TestCommand):
def setUp(self):
super(TestBaremetal, self).setUp()
self.app.client_manager.rdomanager_oscplugin = FakeClientWrapper()

View File

@ -0,0 +1,142 @@
# Copyright 2015 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 tempfile
import json
import mock
import os
from rdomanager_oscplugin.tests.v1.baremetal import fakes
from rdomanager_oscplugin.v1 import baremetal
class TestBaremetalBase(fakes.TestBaremetal):
def setUp(self):
super(TestBaremetalBase, self).setUp()
# Get the command object to test
self.cmd = baremetal.ImportPlugin(self.app, None)
class TestImport(TestBaremetalBase):
def setUp(self):
super(TestImport, self).setUp()
self.json_file = tempfile.NamedTemporaryFile(mode='w', delete=False)
self.csv_file = tempfile.NamedTemporaryFile(mode='w', delete=False)
self.csv_file.write("""\
pxe_ssh,192.168.122.1,root,"KEY1",00:d0:28:4c:e8:e8
pxe_ssh,192.168.122.1,root,"KEY2",00:7c:ef:3d:eb:60""")
json.dump([{
"pm_user": "stack",
"pm_addr": "192.168.122.1",
"pm_password": "KEY1",
"pm_type": "pxe_ssh",
"mac": [
"00:0b:d0:69:7e:59"
],
}, {
"arch": "x86_64",
"pm_user": "stack",
"pm_addr": "192.168.122.2",
"pm_password": "KEY2",
"pm_type": "pxe_ssh",
"mac": [
"00:0b:d0:69:7e:58"
]
}], self.json_file)
self.json_file.close()
self.csv_file.close()
def tearDown(self):
super(TestImport, self).tearDown()
os.unlink(self.json_file.name)
os.unlink(self.csv_file.name)
@mock.patch('os_cloud_config.nodes.register_all_nodes')
def test_json_import(self, mock_register_nodes):
arglist = [self.json_file.name, '--json', '-s', 'http://localhost']
verifylist = [
('csv', False),
('json', True),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
mock_register_nodes.assert_called_with(
'http://localhost',
[
{
'pm_password': 'KEY1',
'pm_type': 'pxe_ssh',
'pm_user': 'stack',
'pm_addr': '192.168.122.1',
'mac': ['00:0b:d0:69:7e:59']
}, {
'pm_user': 'stack',
'pm_password': 'KEY2',
'pm_addr': '192.168.122.2',
'arch': 'x86_64',
'pm_type': 'pxe_ssh',
'mac': ['00:0b:d0:69:7e:58']
}
],
client=self.app.client_manager.rdomanager_oscplugin.baremetal(),
keystone_client=None)
@mock.patch('os_cloud_config.nodes.register_all_nodes')
def test_csv_import(self, mock_register_nodes):
arglist = [self.csv_file.name, '--csv', '-s', 'http://localhost']
verifylist = [
('csv', True),
('json', False),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
mock_register_nodes.assert_called_with(
'http://localhost',
[
{
'pm_password': 'KEY1',
'pm_user': 'root',
'pm_type': 'pxe_ssh',
'pm_addr': '192.168.122.1',
'mac': ['00:d0:28:4c:e8:e8']
}, {
'pm_password': 'KEY2',
'pm_user': 'root',
'pm_type': 'pxe_ssh',
'pm_addr': '192.168.122.1',
'mac': ['00:7c:ef:3d:eb:60']
}
],
client=self.app.client_manager.rdomanager_oscplugin.baremetal(),
keystone_client=None)

View File

@ -0,0 +1,89 @@
# Copyright 2015 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.
#
from __future__ import print_function
import argparse
import csv
import json
import logging
import sys
from os_cloud_config import nodes
from cliff import command
def _csv_to_nodes_dict(nodes_csv):
"""Convert CSV to a list of dicts formatted for os_cloud_config
Given a CSV file in the format below, convert it into the
structure expected by os_could_config JSON files.
pm_type, pm_addr, pm_user, pm_password, mac
"""
data = []
for row in csv.reader(nodes_csv):
node = {
"pm_user": row[2],
"pm_addr": row[1],
"pm_password": row[3],
"pm_type": row[0],
"mac": [
row[4]
]
}
data.append(node)
return data
class ImportPlugin(command.Command):
"""Baremetal Import plugin"""
log = logging.getLogger(__name__ + ".ImportPlugin")
def get_parser(self, prog_name):
parser = super(ImportPlugin, self).get_parser(prog_name)
parser.add_argument('-s', '--service-host', dest='service_host',
help='Nova compute service host to register nodes '
'with')
parser.add_argument('--json', dest='json', action='store_true')
parser.add_argument('--csv', dest='csv', action='store_true')
parser.add_argument('file_in', type=argparse.FileType('r'))
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)" % parsed_args)
# We need JSON or CSV to be specified, not both.
if parsed_args.json == parsed_args.csv:
print("ERROR: Either --json or --csv needs to be specified.",
file=sys.stderr)
return
if parsed_args.json is True:
nodes_json = json.load(parsed_args.file_in)
else:
nodes_json = _csv_to_nodes_dict(parsed_args.file_in)
nodes.register_all_nodes(
parsed_args.service_host,
nodes_json,
client=self.app.client_manager.rdomanager_oscplugin.baremetal(),
keystone_client=self.app.client_manager.identity)

View File

@ -4,7 +4,9 @@
pbr>=0.6,!=0.7,<1.0
Babel>=1.3
cliff>=1.7.0 # Apache-2.0
cliff-tablib>=1.0
cliff>=1.7.0 # Apache-2.0
os-cloud-config
python-ironicclient>=0.4.1
python-openstackclient>=1.0.0

View File

@ -51,9 +51,10 @@ output_file = rdomanager_oscplugin/locale/rdomanager-oscplugin.pot
[entry_points]
openstack.cli.extension =
rdomanger_oscplugin = rdomanager_oscplugin.plugin
rdomanager_oscplugin = rdomanager_oscplugin.plugin
openstack.rdomanager_oscplugin.v1 =
undercloud_install = rdomanager_oscplugin.v1.undercloud:InstallPlugin
baremetal_import = rdomanager_oscplugin.v1.baremetal:ImportPlugin
overcloud_image_build = rdomanager_oscplugin.v1.overcloud_image:BuildPlugin
overcloud_image_create = rdomanager_oscplugin.v1.overcloud_image:CreatePlugin
undercloud_install = rdomanager_oscplugin.v1.undercloud:InstallPlugin