diff options
Diffstat (limited to 'ironic/common')
-rw-r--r-- | ironic/common/exception.py | 4 | ||||
-rw-r--r-- | ironic/common/keystone.py | 51 | ||||
-rw-r--r-- | ironic/common/swift.py | 187 |
3 files changed, 235 insertions, 7 deletions
diff --git a/ironic/common/exception.py b/ironic/common/exception.py index 735f1ce1c..aeec784dc 100644 --- a/ironic/common/exception.py +++ b/ironic/common/exception.py @@ -445,3 +445,7 @@ class InsufficientDiskSpace(IronicException): class ImageCreationFailed(IronicException): message = _('Creating %(image_type)s image failed: %(error)s') + + +class SwiftOperationError(IronicException): + message = _("Swift operation '%(operation)s' failed: %(error)s") diff --git a/ironic/common/keystone.py b/ironic/common/keystone.py index c197c231a..8f785ac1c 100644 --- a/ironic/common/keystone.py +++ b/ironic/common/keystone.py @@ -23,24 +23,61 @@ from ironic.common import exception CONF = cfg.CONF +def _is_apiv3(auth_url, auth_version): + """Checks if V3 version of API is being used or not. + + This method inspects auth_url and auth_version, and checks whether V3 + version of the API is being used or not. + + :param auth_url: a http or https url to be inspected(like + 'http://127.0.0.1:9898/'). + :param auth_version: a string containing the version(like 'v2', 'v3.0') + :returns: True if V3 of the API is being used. + """ + return auth_version == 'v3.0' or '/v3' in parse.urlparse(auth_url).path + + +def get_keystone_url(auth_url, auth_version): + """Gives an http/https url to contact keystone. + + Given an auth_url and auth_version, this method generates the url in + which keystone can be reached. + + :param auth_url: a http or https url to be inspected(like + 'http://127.0.0.1:9898/'). + :param auth_version: a string containing the version(like v2, v3.0, etc) + :returns: a string containing the keystone url + """ + api_v3 = _is_apiv3(auth_url, auth_version) + api_version = 'v3' if api_v3 else 'v2.0' + # NOTE(lucasagomes): Get rid of the trailing '/' otherwise urljoin() + # fails to override the version in the URL + return parse.urljoin(auth_url.rstrip('/'), api_version) + + def get_service_url(service_type='baremetal', endpoint_type='internal'): - """Wrapper for get service url from keystone service catalog.""" + """Wrapper for get service url from keystone service catalog. + + Given a service_type and an endpoint_type, this method queries keystone + service catalog and provides the url for the desired endpoint. + + :param service_type: the keystone service for which url is required. + :param endpoint_type: the type of endpoint for the service. + :returns: an http/https url for the desired endpoint. + """ auth_url = CONF.keystone_authtoken.auth_uri if not auth_url: raise exception.CatalogFailure(_('Keystone API endpoint is missing')) - api_v3 = CONF.keystone_authtoken.auth_version == 'v3.0' or \ - 'v3' in parse.urlparse(auth_url).path + auth_version = CONF.keystone_authtoken.auth_version + api_v3 = _is_apiv3(auth_url, auth_version) if api_v3: from keystoneclient.v3 import client else: from keystoneclient.v2_0 import client - api_version = 'v3' if api_v3 else 'v2.0' - # NOTE(lucasagomes): Get rid of the trailing '/' otherwise urljoin() - # fails to override the version in the URL - auth_url = parse.urljoin(auth_url.rstrip('/'), api_version) + auth_url = get_keystone_url(auth_url, auth_version) try: ksclient = client.Client(username=CONF.keystone_authtoken.admin_user, password=CONF.keystone_authtoken.admin_password, diff --git a/ironic/common/swift.py b/ironic/common/swift.py new file mode 100644 index 000000000..c5f0e9f8c --- /dev/null +++ b/ironic/common/swift.py @@ -0,0 +1,187 @@ +# +# Copyright 2014 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. + +from oslo.config import cfg +from six.moves.urllib import parse +from swiftclient import client as swift_client +from swiftclient import exceptions as swift_exceptions +from swiftclient import utils as swift_utils + +from ironic.common import exception +from ironic.common import keystone +from ironic.openstack.common import log as logging + +swift_opts = [ + cfg.IntOpt('swift_max_retries', + default=2, + help='Maximum number of times to retry a Swift request, ' + 'before failing.') + ] + + +CONF = cfg.CONF +CONF.register_opts(swift_opts, group='swift') + +CONF.import_opt('admin_user', 'keystonemiddleware.auth_token', + group='keystone_authtoken') +CONF.import_opt('admin_tenant_name', 'keystonemiddleware.auth_token', + group='keystone_authtoken') +CONF.import_opt('admin_password', 'keystonemiddleware.auth_token', + group='keystone_authtoken') +CONF.import_opt('auth_uri', 'keystonemiddleware.auth_token', + group='keystone_authtoken') +CONF.import_opt('auth_version', 'keystonemiddleware.auth_token', + group='keystone_authtoken') +CONF.import_opt('insecure', 'keystonemiddleware.auth_token', + group='keystone_authtoken') + +LOG = logging.getLogger(__name__) + + +class SwiftAPI(object): + """API for communicating with Swift.""" + + def __init__(self, + user=CONF.keystone_authtoken.admin_user, + tenant_name=CONF.keystone_authtoken.admin_tenant_name, + key=CONF.keystone_authtoken.admin_password, + auth_url=CONF.keystone_authtoken.auth_uri, + auth_version=CONF.keystone_authtoken.auth_version): + """Constructor for creating a SwiftAPI object. + + :param user: the name of the user for Swift account + :param tenant_name: the name of the tenant for Swift account + :param key: the 'password' or key to authenticate with + :param auth_url: the url for authentication + :param auth_version: the version of api to use for authentication + """ + auth_url = keystone.get_keystone_url(auth_url, auth_version) + params = {'retries': CONF.swift.swift_max_retries, + 'insecure': CONF.keystone_authtoken.insecure, + 'user': user, + 'tenant_name': tenant_name, + 'key': key, + 'authurl': auth_url, + 'auth_version': auth_version} + + self.connection = swift_client.Connection(**params) + + def create_object(self, container, object, filename, + object_headers=None): + """Uploads a given file to Swift. + + :param container: The name of the container for the object. + :param object: The name of the object in Swift + :param filename: The file to upload, as the object data + :param object_headers: the headers for the object to pass to Swift + :returns: The Swift UUID of the object + :raises: SwiftOperationError, if any operation with Swift fails. + """ + try: + self.connection.put_container(container) + except swift_exceptions.ClientException as e: + operation = _("put container") + raise exception.SwiftOperationError(operation=operation, error=e) + + with open(filename, "r") as fileobj: + + try: + obj_uuid = self.connection.put_object(container, + object, + fileobj, + headers=object_headers) + except swift_exceptions.ClientException as e: + operation = _("put object") + raise exception.SwiftOperationError(operation=operation, + error=e) + + return obj_uuid + + def get_temp_url(self, container, object, timeout): + """Returns the temp url for the given Swift object. + + :param container: The name of the container in which Swift object + is placed. + :param object: The name of the Swift object. + :param timeout: The timeout in seconds after which the generated url + should expire. + :returns: The temp url for the object. + :raises: SwiftOperationError, if any operation with Swift fails. + """ + try: + account_info = self.connection.head_account() + except swift_exceptions.ClientException as e: + operation = _("head account") + raise exception.SwiftOperationError(operation=operation, + error=e) + + storage_url, token = self.connection.get_auth() + parse_result = parse.urlparse(storage_url) + swift_object_path = '/'.join((parse_result.path, container, object)) + temp_url_key = account_info['x-account-meta-temp-url-key'] + url_path = swift_utils.generate_temp_url(swift_object_path, timeout, + temp_url_key, 'GET') + return parse.urlunparse((parse_result.scheme, + parse_result.netloc, + url_path, + None, + None, + None)) + + def delete_object(self, container, object): + """Deletes the given Swift object. + + :param container: The name of the container in which Swift object + is placed. + :param object: The name of the object in Swift to be deleted. + :raises: SwiftOperationError, if operation with Swift fails. + """ + try: + self.connection.delete_object(container, object) + except swift_exceptions.ClientException as e: + operation = _("delete object") + raise exception.SwiftOperationError(operation=operation, error=e) + + def head_object(self, container, object): + """Retrieves the information about the given Swift object. + + :param container: The name of the container in which Swift object + is placed. + :param object: The name of the object in Swift + :returns: The information about the object as returned by + Swift client's head_object call. + :raises: SwiftOperationError, if operation with Swift fails. + """ + try: + return self.connection.head_object(container, object) + except swift_exceptions.ClientException as e: + operation = _("head object") + raise exception.SwiftOperationError(operation=operation, error=e) + + def update_object_meta(self, container, object, object_headers): + """Update the metadata of a given Swift object. + + :param container: The name of the container in which Swift object + is placed. + :param object: The name of the object in Swift + :param object_headers: the headers for the object to pass to Swift + :raises: SwiftOperationError, if operation with Swift fails. + """ + try: + self.connection.post_object(container, object, object_headers) + except swift_exceptions.ClientException as e: + operation = _("post object") + raise exception.SwiftOperationError(operation=operation, error=e) |