Skip to content

Commit

Permalink
Add support for Folsom's python-glanceclient
Browse files Browse the repository at this point in the history
Fixes #20

Heat supports both Essex and Folsom for now. However, in Folsom, the use
of python-glance client is deprecated in favor of python-glanceclient.

Since the clients are incompatible, this adds the `heat.glance_client`
package that abstracts the differences away.

Once we stop supporting Essex, most of this should go away.

Signed-off-by: Tomas Sedovic <tomas@sedovic.cz>
  • Loading branch information
tomassedovic committed Oct 12, 2012
1 parent 3f3cc81 commit 3940132
Show file tree
Hide file tree
Showing 2 changed files with 188 additions and 102 deletions.
120 changes: 18 additions & 102 deletions bin/heat-jeos
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,8 @@ import os
import os.path
import sys
import time
import traceback


from glance.common import exception
from glance import client as glance_client
from prettytable import PrettyTable


Expand All @@ -40,6 +38,7 @@ possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
if os.path.exists(os.path.join(possible_topdir, 'heat_jeos')):
sys.path.insert(0, possible_topdir)

from heat_jeos import glance_clients as glance
from heat_jeos.utils import *


Expand Down Expand Up @@ -92,22 +91,26 @@ def command_create(options, arguments):
tdl_path = find_template_by_name(options.jeos_dir, arguments[0])

if not tdl_path:
print 'You must specify a correct template name or path.'
logging.info('You must specify a correct template name or path.')
sys.exit(1)

if os.geteuid() != 0:
logging.error("This command must be run as root")
sys.exit(1)

if options.register_with_glance:
client = get_glance_client(options)
try:
client.get_image('test')
except exception.NotFound:
client = glance.client(options)
glance.get_image(client, 'test')
except glance.NotFoundError:
pass
except Exception:
print("Cannot connect to Glance. Please verify that it's running "
"and check your credentials.")
except glance.ConnectionError:
logging.error("Cannot connect to Glance. Please verify that it's "
"running.")
sys.exit(1)
except glance.AuthError:
logging.error("Cannot authenticate to Keystone, please check your "
"credentials.")
sys.exit(1)

with open(tdl_path, 'r') as f:
Expand Down Expand Up @@ -142,20 +145,20 @@ def command_create(options, arguments):
sys.exit(1)

try:
client = get_glance_client(options)
image = find_image(client, image_name)
client = glance.client(options)
image = glance.find_image_by_name(client, image_name)
if image:
delete_image = options.yes or prompt_bool('Do you want to '
'delete the existing JEOS in glance? (y/n) ')
if delete_image:
client.delete_image(image['id'])
glance.delete_image(client, image)
else:
logging.info('No action taken')
sys.exit(0)
image_id = register_image(client, qcow2_path, image_name,
image_id = glance.register_image(client, qcow2_path, image_name,
options.username, image)
print('\nImage %s was registered with ID %s' % (image_name, image_id))
except exception.ClientConnectionError, e:
except glance.ConnectionError, e:
logging.error('Failed to connect to the Glance API server.')
sys.exit(1)
except Exception, e:
Expand All @@ -166,66 +169,6 @@ def command_create(options, arguments):
sys.exit(1)


def get_glance_client(options):
"""
Returns a new Glance client connection based on the passed options.
"""
creds = dict(username=options.username,
password=options.password,
tenant=options.tenant,
auth_url=options.auth_url,
strategy=options.auth_strategy)

# When neither host nor port are specified and we're using Keystone auth,
# let it tell us the Glance entrypoint
configure_via_auth = (options.auth_strategy == 'keystone' and
not (options.glance_host or options.glance_port))

# NOTE: these are ignored by the client when `configure_via_auth` is True
glance_host = options.glance_host if options.glance_host else '0.0.0.0'
try:
glance_port = int(options.glance_port) if options.glance_port else 9292
except:
logging.error('Port must be a number.')
sys.exit(1)

if configure_via_auth:
logging.debug('Using Glance entry point received by Keystone.')
else:
logging.debug('Connecting to Glance at host: %s, port: %d' %
(glance_host, glance_port))

client = glance_client.Client(host=glance_host,
port=glance_port,
use_ssl=False,
auth_tok=None,
configure_via_auth=configure_via_auth,
creds=creds)
return client


def find_image(client, image_name):
"""
Looks up the image of a given name in Glance.
Returns the image metadata or None if no image is found.
"""
parameters = {
"filters": {},
"limit": 10,
}

images = client.get_images(**parameters)

try:
image = [i for i in images if i['name'] == image_name][0]
except IndexError:
image = None
return image




def prompt_bool(question):
"""
Ask the user a yes/no question and return the answer as a bool.
Expand All @@ -238,33 +181,6 @@ def prompt_bool(question):
return False




def register_image(client, qcow2_path, name, owner, existing_image):
"""
Register the given image with Glance.
"""
image_meta = {'name': name,
'is_public': True,
'disk_format': 'qcow2',
'min_disk': 0,
'min_ram': 0,
'owner': owner,
'container_format': 'bare'}

if existing_image:
client.delete_image(existing_image['id'])

with open(qcow2_path) as ifile:
image_meta = client.add_image(image_meta, ifile)
image_id = image_meta['id']
logging.debug(" Added new image with ID: %s" % image_id)
logging.debug(" Returned the following metadata for the new image:")
for k, v in sorted(image_meta.items()):
logging.debug(" %(k)30s => %(v)s" % locals())
return image_id


def create_options(parser):
"""
Sets up the CLI and config-file options that may be
Expand Down
170 changes: 170 additions & 0 deletions heat_jeos/glance_clients.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
"""
This is a Glance interface that serves as a compatibility layer for python-
glance (used in Essex) and python-glanceclient (Folsom), which have very
different APIs.
Once we stop supporting Essex, this should be removed.
"""

import logging

__all__ = [
'client',
'get_image',
'find_image_by_name',
'create_image',
'delete_image',
'register_image',
'ConnectionError',
'NotFoundError',
'AuthError',
]


def get_glance_client_essex(options):
"""
Returns a new Glance client connection based on the passed options.
"""
creds = dict(username=options.username,
password=options.password,
tenant=options.tenant,
auth_url=options.auth_url,
strategy=options.auth_strategy)

# When neither host nor port are specified and we're using Keystone auth,
# let it tell us the Glance entrypoint
configure_via_auth = (options.auth_strategy == 'keystone' and
not (options.glance_host or options.glance_port))

# NOTE: these are ignored by the client when `configure_via_auth` is True
glance_host = options.glance_host if options.glance_host else '0.0.0.0'
try:
glance_port = int(options.glance_port) if options.glance_port else 9292
except:
logging.error('Port must be a number.')
sys.exit(1)

if configure_via_auth:
logging.debug('Using Glance entry point received by Keystone.')
else:
logging.debug('Connecting to Glance at host: %s, port: %d' %
(glance_host, glance_port))

client = GlanceClient(host=glance_host,
port=glance_port,
use_ssl=False,
auth_tok=None,
configure_via_auth=configure_via_auth,
creds=creds)
return client


def get_glance_client_folsom(options):
creds = dict(username=options.username,
password=options.password,
tenant_name=options.tenant,
auth_url=options.auth_url)
import keystoneclient.v2_0.client
kc = keystoneclient.v2_0.client.Client(**creds)
glance_url = kc.service_catalog.url_for(service_type='image',
endpoint_type='publicURL')
auth_token = kc.auth_token
client = GlanceClient(1, glance_url, token=auth_token)
return client


def get_image_essex(client, image_id):
return client.get_image(image_id)


def get_image_folsom(client, image_id):
return client.images.get(image_id)


def find_image_essex(client, image_name):
"""
Looks up the image of a given name in Glance.
Returns the image metadata or None if no image is found.
"""
images = client.get_images(name=image_name)
if images:
return images[0]
else:
return None


def find_image_folsom(client, image_name):
images = client.images.list(filters={'name': image_name})
try:
return images.next()
except StopIteration:
return None


def delete_image_essex(client, image):
return client.delete_image(image['id'])


def delete_image_folsom(client, image):
return client.images.delete(image)


def create_image_essex(client, image_meta, image_file):
return client.add_image(image_meta, image_file)


def create_image_folsom(client, image_meta, image_file):
image_meta['data'] = image_file
image = client.images.create(**image_meta)
return vars(image)


def register_image(client, qcow2_path, name, owner, existing_image):
"""
Register the given image with Glance.
"""
image_meta = {'name': name,
'is_public': True,
'disk_format': 'qcow2',
'min_disk': 0,
'min_ram': 0,
'owner': owner,
'container_format': 'bare'}

if existing_image:
delete_image(client, existing_image)

with open(qcow2_path) as ifile:
image_meta = create_image(client, image_meta, ifile)
image_id = image_meta['id']
logging.debug(" Added new image with ID: %s" % image_id)
logging.debug(" Returned the following metadata for the new image:")
for k, v in sorted(image_meta.items()):
logging.debug(" %(k)30s => %(v)s" % locals())
return image_id


try:
import glanceclient
from glanceclient.client import Client as GlanceClient
from glanceclient.exc import CommunicationError as ConnectionError
from glanceclient.exc import HTTPNotFound as NotFoundError
from glanceclient.exc import HTTPUnauthorized as AuthError
import keystoneclient.v2_0.client
client = get_glance_client_folsom
get_image = get_image_folsom
find_image_by_name = find_image_folsom
create_image = create_image_folsom
delete_image = delete_image_folsom
except ImportError:
import glance
from glance.client import Client as GlanceClient
from glance.common.exception import ClientConnectionError as ConnectionError
from glance.common.exception import NotFound as NotFoundError
from glance.common.exception import NotAuthenticated as AuthError
client = get_glance_client_essex
get_image = get_image_essex
find_image_by_name = find_image_essex
create_image = create_image_essex
delete_image = delete_image_essex

0 comments on commit 3940132

Please sign in to comment.