Add a glance module that allows uploading images
Add a new module, glance, that allows uploading or finding kernel and ramdisk images that are in Glance. This allows us to put setup-baremetal in -incubator on a radical diet. Also adds a command-line utility, upload_kernel_ramdisk to make adoption in -incubator easier. Change-Id: I2708646090cf9435f8910ee2170a5ff2939ec27a
This commit is contained in:
parent
e3abc1c269
commit
fc91c84a2a
38
os_cloud_config/cmd/tests/test_upload_kernel_ramdisk.py
Normal file
38
os_cloud_config/cmd/tests/test_upload_kernel_ramdisk.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# Copyright (c) 2014 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 sys
|
||||||
|
|
||||||
|
import mock
|
||||||
|
|
||||||
|
from os_cloud_config.cmd import upload_kernel_ramdisk
|
||||||
|
from os_cloud_config.tests import base
|
||||||
|
|
||||||
|
|
||||||
|
class UploadKernelRamdiskTest(base.TestCase):
|
||||||
|
|
||||||
|
@mock.patch('os_cloud_config.cmd.utils._clients.get_glance_client',
|
||||||
|
return_value='glance_client_mock')
|
||||||
|
@mock.patch('os_cloud_config.glance.create_or_find_kernel_and_ramdisk')
|
||||||
|
@mock.patch.dict('os.environ', {'OS_USERNAME': 'a', 'OS_PASSWORD': 'a',
|
||||||
|
'OS_TENANT_NAME': 'a', 'OS_AUTH_URL': 'a'})
|
||||||
|
@mock.patch.object(sys, 'argv', ['upload_kernel_ramdisk', '-k',
|
||||||
|
'bm-kernel', '-r', 'bm-ramdisk', '-l',
|
||||||
|
'kernel-file', '-s', 'ramdisk-file'])
|
||||||
|
def test_with_arguments(self, create_or_find_mock, glanceclient_mock):
|
||||||
|
upload_kernel_ramdisk.main()
|
||||||
|
create_or_find_mock.assert_called_once_with(
|
||||||
|
'glance_client_mock', 'bm-kernel', 'bm-ramdisk',
|
||||||
|
kernel_path='kernel-file', ramdisk_path='ramdisk-file')
|
56
os_cloud_config/cmd/upload_kernel_ramdisk.py
Normal file
56
os_cloud_config/cmd/upload_kernel_ramdisk.py
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
# 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 argparse
|
||||||
|
import logging
|
||||||
|
import textwrap
|
||||||
|
|
||||||
|
from os_cloud_config.cmd.utils import _clients as clients
|
||||||
|
from os_cloud_config.cmd.utils import environment
|
||||||
|
from os_cloud_config import glance
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args():
|
||||||
|
description = textwrap.dedent("""
|
||||||
|
Uploads the provided kernel and ramdisk to a Glance store.
|
||||||
|
""")
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description=description,
|
||||||
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||||
|
)
|
||||||
|
parser.add_argument('-k', '--kernel', dest='kernel',
|
||||||
|
help='Name of the kernel image', required=True)
|
||||||
|
parser.add_argument('-l' '--kernel-file', dest='kernel_file',
|
||||||
|
help='Kernel to upload', required=True)
|
||||||
|
parser.add_argument('-r', '--ramdisk', dest='ramdisk',
|
||||||
|
help='Name of the ramdisk image', required=True)
|
||||||
|
parser.add_argument('-s', '--ramdisk-file', dest='ramdisk_file',
|
||||||
|
help='Ramdisk to upload', required=True)
|
||||||
|
environment._add_logging_arguments(parser)
|
||||||
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
args = parse_args()
|
||||||
|
environment._configure_logging(args)
|
||||||
|
try:
|
||||||
|
environment._ensure()
|
||||||
|
client = clients.get_glance_client()
|
||||||
|
glance.create_or_find_kernel_and_ramdisk(
|
||||||
|
client, args.kernel, args.ramdisk, kernel_path=args.kernel_file,
|
||||||
|
ramdisk_path=args.ramdisk_file)
|
||||||
|
except Exception:
|
||||||
|
logging.exception("Unexpected error during command execution")
|
||||||
|
return 1
|
||||||
|
return 0
|
61
os_cloud_config/glance.py
Normal file
61
os_cloud_config/glance.py
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
# Copyright (c) 2014 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 logging
|
||||||
|
|
||||||
|
from glanceclient.openstack.common.apiclient import exceptions
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def create_or_find_kernel_and_ramdisk(glanceclient, kernel_name, ramdisk_name,
|
||||||
|
kernel_path=None, ramdisk_path=None):
|
||||||
|
"""Find or create a given kernel and ramdisk in Glance.
|
||||||
|
|
||||||
|
If either kernel_path or ramdisk_path is None, they will not be created,
|
||||||
|
and an exception will be raised if it does not exist in Glance.
|
||||||
|
|
||||||
|
:param glanceclient: A client for Glance.
|
||||||
|
:param kernel_name: Name to search for or create for the kernel.
|
||||||
|
:param ramdisk_name: Name to search for or create for the ramdisk.
|
||||||
|
:param kernel_path: Path to the kernel on disk.
|
||||||
|
:param ramdisk_path: Path to the ramdisk on disk.
|
||||||
|
|
||||||
|
:returns: A dictionary mapping kernel or ramdisk to the ID in Glance.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
kernel_image = glanceclient.images.find(name=kernel_name,
|
||||||
|
disk_format='aki')
|
||||||
|
except exceptions.NotFound:
|
||||||
|
if kernel_path:
|
||||||
|
kernel_image = glanceclient.images.create(
|
||||||
|
name=kernel_name, disk_format='aki', is_public=True,
|
||||||
|
data=open(kernel_path, 'rb'))
|
||||||
|
else:
|
||||||
|
raise ValueError("Kernel image not found in Glance, and no path "
|
||||||
|
"specified.")
|
||||||
|
try:
|
||||||
|
ramdisk_image = glanceclient.images.find(name=ramdisk_name,
|
||||||
|
disk_format='ari')
|
||||||
|
except exceptions.NotFound:
|
||||||
|
if ramdisk_path:
|
||||||
|
# public, type=ari
|
||||||
|
ramdisk_image = glanceclient.images.create(
|
||||||
|
name=ramdisk_name, disk_format='ari', is_public=True,
|
||||||
|
data=open(ramdisk_path, 'rb'))
|
||||||
|
else:
|
||||||
|
raise ValueError("Ramdisk image not found in Glance, and no path "
|
||||||
|
"specified.")
|
||||||
|
return {'kernel': kernel_image.id, 'ramdisk': ramdisk_image.id}
|
74
os_cloud_config/tests/test_glance.py
Normal file
74
os_cloud_config/tests/test_glance.py
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
# Copyright (c) 2014 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 collections
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
from glanceclient.openstack.common.apiclient import exceptions
|
||||||
|
import mock
|
||||||
|
import testtools
|
||||||
|
|
||||||
|
from os_cloud_config import glance
|
||||||
|
from os_cloud_config.tests import base
|
||||||
|
|
||||||
|
|
||||||
|
class GlanceTest(base.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(GlanceTest, self).setUp()
|
||||||
|
self.image = collections.namedtuple('image', ['id'])
|
||||||
|
|
||||||
|
def test_return_existing_kernel_and_ramdisk(self):
|
||||||
|
client = mock.MagicMock()
|
||||||
|
expected = {'kernel': 'aaa', 'ramdisk': 'zzz'}
|
||||||
|
client.images.find.side_effect = (self.image('aaa'), self.image('zzz'))
|
||||||
|
ids = glance.create_or_find_kernel_and_ramdisk(client, 'bm-kernel',
|
||||||
|
'bm-ramdisk')
|
||||||
|
client.images.create.assert_not_called()
|
||||||
|
self.assertEqual(expected, ids)
|
||||||
|
|
||||||
|
def test_raise_exception_kernel(self):
|
||||||
|
client = mock.MagicMock()
|
||||||
|
client.images.find.side_effect = exceptions.NotFound
|
||||||
|
message = "Kernel image not found in Glance, and no path specified."
|
||||||
|
with testtools.ExpectedException(ValueError, message):
|
||||||
|
glance.create_or_find_kernel_and_ramdisk(client, 'bm-kernel',
|
||||||
|
None)
|
||||||
|
|
||||||
|
def test_raise_exception_ramdisk(self):
|
||||||
|
client = mock.MagicMock()
|
||||||
|
client.images.find.side_effect = (self.image('aaa'),
|
||||||
|
exceptions.NotFound)
|
||||||
|
message = "Ramdisk image not found in Glance, and no path specified."
|
||||||
|
with testtools.ExpectedException(ValueError, message):
|
||||||
|
glance.create_or_find_kernel_and_ramdisk(client, 'bm-kernel',
|
||||||
|
'bm-ramdisk')
|
||||||
|
|
||||||
|
def test_create_kernel_and_ramdisk(self):
|
||||||
|
client = mock.MagicMock()
|
||||||
|
client.images.find.side_effect = exceptions.NotFound
|
||||||
|
client.images.create.side_effect = (self.image('aaa'),
|
||||||
|
self.image('zzz'))
|
||||||
|
expected = {'kernel': 'aaa', 'ramdisk': 'zzz'}
|
||||||
|
with tempfile.NamedTemporaryFile() as imagefile:
|
||||||
|
ids = glance.create_or_find_kernel_and_ramdisk(
|
||||||
|
client, 'bm-kernel', 'bm-ramdisk', kernel_path=imagefile.name,
|
||||||
|
ramdisk_path=imagefile.name)
|
||||||
|
kernel_create = mock.call(name='bm-kernel', disk_format='aki',
|
||||||
|
is_public=True, data=mock.ANY)
|
||||||
|
ramdisk_create = mock.call(name='bm-ramdisk', disk_format='ari',
|
||||||
|
is_public=True, data=mock.ANY)
|
||||||
|
client.images.create.assert_has_calls([kernel_create, ramdisk_create])
|
||||||
|
self.assertEqual(expected, ids)
|
@ -31,6 +31,7 @@ console_scripts =
|
|||||||
setup-endpoints = os_cloud_config.cmd.setup_endpoints:main
|
setup-endpoints = os_cloud_config.cmd.setup_endpoints:main
|
||||||
setup-flavors = os_cloud_config.cmd.setup_flavors:main
|
setup-flavors = os_cloud_config.cmd.setup_flavors:main
|
||||||
setup-neutron = os_cloud_config.cmd.setup_neutron:main
|
setup-neutron = os_cloud_config.cmd.setup_neutron:main
|
||||||
|
upload-kernel-ramdisk = os_cloud_config.cmd.upload_kernel_ramdisk:main
|
||||||
|
|
||||||
[build_sphinx]
|
[build_sphinx]
|
||||||
source-dir = doc/source
|
source-dir = doc/source
|
||||||
|
Loading…
Reference in New Issue
Block a user