Pass in InstanceConfig instead of ssh_keys
Change-Id: I7b313174896917d583bf8356207abf57ea08a197
This commit is contained in:
parent
72995b1117
commit
703bddb775
|
@ -13,7 +13,8 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from metalsmith._config import InstanceConfig
|
||||
from metalsmith._instance import Instance
|
||||
from metalsmith._provisioner import Provisioner
|
||||
|
||||
__all__ = ['Instance', 'Provisioner']
|
||||
__all__ = ['Instance', 'InstanceConfig', 'Provisioner']
|
||||
|
|
|
@ -19,6 +19,7 @@ import sys
|
|||
|
||||
from openstack import config as os_config
|
||||
|
||||
from metalsmith import _config
|
||||
from metalsmith import _format
|
||||
from metalsmith import _provisioner
|
||||
from metalsmith import _utils
|
||||
|
@ -51,12 +52,14 @@ def _do_deploy(api, args, formatter):
|
|||
if args.hostname and not _utils.is_hostname_safe(args.hostname):
|
||||
raise RuntimeError("%s cannot be used as a hostname" % args.hostname)
|
||||
|
||||
config = _config.InstanceConfig(ssh_keys=ssh_keys)
|
||||
|
||||
node = api.reserve_node(args.resource_class, capabilities=capabilities)
|
||||
instance = api.provision_node(node,
|
||||
image=args.image,
|
||||
nics=args.nics,
|
||||
root_disk_size=args.root_disk_size,
|
||||
ssh_keys=ssh_keys,
|
||||
config=config,
|
||||
hostname=args.hostname,
|
||||
netboot=args.netboot,
|
||||
wait=wait)
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
# Copyright 2018 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 contextlib
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
|
||||
class InstanceConfig(object):
|
||||
"""Configuration of the target instance.
|
||||
|
||||
The information attached to this object will be passed via a configdrive
|
||||
to the instance's first boot script (e.g. cloud-init).
|
||||
|
||||
:ivar ssh_keys: List of SSH public keys.
|
||||
"""
|
||||
|
||||
def __init__(self, ssh_keys=None):
|
||||
self.ssh_keys = ssh_keys or []
|
||||
|
||||
@contextlib.contextmanager
|
||||
def build_configdrive_directory(self, node, hostname):
|
||||
"""Build a configdrive from the provided information.
|
||||
|
||||
:param node: `Node` object.
|
||||
:param hostname: instance hostname.
|
||||
:return: a context manager yielding a directory with files
|
||||
"""
|
||||
d = tempfile.mkdtemp()
|
||||
try:
|
||||
metadata = {'public_keys': self.ssh_keys,
|
||||
'uuid': node.uuid,
|
||||
'name': node.name,
|
||||
'hostname': hostname,
|
||||
'launch_index': 0,
|
||||
'availability_zone': '',
|
||||
'files': [],
|
||||
'meta': {}}
|
||||
for version in ('2012-08-10', 'latest'):
|
||||
subdir = os.path.join(d, 'openstack', version)
|
||||
if not os.path.exists(subdir):
|
||||
os.makedirs(subdir)
|
||||
|
||||
with open(os.path.join(subdir, 'meta_data.json'), 'w') as fp:
|
||||
json.dump(metadata, fp)
|
||||
|
||||
yield d
|
||||
finally:
|
||||
shutil.rmtree(d)
|
|
@ -19,6 +19,7 @@ import sys
|
|||
|
||||
import six
|
||||
|
||||
from metalsmith import _config
|
||||
from metalsmith import _instance
|
||||
from metalsmith import _os_api
|
||||
from metalsmith import _scheduler
|
||||
|
@ -144,7 +145,7 @@ class Provisioner(object):
|
|||
return hostname
|
||||
|
||||
def provision_node(self, node, image, nics=None, root_disk_size=None,
|
||||
ssh_keys=None, hostname=None, netboot=False, wait=None):
|
||||
config=None, hostname=None, netboot=False, wait=None):
|
||||
"""Provision the node with the given image.
|
||||
|
||||
Example::
|
||||
|
@ -165,8 +166,8 @@ class Provisioner(object):
|
|||
to create a port on (``{"network": "<network name or ID>"}``).
|
||||
:param root_disk_size: The size of the root partition. By default
|
||||
the value of the local_gb property is used.
|
||||
:param ssh_keys: list of public parts of the SSH keys to upload
|
||||
to the nodes.
|
||||
:param config: :py:class:`metalsmith.InstanceConfig` object with
|
||||
the configuration to pass to the instance.
|
||||
:param hostname: Hostname to assign to the instance. Defaults to the
|
||||
node's name or UUID.
|
||||
:param netboot: Whether to use networking boot for final instances.
|
||||
|
@ -177,6 +178,8 @@ class Provisioner(object):
|
|||
is already finished.
|
||||
:raises: :py:class:`metalsmith.exceptions.Error`
|
||||
"""
|
||||
if config is None:
|
||||
config = _config.InstanceConfig()
|
||||
node = self._check_node_for_deploy(node)
|
||||
created_ports = []
|
||||
attached_ports = []
|
||||
|
@ -225,7 +228,7 @@ class Provisioner(object):
|
|||
|
||||
LOG.debug('Generating a configdrive for node %s',
|
||||
_utils.log_node(node))
|
||||
with _utils.config_drive_dir(node, ssh_keys, hostname) as cd:
|
||||
with config.build_configdrive_directory(node, hostname) as cd:
|
||||
self._api.node_action(node, 'active',
|
||||
configdrive=cd)
|
||||
except Exception:
|
||||
|
|
|
@ -14,12 +14,7 @@
|
|||
# limitations under the License.
|
||||
|
||||
import collections
|
||||
import contextlib
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
import six
|
||||
|
||||
|
@ -47,31 +42,6 @@ def get_capabilities(node):
|
|||
return caps
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def config_drive_dir(node, ssh_keys, hostname):
|
||||
d = tempfile.mkdtemp()
|
||||
try:
|
||||
metadata = {'public_keys': ssh_keys,
|
||||
'uuid': node.uuid,
|
||||
'name': node.name,
|
||||
'hostname': hostname,
|
||||
'launch_index': 0,
|
||||
'availability_zone': '',
|
||||
'files': [],
|
||||
'meta': {}}
|
||||
for version in ('2012-08-10', 'latest'):
|
||||
subdir = os.path.join(d, 'openstack', version)
|
||||
if not os.path.exists(subdir):
|
||||
os.makedirs(subdir)
|
||||
|
||||
with open(os.path.join(subdir, 'meta_data.json'), 'w') as fp:
|
||||
json.dump(metadata, fp)
|
||||
|
||||
yield d
|
||||
finally:
|
||||
shutil.rmtree(d)
|
||||
|
||||
|
||||
def get_root_disk(root_disk_size, node):
|
||||
"""Validate and calculate the root disk size."""
|
||||
if root_disk_size is not None:
|
||||
|
|
|
@ -61,10 +61,12 @@ class TestDeploy(testtools.TestCase):
|
|||
image='myimg',
|
||||
nics=[{'network': 'mynet'}],
|
||||
root_disk_size=None,
|
||||
ssh_keys=[],
|
||||
config=mock.ANY,
|
||||
hostname=None,
|
||||
netboot=False,
|
||||
wait=1800)
|
||||
config = mock_pr.return_value.provision_node.call_args[1]['config']
|
||||
self.assertEqual([], config.ssh_keys)
|
||||
mock_log.basicConfig.assert_called_once_with(level=mock_log.WARNING,
|
||||
format=mock.ANY)
|
||||
self.assertEqual(
|
||||
|
@ -104,7 +106,7 @@ class TestDeploy(testtools.TestCase):
|
|||
image='myimg',
|
||||
nics=[{'network': 'mynet'}],
|
||||
root_disk_size=None,
|
||||
ssh_keys=[],
|
||||
config=mock.ANY,
|
||||
hostname=None,
|
||||
netboot=False,
|
||||
wait=1800)
|
||||
|
@ -176,7 +178,7 @@ class TestDeploy(testtools.TestCase):
|
|||
image='myimg',
|
||||
nics=[{'network': 'mynet'}],
|
||||
root_disk_size=None,
|
||||
ssh_keys=[],
|
||||
config=mock.ANY,
|
||||
hostname=None,
|
||||
netboot=False,
|
||||
wait=1800)
|
||||
|
@ -198,7 +200,7 @@ class TestDeploy(testtools.TestCase):
|
|||
image='myimg',
|
||||
nics=[{'network': 'mynet'}],
|
||||
root_disk_size=None,
|
||||
ssh_keys=[],
|
||||
config=mock.ANY,
|
||||
hostname=None,
|
||||
netboot=False,
|
||||
wait=1800)
|
||||
|
@ -228,7 +230,7 @@ class TestDeploy(testtools.TestCase):
|
|||
image='myimg',
|
||||
nics=[{'network': 'mynet'}],
|
||||
root_disk_size=None,
|
||||
ssh_keys=[],
|
||||
config=mock.ANY,
|
||||
hostname=None,
|
||||
netboot=False,
|
||||
wait=1800)
|
||||
|
@ -260,7 +262,7 @@ class TestDeploy(testtools.TestCase):
|
|||
image='myimg',
|
||||
nics=[{'network': 'mynet'}],
|
||||
root_disk_size=None,
|
||||
ssh_keys=[],
|
||||
config=mock.ANY,
|
||||
hostname=None,
|
||||
netboot=False,
|
||||
wait=1800)
|
||||
|
@ -290,7 +292,7 @@ class TestDeploy(testtools.TestCase):
|
|||
image='myimg',
|
||||
nics=[{'network': 'mynet'}],
|
||||
root_disk_size=None,
|
||||
ssh_keys=[],
|
||||
config=mock.ANY,
|
||||
hostname=None,
|
||||
netboot=False,
|
||||
wait=1800)
|
||||
|
@ -320,7 +322,7 @@ class TestDeploy(testtools.TestCase):
|
|||
image='myimg',
|
||||
nics=[{'network': 'mynet'}],
|
||||
root_disk_size=None,
|
||||
ssh_keys=[],
|
||||
config=mock.ANY,
|
||||
hostname=None,
|
||||
netboot=False,
|
||||
wait=1800)
|
||||
|
@ -375,7 +377,7 @@ class TestDeploy(testtools.TestCase):
|
|||
image='myimg',
|
||||
nics=[{'network': 'mynet'}],
|
||||
root_disk_size=None,
|
||||
ssh_keys=[],
|
||||
config=mock.ANY,
|
||||
hostname=None,
|
||||
netboot=False,
|
||||
wait=1800)
|
||||
|
@ -400,10 +402,12 @@ class TestDeploy(testtools.TestCase):
|
|||
image='myimg',
|
||||
nics=[{'network': 'mynet'}],
|
||||
root_disk_size=None,
|
||||
ssh_keys=['foo'],
|
||||
config=mock.ANY,
|
||||
hostname=None,
|
||||
netboot=False,
|
||||
wait=1800)
|
||||
config = mock_pr.return_value.provision_node.call_args[1]['config']
|
||||
self.assertEqual(['foo'], config.ssh_keys)
|
||||
|
||||
def test_args_port(self, mock_os_conf, mock_pr):
|
||||
args = ['deploy', '--port', 'myport', '--image', 'myimg',
|
||||
|
@ -421,7 +425,7 @@ class TestDeploy(testtools.TestCase):
|
|||
image='myimg',
|
||||
nics=[{'port': 'myport'}],
|
||||
root_disk_size=None,
|
||||
ssh_keys=[],
|
||||
config=mock.ANY,
|
||||
hostname=None,
|
||||
netboot=False,
|
||||
wait=1800)
|
||||
|
@ -441,7 +445,7 @@ class TestDeploy(testtools.TestCase):
|
|||
image='myimg',
|
||||
nics=None,
|
||||
root_disk_size=None,
|
||||
ssh_keys=[],
|
||||
config=mock.ANY,
|
||||
hostname=None,
|
||||
netboot=False,
|
||||
wait=1800)
|
||||
|
@ -464,7 +468,7 @@ class TestDeploy(testtools.TestCase):
|
|||
nics=[{'network': 'net1'}, {'port': 'port1'},
|
||||
{'port': 'port2'}, {'network': 'net2'}],
|
||||
root_disk_size=None,
|
||||
ssh_keys=[],
|
||||
config=mock.ANY,
|
||||
hostname=None,
|
||||
netboot=False,
|
||||
wait=1800)
|
||||
|
@ -485,7 +489,7 @@ class TestDeploy(testtools.TestCase):
|
|||
image='myimg',
|
||||
nics=None,
|
||||
root_disk_size=None,
|
||||
ssh_keys=[],
|
||||
config=mock.ANY,
|
||||
hostname='host',
|
||||
netboot=False,
|
||||
wait=1800)
|
||||
|
@ -506,7 +510,7 @@ class TestDeploy(testtools.TestCase):
|
|||
image='myimg',
|
||||
nics=[{'network': 'mynet'}],
|
||||
root_disk_size=None,
|
||||
ssh_keys=[],
|
||||
config=mock.ANY,
|
||||
hostname=None,
|
||||
netboot=False,
|
||||
wait=3600)
|
||||
|
@ -527,7 +531,7 @@ class TestDeploy(testtools.TestCase):
|
|||
image='myimg',
|
||||
nics=[{'network': 'mynet'}],
|
||||
root_disk_size=None,
|
||||
ssh_keys=[],
|
||||
config=mock.ANY,
|
||||
hostname=None,
|
||||
netboot=False,
|
||||
wait=None)
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
import mock
|
||||
import testtools
|
||||
|
||||
from metalsmith import _config
|
||||
from metalsmith import _instance
|
||||
from metalsmith import _os_api
|
||||
from metalsmith import _provisioner
|
||||
|
@ -134,6 +135,30 @@ class TestProvisionNode(Base):
|
|||
self.assertFalse(self.api.release_node.called)
|
||||
self.assertFalse(self.api.delete_port.called)
|
||||
|
||||
def test_with_config(self):
|
||||
config = mock.MagicMock(spec=_config.InstanceConfig)
|
||||
inst = self.pr.provision_node(self.node, 'image',
|
||||
[{'network': 'network'}],
|
||||
config=config)
|
||||
|
||||
self.assertEqual(inst.uuid, self.node.uuid)
|
||||
self.assertEqual(inst.node, self.node)
|
||||
|
||||
config.build_configdrive_directory.assert_called_once_with(
|
||||
self.node, self.node.name)
|
||||
self.api.create_port.assert_called_once_with(
|
||||
network_id=self.api.get_network.return_value.id)
|
||||
self.api.attach_port_to_node.assert_called_once_with(
|
||||
self.node.uuid, self.api.create_port.return_value.id)
|
||||
self.api.update_node.assert_called_once_with(self.node, self.updates)
|
||||
self.api.validate_node.assert_called_once_with(self.node,
|
||||
validate_deploy=True)
|
||||
self.api.node_action.assert_called_once_with(self.node, 'active',
|
||||
configdrive=mock.ANY)
|
||||
self.assertFalse(self.api.wait_for_node_state.called)
|
||||
self.assertFalse(self.api.release_node.called)
|
||||
self.assertFalse(self.api.delete_port.called)
|
||||
|
||||
def test_with_hostname(self):
|
||||
hostname = 'control-0.example.com'
|
||||
inst = self.pr.provision_node(self.node, 'image',
|
||||
|
|
Loading…
Reference in New Issue