Use glance public API as opposed to registry API.

Fixes bug 1064008

The motivation for switching over to the public API:

 - the registry API is more of an artifact of the internal glance
   architecture than a true public API

 - the registry API exposes internal attributes such as the backend
   store image location, which is stripped from the API-provided image
   representation for security reasons (hence should not bleed into
   metering)

 - the internalURL should be available from the keystone service catalog,
   so we can get rid of the registry host/port config

Change-Id: I0d75392066a020c608a67f1ecf0e8dba15aa48f0
This commit is contained in:
Eoghan Glynn 2012-10-08 18:26:32 +00:00
parent 6bc3a13d93
commit c01c66c10f
3 changed files with 80 additions and 84 deletions

View File

@ -22,49 +22,45 @@ from __future__ import absolute_import
import itertools import itertools
import glanceclient
from keystoneclient.v2_0 import client as ksclient from keystoneclient.v2_0 import client as ksclient
from glance.registry import client
from ceilometer import plugin from ceilometer import plugin
from ceilometer import counter from ceilometer import counter
from ceilometer.openstack.common import cfg from ceilometer.openstack.common import cfg
from ceilometer.openstack.common import timeutils from ceilometer.openstack.common import timeutils
cfg.CONF.register_opts(
[
cfg.StrOpt('glance_registry_host',
default='localhost',
help="URL of Glance API server"),
cfg.IntOpt('glance_registry_port',
default=9191,
help="URL of Glance API server"),
])
class _Base(plugin.PollsterBase): class _Base(plugin.PollsterBase):
@staticmethod @staticmethod
def get_registry_client(): def get_glance_client():
k = ksclient.Client(username=cfg.CONF.os_username, k = ksclient.Client(username=cfg.CONF.os_username,
password=cfg.CONF.os_password, password=cfg.CONF.os_password,
tenant_id=cfg.CONF.os_tenant_id, tenant_id=cfg.CONF.os_tenant_id,
tenant_name=cfg.CONF.os_tenant_name, tenant_name=cfg.CONF.os_tenant_name,
auth_url=cfg.CONF.os_auth_url) auth_url=cfg.CONF.os_auth_url)
return client.RegistryClient(cfg.CONF.glance_registry_host,
cfg.CONF.glance_registry_port, endpoint = k.service_catalog.url_for(service_type='image',
auth_tok=k.auth_token) endpoint_type='internalURL')
# hard-code v1 glance API version selection while v2 API matures
return glanceclient.Client('1', endpoint, token=k.auth_token)
def iter_images(self): def iter_images(self):
"""Iterate over all images.""" """Iterate over all images."""
# We need to ask for both public and non public to get all images. client = self.get_glance_client()
client = self.get_registry_client() #TODO(eglynn): use pagination to protect against unbounded
# memory usage
return itertools.chain( return itertools.chain(
client.get_images_detailed(filters={"is_public": True}), client.images.list(filters={"is_public": True}),
client.get_images_detailed(filters={"is_public": False})) #TODO(eglynn): extend glance API with all_tenants logic to
# avoid second call to retrieve private images
client.images.list(filters={"is_public": False}))
@staticmethod @staticmethod
def extract_image_metadata(image): def extract_image_metadata(image):
return dict([(k, image[k]) return dict((k, getattr(image, k))
for k in [ for k in [
"status", "status",
"is_public", "is_public",
@ -77,13 +73,12 @@ class _Base(plugin.PollsterBase):
"properties", "properties",
"min_disk", "min_disk",
"protected", "protected",
"location",
"checksum", "checksum",
"deleted_at", "deleted_at",
"min_ram", "min_ram",
"size", "size",
] ]
]) )
class ImagePollster(_Base): class ImagePollster(_Base):
@ -96,8 +91,8 @@ class ImagePollster(_Base):
type=counter.TYPE_GAUGE, type=counter.TYPE_GAUGE,
volume=1, volume=1,
user_id=None, user_id=None,
project_id=image['owner'], project_id=image.owner,
resource_id=image['id'], resource_id=image.id,
timestamp=timeutils.isotime(), timestamp=timeutils.isotime(),
resource_metadata=self.extract_image_metadata(image), resource_metadata=self.extract_image_metadata(image),
) )
@ -111,10 +106,10 @@ class ImageSizePollster(_Base):
source='?', source='?',
name='image.size', name='image.size',
type=counter.TYPE_GAUGE, type=counter.TYPE_GAUGE,
volume=image['size'], volume=image.size,
user_id=None, user_id=None,
project_id=image['owner'], project_id=image.owner,
resource_id=image['id'], resource_id=image.id,
timestamp=timeutils.isotime(), timestamp=timeutils.isotime(),
resource_metadata=self.extract_image_metadata(image), resource_metadata=self.extract_image_metadata(image),
) )

View File

@ -32,8 +32,6 @@ Parameter Default Note
========================== ==================================== ============================================================== ========================== ==================================== ==============================================================
nova_control_exchange nova Exchange name for Nova notifications nova_control_exchange nova Exchange name for Nova notifications
glance_control_exchange glance_notifications Exchange name for Glance notifications glance_control_exchange glance_notifications Exchange name for Glance notifications
glance_registry_host localhost URL of Glance API server
glance_registry_port 9191 port of Glance API server
cinder_control_exchange cinder Exchange name for Cinder notifications cinder_control_exchange cinder Exchange name for Cinder notifications
quantum_control_exchange quantum Exchange name for Quantum notifications quantum_control_exchange quantum Exchange name for Quantum notifications
metering_secret change this or be hacked Secret value for signing metering messages metering_secret change this or be hacked Secret value for signing metering messages

View File

@ -23,60 +23,63 @@ from ceilometer.openstack.common import context
IMAGE_LIST = [ IMAGE_LIST = [
{u'status': u'queued', type('Image', (object,),
u'name': "some name", {u'status': u'queued',
u'deleted': False, u'name': "some name",
u'container_format': None, u'deleted': False,
u'created_at': u'2012-09-18T16:29:46', u'container_format': None,
u'disk_format': None, u'created_at': u'2012-09-18T16:29:46',
u'updated_at': u'2012-09-18T16:29:46', u'disk_format': None,
u'properties': {}, u'updated_at': u'2012-09-18T16:29:46',
u'min_disk': 0, u'properties': {},
u'protected': False, u'min_disk': 0,
u'id': u'1d21a8d0-25f4-4e0a-b4ec-85f40237676b', u'protected': False,
u'location': None, u'id': u'1d21a8d0-25f4-4e0a-b4ec-85f40237676b',
u'checksum': None, u'location': None,
u'owner': u'4c8364fc20184ed7971b76602aa96184', u'checksum': None,
u'is_public': True, u'owner': u'4c8364fc20184ed7971b76602aa96184',
u'deleted_at': None, u'is_public': True,
u'min_ram': 0, u'deleted_at': None,
u'size': 2048}, u'min_ram': 0,
{u'status': u'active', u'size': 2048}),
u'name': "hello world", type('Image', (object,),
u'deleted': False, {u'status': u'active',
u'container_format': None, u'name': "hello world",
u'created_at': u'2012-09-18T16:27:41', u'deleted': False,
u'disk_format': None, u'container_format': None,
u'updated_at': u'2012-09-18T16:27:41', u'created_at': u'2012-09-18T16:27:41',
u'properties': {}, u'disk_format': None,
u'min_disk': 0, u'updated_at': u'2012-09-18T16:27:41',
u'protected': False, u'properties': {},
u'id': u'22be9f90-864d-494c-aa74-8035fd535989', u'min_disk': 0,
u'location': None, u'protected': False,
u'checksum': None, u'id': u'22be9f90-864d-494c-aa74-8035fd535989',
u'owner': u'9e4f98287a0246daa42eaf4025db99d4', u'location': None,
u'is_public': True, u'checksum': None,
u'deleted_at': None, u'owner': u'9e4f98287a0246daa42eaf4025db99d4',
u'min_ram': 0, u'is_public': True,
u'size': 0}, u'deleted_at': None,
{u'status': u'queued', u'min_ram': 0,
u'name': None, u'size': 0}),
u'deleted': False, type('Image', (object,),
u'container_format': None, {u'status': u'queued',
u'created_at': u'2012-09-18T16:23:27', u'name': None,
u'disk_format': "raw", u'deleted': False,
u'updated_at': u'2012-09-18T16:23:27', u'container_format': None,
u'properties': {}, u'created_at': u'2012-09-18T16:23:27',
u'min_disk': 0, u'disk_format': "raw",
u'protected': False, u'updated_at': u'2012-09-18T16:23:27',
u'id': u'8d133f6c-38a8-403c-b02c-7071b69b432d', u'properties': {},
u'location': None, u'min_disk': 0,
u'checksum': None, u'protected': False,
u'owner': u'5f8806a76aa34ee8b8fc8397bd154319', u'id': u'8d133f6c-38a8-403c-b02c-7071b69b432d',
u'is_public': True, u'location': None,
u'deleted_at': None, u'checksum': None,
u'min_ram': 0, u'owner': u'5f8806a76aa34ee8b8fc8397bd154319',
u'size': 1024}, u'is_public': True,
u'deleted_at': None,
u'min_ram': 0,
u'size': 1024}),
] ]
@ -106,5 +109,5 @@ class TestImagePollster(base.TestCase):
self.assertEqual(len(counters), 3) self.assertEqual(len(counters), 3)
for image in IMAGE_LIST: for image in IMAGE_LIST:
self.assert_( self.assert_(
any(map(lambda counter: counter.volume == image['size'], any(map(lambda counter: counter.volume == image.size,
counters))) counters)))