initial pull from rcbops-cookbooks/glance, monitoring removed
This commit is contained in:
		
							
								
								
									
										86
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										86
									
								
								README.md
									
									
									
									
									
								
							@@ -1,17 +1,98 @@
 | 
			
		||||
Description
 | 
			
		||||
===========
 | 
			
		||||
Placeholder for the OpenStack Image service `Glance` as part of the OpenStack `Essex` reference architecture using Chef. The http://github.com/opscode/openstack-chef-repo will contain documentation for using this cookbook in the context of a full OpenStack deployment.
 | 
			
		||||
 | 
			
		||||
Sharing the Nova database will be preferred for simplicity.
 | 
			
		||||
This cookbook installs the OpenStack Image service **Glance** as part of the OpenStack **Essex** reference deployment Chef for OpenStack. The http://github.com/opscode/openstack-chef-repo contains documentation for using this cookbook in the context of a full OpenStack deployment. Glance is installed from packages, optionally populating the repository with default images.
 | 
			
		||||
 | 
			
		||||
http://glance.openstack.org/
 | 
			
		||||
 | 
			
		||||
Requirements
 | 
			
		||||
============
 | 
			
		||||
 | 
			
		||||
Chef 0.10.0 or higher required (for Chef environment use)
 | 
			
		||||
 | 
			
		||||
Platform
 | 
			
		||||
--------
 | 
			
		||||
 | 
			
		||||
* Ubuntu-12.04
 | 
			
		||||
* Fedora-17
 | 
			
		||||
 | 
			
		||||
Cookbooks
 | 
			
		||||
---------
 | 
			
		||||
 | 
			
		||||
The following cookbooks are dependencies:
 | 
			
		||||
 | 
			
		||||
* database
 | 
			
		||||
* mysql
 | 
			
		||||
* keystone
 | 
			
		||||
* osops-utils
 | 
			
		||||
 | 
			
		||||
Recipes
 | 
			
		||||
=======
 | 
			
		||||
 | 
			
		||||
default
 | 
			
		||||
-------
 | 
			
		||||
-Includes recipes `api`, `registry`
 | 
			
		||||
 | 
			
		||||
api
 | 
			
		||||
------
 | 
			
		||||
-Installs the glance-api server
 | 
			
		||||
 | 
			
		||||
registry
 | 
			
		||||
--------
 | 
			
		||||
-Includes recipe `mysql:client`
 | 
			
		||||
-Installs the glance-registry server
 | 
			
		||||
 | 
			
		||||
Attributes
 | 
			
		||||
==========
 | 
			
		||||
 | 
			
		||||
* `glance["db"]["name"]` - Name of glance database
 | 
			
		||||
* `glance["db"]["user"]` - Username for glance database access
 | 
			
		||||
* `glance["db"]["password"]` - Password for glance database access
 | 
			
		||||
* `glance["api"]["ip_address"]` - IP address to use for communicating with the glance API
 | 
			
		||||
* `glance["api"]["bind_address"]` - IP address for the glance API to bind to
 | 
			
		||||
* `glance["api"]["port"]` - Port for the glance API to bind to
 | 
			
		||||
* `glance["api"]["adminURL"]` - Used when registering image endpoint with keystone
 | 
			
		||||
* `glance["api"]["internalURL"]` - Used when registering image endpoint with keystone
 | 
			
		||||
* `glance["api"]["publicURL"]` - Used when registering image endpoint with keystone
 | 
			
		||||
* `glance["registry"]["ip_address"]` - IP address to use for communicating with the glance registry
 | 
			
		||||
* `glance["registry"]["bind_address"]` - IP address for the glance registry to bind to
 | 
			
		||||
* `glance["registry"]["port"]` - IP address for the glance port to bind to
 | 
			
		||||
* `glance["service_tenant_name"]` - Tenant name used by glance when interacting with keystone - used in the API and registry paste.ini files
 | 
			
		||||
* `glance["service_user"]` - User name used by glance when interacting with keystone - used in the API and registry paste.ini files
 | 
			
		||||
* `glance["service_pass"]` - User password used by glance when interacting with keystone - used in the API and registry paste.ini files
 | 
			
		||||
* `glance["service_role"]` - User role used by glance when interacting with keystone - used in the API and registry paste.ini files
 | 
			
		||||
* `glance["image_upload"]` - Toggles whether to automatically upload images in the `glance["images"]` array
 | 
			
		||||
* `glance["images"]` - Default list of images to upload to the glance repository as part of the install
 | 
			
		||||
* `glance["image]["<imagename>"]` - URL location of the <imagename> image. There can be multiple instances of this line to define multiple imagess (eg natty, maverick, fedora17 etc)
 | 
			
		||||
--- example `glance["image]["natty"]` - "http://c250663.r63.cf1.rackcdn.com/ubuntu-11.04-server-uec-amd64-multinic.tar.gz"
 | 
			
		||||
* `glance["api"]["default_store"]` - Toggles the backend storage type.  Currently supported is "file" and "swift"
 | 
			
		||||
* `glance["api"]["swift"]["store_container"] - Set the container used by glance to store images and snapshots.  Defaults to "glance"
 | 
			
		||||
* `glance["api"]["swift"]["store_large_object_size"] - Set the size at which glance starts to chunnk files.  Defaults to "200" MB
 | 
			
		||||
* `glance["api"]["swift"]["store_large_object_chunk_size"] - Set the chunk size for glance.  Defaults to "200" MB
 | 
			
		||||
 | 
			
		||||
Templates
 | 
			
		||||
=========
 | 
			
		||||
 | 
			
		||||
* `glance-api-paste.ini.erb` - Paste config for glance-api middleware
 | 
			
		||||
* `glance-api.conf.erb` - Config file for glance-api server
 | 
			
		||||
* `glance-registry-paste.ini.erb` - Paste config for glance-registry middleware
 | 
			
		||||
* `glance-registry.conf.erb` - Config file for glance-registry server
 | 
			
		||||
* `glance-scrubber.conf.erb` - Config file for glance image scrubber service
 | 
			
		||||
* `policy.json.erb` - Configuration of ACLs for glance API server
 | 
			
		||||
 | 
			
		||||
License and Author
 | 
			
		||||
==================
 | 
			
		||||
 | 
			
		||||
Author:: Justin Shepherd (<justin.shepherd@rackspace.com>)
 | 
			
		||||
Author:: Jason Cannavale (<jason.cannavale@rackspace.com>)
 | 
			
		||||
Author:: Ron Pedde (<ron.pedde@rackspace.com>)
 | 
			
		||||
Author:: Joseph Breu (<joseph.breu@rackspace.com>)
 | 
			
		||||
Author:: William Kelly (<william.kelly@rackspace.com>)
 | 
			
		||||
Author:: Darren Birkett (<darren.birkett@rackspace.co.uk>)
 | 
			
		||||
Author:: Evan Callicoat (<evan.callicoat@rackspace.com>)
 | 
			
		||||
Author:: Matt Ray (<matt@opscode.com>)
 | 
			
		||||
 | 
			
		||||
Copyright 2012 Rackspace, Inc.
 | 
			
		||||
Copyright 2012 Opscode, Inc.
 | 
			
		||||
 | 
			
		||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
@@ -25,4 +106,3 @@ 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.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										79
									
								
								attributes/default.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								attributes/default.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,79 @@
 | 
			
		||||
#
 | 
			
		||||
# Cookbook Name:: glance
 | 
			
		||||
# Attributes:: glance
 | 
			
		||||
#
 | 
			
		||||
# Copyright 2009, Rackspace Hosting, 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.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
########################################################################
 | 
			
		||||
# Toggles - These can be overridden at the environment level
 | 
			
		||||
default["enable_monit"] = false  # OS provides packages
 | 
			
		||||
default["developer_mode"] = true  # we want secure passwords by default
 | 
			
		||||
########################################################################
 | 
			
		||||
 | 
			
		||||
default["glance"]["services"]["api"]["scheme"] = "http"
 | 
			
		||||
default["glance"]["services"]["api"]["network"] = "public"
 | 
			
		||||
default["glance"]["services"]["api"]["port"] = 9292
 | 
			
		||||
default["glance"]["services"]["api"]["path"] = "/v1"
 | 
			
		||||
 | 
			
		||||
default["glance"]["services"]["registry"]["scheme"] = "http"
 | 
			
		||||
default["glance"]["services"]["registry"]["network"] = "public"
 | 
			
		||||
default["glance"]["services"]["registry"]["port"] = 9191
 | 
			
		||||
default["glance"]["services"]["registry"]["path"] = "/v1"
 | 
			
		||||
 | 
			
		||||
default["glance"]["db"]["name"] = "glance"
 | 
			
		||||
default["glance"]["db"]["username"] = "glance"
 | 
			
		||||
 | 
			
		||||
# TODO: These may need to be glance-registry specific.. and looked up by glance-api
 | 
			
		||||
default["glance"]["service_tenant_name"] = "service"
 | 
			
		||||
default["glance"]["service_user"] = "glance"
 | 
			
		||||
default["glance"]["service_role"] = "admin"
 | 
			
		||||
default["glance"]["api"]["default_store"] = "file"
 | 
			
		||||
default["glance"]["api"]["swift"]["store_container"] = "glance"
 | 
			
		||||
default["glance"]["api"]["swift"]["store_large_object_size"] = "200"
 | 
			
		||||
default["glance"]["api"]["swift"]["store_large_object_chunk_size"] = "200"
 | 
			
		||||
 | 
			
		||||
default["glance"]["image_upload"] = false
 | 
			
		||||
default["glance"]["images"] = [ "tty" ]
 | 
			
		||||
default["glance"]["image"]["oneiric"] = "http://c250663.r63.cf1.rackcdn.com/ubuntu-11.10-server-uec-amd64-multinic.tar.gz"
 | 
			
		||||
default["glance"]["image"]["natty"] = "http://c250663.r63.cf1.rackcdn.com/ubuntu-11.04-server-uec-amd64-multinic.tar.gz"
 | 
			
		||||
default["glance"]["image"]["maverick"] = "http://c250663.r63.cf1.rackcdn.com/ubuntu-10.10-server-uec-amd64-multinic.tar.gz"
 | 
			
		||||
#default["glance"]["image"]["tty"] = "http://smoser.brickies.net/ubuntu/ttylinux-uec/ttylinux-uec-amd64-12.1_2.6.35-22_1.tar.gz"
 | 
			
		||||
default["glance"]["image"]["tty"] = "http://c250663.r63.cf1.rackcdn.com/ttylinux.tgz"
 | 
			
		||||
default["glance"]["image"]["cirros"] = "https://launchpadlibrarian.net/83305869/cirros-0.3.0-x86_64-uec.tar.gz"
 | 
			
		||||
 | 
			
		||||
# logging attribute
 | 
			
		||||
default["glance"]["syslog"]["use"] = true
 | 
			
		||||
default["glance"]["syslog"]["facility"] = "LOG_LOCAL2"
 | 
			
		||||
 | 
			
		||||
# platform-specific settings
 | 
			
		||||
case platform
 | 
			
		||||
when "fedora"
 | 
			
		||||
  default["glance"]["platform"] = {
 | 
			
		||||
    "mysql_python_packages" => [ "MySQL-python" ],
 | 
			
		||||
    "glance_packages" => [ "openstack-glance", "openstack-swift" ],
 | 
			
		||||
    "glance_api_service" => "openstack-glance-api",
 | 
			
		||||
    "glance_registry_service" => "openstack-glance-registry",
 | 
			
		||||
    "package_overrides" => ""
 | 
			
		||||
  }
 | 
			
		||||
when "ubuntu"
 | 
			
		||||
  default["glance"]["platform"] = {
 | 
			
		||||
    "mysql_python_packages" => [ "python-mysqldb" ],
 | 
			
		||||
    "glance_packages" => [ "glance", "python-swift" ],
 | 
			
		||||
    "glance_api_service" => "glance-api",
 | 
			
		||||
    "glance_registry_service" => "glance-registry",
 | 
			
		||||
    "package_overrides" => "-o Dpkg::Options::='--force-confold' -o Dpkg::Options::='--force-confdef'"
 | 
			
		||||
  }
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										116
									
								
								files/default/glance_plugin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								files/default/glance_plugin.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,116 @@
 | 
			
		||||
from glance.client import V1Client
 | 
			
		||||
from glance.common import exception
 | 
			
		||||
 | 
			
		||||
import collectd
 | 
			
		||||
 | 
			
		||||
global NAME, OS_USERNAME, OS_PASSWORD, OS_TENANT_NAME, OS_AUTH_URL, OS_AUTH_STRATEGY, VERBOSE_LOGGING
 | 
			
		||||
 | 
			
		||||
OS_USERNAME = "username"
 | 
			
		||||
OS_PASSWORD = "password"
 | 
			
		||||
OS_TENANT_NAME = "tenantname"
 | 
			
		||||
OS_AUTH_URL = "http://localhost:5000/v2.0"
 | 
			
		||||
OS_AUTH_STRATEGY = "keystone"
 | 
			
		||||
VERBOSE_LOGGING = False
 | 
			
		||||
 | 
			
		||||
def get_stats(user, passwd, tenant, url, host=None):
 | 
			
		||||
    creds = {"username": user, "password": passwd, "tenant": tenant,"auth_url": url, "strategy": OS_AUTH_STRATEGY}
 | 
			
		||||
    client = V1Client(host,creds=creds)
 | 
			
		||||
    try:
 | 
			
		||||
        image_list = client.get_images_detailed()
 | 
			
		||||
    except exception.NotAuthenticated:
 | 
			
		||||
        msg = "Client credentials appear to be invalid"
 | 
			
		||||
        raise exception.ClientConnectionError(msg)
 | 
			
		||||
    else:
 | 
			
		||||
        # TODO(shep): this needs to be rewritten more inline with the keystone|nova plugins
 | 
			
		||||
        data = dict()
 | 
			
		||||
        data["count"] = int(len(image_list))
 | 
			
		||||
        data["bytes"] = 0
 | 
			
		||||
        data["snapshot.count"] = 0
 | 
			
		||||
        data["snapshot.bytes"] = 0
 | 
			
		||||
        data["tenant"] = dict()
 | 
			
		||||
        for image in image_list:
 | 
			
		||||
            data["bytes"] += int(image["size"])
 | 
			
		||||
            if "image_type" in image["properties"] and image["properties"]["image_type"] == "snapshot":
 | 
			
		||||
                data["snapshot.count"] += 1
 | 
			
		||||
                data["snapshot.bytes"] += int(image["size"])
 | 
			
		||||
            uuid = str(image["owner"])
 | 
			
		||||
            if uuid in data["tenant"]:
 | 
			
		||||
                data["tenant"][uuid]["count"] += 1
 | 
			
		||||
                data["tenant"][uuid]["bytes"] += int(image["size"])
 | 
			
		||||
                if "image_type" in image["properties"] and image["properties"]["image_type"] == "snapshot":
 | 
			
		||||
                    data["tenant"][uuid]["snapshot.count"] += 1
 | 
			
		||||
                    data["tenant"][uuid]["snapshot.bytes"] += int(image["size"])
 | 
			
		||||
            else:
 | 
			
		||||
                data["tenant"][uuid] = dict()
 | 
			
		||||
                data["tenant"][uuid]["count"] = 1
 | 
			
		||||
                data["tenant"][uuid]["bytes"] = int(image["size"])
 | 
			
		||||
                data["tenant"][uuid]["snapshot.count"] = 0
 | 
			
		||||
                data["tenant"][uuid]["snapshot.bytes"] = 0
 | 
			
		||||
                if "image_type" in image["properties"] and image["properties"]["image_type"] == "snapshot":
 | 
			
		||||
                    data["tenant"][uuid]["snapshot.count"] += 1
 | 
			
		||||
                    data["tenant"][uuid]["snapshot.bytes"] += int(image["size"])
 | 
			
		||||
        # debug
 | 
			
		||||
        #for key in data.keys():
 | 
			
		||||
        #    if key == "tenant":
 | 
			
		||||
        #        for uuid in data[key].keys():
 | 
			
		||||
        #            for field in data[key][uuid]:
 | 
			
		||||
        #                print "glance.images.tenant.%s.%s : %i" % (uuid, field, data[key][uuid][field])
 | 
			
		||||
        #    else:
 | 
			
		||||
        #        print "glance.images.%s : %i" % (key, data[key])
 | 
			
		||||
        ##########
 | 
			
		||||
        return data
 | 
			
		||||
 | 
			
		||||
def configure_callback(conf):
 | 
			
		||||
    """Received configuration information"""
 | 
			
		||||
    global OS_USERNAME, OS_PASSWORD, OS_TENANT_NAME, OS_AUTH_URL
 | 
			
		||||
    for node in conf.children:
 | 
			
		||||
        if node.key == "Username":
 | 
			
		||||
            OS_USERNAME = node.values[0]
 | 
			
		||||
        elif node.key == "Password":
 | 
			
		||||
            OS_PASSWORD = node.values[0]
 | 
			
		||||
        elif node.key == "TenantName":
 | 
			
		||||
            OS_TENANT_NAME = node.values[0]
 | 
			
		||||
        elif node.key == "AuthURL":
 | 
			
		||||
            OS_AUTH_URL = node.values[0]
 | 
			
		||||
        elif node.key == "Verbose":
 | 
			
		||||
            VERBOSE_LOGGING = node.values[0]
 | 
			
		||||
        else:
 | 
			
		||||
            logger("warn", "Unknown config key: %s" % node.key)
 | 
			
		||||
 | 
			
		||||
def read_callback():
 | 
			
		||||
    logger("verb", "read_callback")
 | 
			
		||||
    info = get_stats(OS_USERNAME, OS_PASSWORD, OS_TENANT_NAME, OS_AUTH_URL)
 | 
			
		||||
 | 
			
		||||
    if not info:
 | 
			
		||||
        logger("err", "No information received")
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    for key in info.keys():
 | 
			
		||||
        if key == "tenant":
 | 
			
		||||
            for uuid in info[key].keys():
 | 
			
		||||
                for field in info[key][uuid]:
 | 
			
		||||
                    logger('verb', 'Dispatching glance.images.tenant.%s.%s : %i' % (uuid, field, int(info[key][uuid][field])))
 | 
			
		||||
                    path = 'glance.images.%s.%s' % (uuid, field)
 | 
			
		||||
                    val = collectd.Values(plugin=path)
 | 
			
		||||
                    val.type = 'gauge'
 | 
			
		||||
                    val.values = [int(info[key][uuid][field])]
 | 
			
		||||
                    val.dispatch()
 | 
			
		||||
        else:
 | 
			
		||||
            logger('verb', 'Dispatching %s : %i' % (key, int(info[key])))
 | 
			
		||||
            path = 'glance.images.%s' % (key)
 | 
			
		||||
            val = collectd.Values(plugin=path)
 | 
			
		||||
            val.type = 'gauge'
 | 
			
		||||
            val.values = [int(info[key])]
 | 
			
		||||
            val.dispatch()
 | 
			
		||||
 | 
			
		||||
def logger(t, msg):
 | 
			
		||||
    if t == 'err':
 | 
			
		||||
        collectd.error('%s: %s' % (NAME, msg))
 | 
			
		||||
    if t == 'warn':
 | 
			
		||||
        collectd.warning('%s: %s' % (NAME, msg))
 | 
			
		||||
    elif t == 'verb' and VERBOSE_LOGGING == True:
 | 
			
		||||
        collectd.info('%s: %s' % (NAME, msg))
 | 
			
		||||
 | 
			
		||||
collectd.register_config(configure_callback)
 | 
			
		||||
collectd.warning("Initializing glance plugin")
 | 
			
		||||
collectd.register_read(read_callback)
 | 
			
		||||
							
								
								
									
										21
									
								
								metadata.rb
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								metadata.rb
									
									
									
									
									
								
							@@ -1,6 +1,15 @@
 | 
			
		||||
maintainer       "Opscode, Inc."
 | 
			
		||||
maintainer_email "matt@opscode.com"
 | 
			
		||||
license          "Apache 2.0"
 | 
			
		||||
description      "The OpenStack Image service Glance."
 | 
			
		||||
long_description IO.read(File.join(File.dirname(__FILE__), 'README.md'))
 | 
			
		||||
version          "0.0.1"
 | 
			
		||||
maintainer        "Opscode, Inc."
 | 
			
		||||
license           "Apache 2.0"
 | 
			
		||||
description       "The Glance Image Registry and Delivery Service Glance"
 | 
			
		||||
long_description  IO.read(File.join(File.dirname(__FILE__), 'README.md'))
 | 
			
		||||
version           "5.0.0"
 | 
			
		||||
recipe            "glance::api", "Installs packages required for a glance api server"
 | 
			
		||||
recipe            "glance::registry", "Installs packages required for a glance registry server"
 | 
			
		||||
 | 
			
		||||
%w{ ubuntu fedora }.each do |os|
 | 
			
		||||
  supports os
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
%w{ database keystone mysql osops-utils }.each do |dep|
 | 
			
		||||
  depends dep
 | 
			
		||||
end
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										220
									
								
								recipes/api.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										220
									
								
								recipes/api.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,220 @@
 | 
			
		||||
#
 | 
			
		||||
# Cookbook Name:: glance
 | 
			
		||||
# Recipe:: api
 | 
			
		||||
#
 | 
			
		||||
# Copyright 2009-2012, Rackspace Hosting, Inc.
 | 
			
		||||
# Copyright 2012, Opscode, 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.
 | 
			
		||||
#
 | 
			
		||||
include_recipe "glance::glance-rsyslog"
 | 
			
		||||
 | 
			
		||||
platform_options = node["glance"]["platform"]
 | 
			
		||||
 | 
			
		||||
package "curl" do
 | 
			
		||||
  action :upgrade
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
package "python-keystone" do
 | 
			
		||||
    action :install
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
platform_options["glance_packages"].each do |pkg|
 | 
			
		||||
  package pkg do
 | 
			
		||||
    action :upgrade
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
service "glance-api" do
 | 
			
		||||
  service_name platform_options["glance_api_service"]
 | 
			
		||||
  supports :status => true, :restart => true
 | 
			
		||||
  action :enable
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
# FIXME: this is broken.  Joe, Wilk, fix this.
 | 
			
		||||
template "/usr/share/pyshared/glance/store/swift.py" do
 | 
			
		||||
  source "swift.py"
 | 
			
		||||
  group "root"
 | 
			
		||||
  owner "root"
 | 
			
		||||
  mode "0644"
 | 
			
		||||
  only_if do platform?(%w{debian ubuntu}) end
 | 
			
		||||
  notifies :restart, resources(:service => "glance-api"), :immediately
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
directory "/etc/glance" do
 | 
			
		||||
  action :create
 | 
			
		||||
  group "glance"
 | 
			
		||||
  owner "glance"
 | 
			
		||||
  mode "0700"
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
# FIXME: seems like misfeature
 | 
			
		||||
template "/etc/glance/policy.json" do
 | 
			
		||||
  source "policy.json.erb"
 | 
			
		||||
  owner "root"
 | 
			
		||||
  group "root"
 | 
			
		||||
  mode "0644"
 | 
			
		||||
  notifies :restart, resources(:service => "glance-api"), :immediately
 | 
			
		||||
  not_if do
 | 
			
		||||
    File.exists?("/etc/glance/policy.json")
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
rabbit_info = get_settings_by_role("rabbitmq-server", "rabbitmq") # FIXME: access
 | 
			
		||||
 | 
			
		||||
ks_admin_endpoint = get_access_endpoint("keystone", "keystone", "admin-api")
 | 
			
		||||
ks_service_endpoint = get_access_endpoint("keystone", "keystone","service-api")
 | 
			
		||||
keystone = get_settings_by_role("keystone", "keystone")
 | 
			
		||||
glance = get_settings_by_role("glance-api", "glance")
 | 
			
		||||
 | 
			
		||||
registry_endpoint = get_access_endpoint("glance-registry", "glance", "registry")
 | 
			
		||||
api_endpoint = get_bind_endpoint("glance", "api")
 | 
			
		||||
 | 
			
		||||
template "/etc/glance/glance-api.conf" do
 | 
			
		||||
  source "glance-api.conf.erb"
 | 
			
		||||
  owner "root"
 | 
			
		||||
  group "root"
 | 
			
		||||
  mode "0644"
 | 
			
		||||
  variables(
 | 
			
		||||
    "api_bind_address" => api_endpoint["host"],
 | 
			
		||||
    "api_bind_port" => api_endpoint["port"],
 | 
			
		||||
    "registry_ip_address" => registry_endpoint["host"],
 | 
			
		||||
    "registry_port" => registry_endpoint["port"],
 | 
			
		||||
    "use_syslog" => node["glance"]["syslog"]["use"],
 | 
			
		||||
    "log_facility" => node["glance"]["syslog"]["facility"],
 | 
			
		||||
    "rabbit_ipaddress" => rabbit_info["ipaddress"],    #FIXME!
 | 
			
		||||
    "keystone_api_ipaddress" => ks_admin_endpoint["host"],
 | 
			
		||||
    "keystone_service_port" => ks_service_endpoint["port"],
 | 
			
		||||
    "service_user" => glance["service_user"],
 | 
			
		||||
    "service_pass" => glance["service_pass"],
 | 
			
		||||
    "service_tenant_name" => glance["service_tenant_name"],
 | 
			
		||||
    "default_store" => glance["api"]["default_store"],
 | 
			
		||||
    "swift_large_object_size" => glance["api"]["swift"]["store_large_object_size"],
 | 
			
		||||
    "swift_large_object_chunk_size" => glance["api"]["swift"]["store_large_object_chunk_size"],
 | 
			
		||||
    "swift_store_container" => glance["api"]["swift"]["store_container"]
 | 
			
		||||
  )
 | 
			
		||||
  notifies :restart, resources(:service => "glance-api"), :immediately
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
template "/etc/glance/glance-api-paste.ini" do
 | 
			
		||||
  source "glance-api-paste.ini.erb"
 | 
			
		||||
  owner "root"
 | 
			
		||||
  group "root"
 | 
			
		||||
  mode "0644"
 | 
			
		||||
  variables(
 | 
			
		||||
    "keystone_api_ipaddress" => ks_admin_endpoint["host"],
 | 
			
		||||
    "keystone_service_port" => ks_service_endpoint["port"],
 | 
			
		||||
    "keystone_admin_port" => ks_admin_endpoint["port"],
 | 
			
		||||
    "keystone_admin_token" => keystone["admin_token"],
 | 
			
		||||
    "service_tenant_name" => node["glance"]["service_tenant_name"],
 | 
			
		||||
    "service_user" => node["glance"]["service_user"],
 | 
			
		||||
    "service_pass" => node["glance"]["service_pass"]
 | 
			
		||||
  )
 | 
			
		||||
  notifies :restart, resources(:service => "glance-api"), :immediately
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
template "/etc/glance/glance-scrubber.conf" do
 | 
			
		||||
  source "glance-scrubber.conf.erb"
 | 
			
		||||
  owner "root"
 | 
			
		||||
  group "root"
 | 
			
		||||
  mode "0644"
 | 
			
		||||
  variables(
 | 
			
		||||
    "registry_ip_address" => registry_endpoint["host"],
 | 
			
		||||
    "registry_port" => registry_endpoint["port"]
 | 
			
		||||
  )
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
template "/etc/glance/glance-scrubber-paste.ini" do
 | 
			
		||||
  source "glance-scrubber-paste.ini.erb"
 | 
			
		||||
  owner "root"
 | 
			
		||||
  group "root"
 | 
			
		||||
  mode "0644"
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
# Register Image Service
 | 
			
		||||
keystone_register "Register Image Service" do
 | 
			
		||||
  auth_host ks_admin_endpoint["host"]
 | 
			
		||||
  auth_port ks_admin_endpoint["port"]
 | 
			
		||||
  auth_protocol ks_admin_endpoint["scheme"]
 | 
			
		||||
  api_ver ks_admin_endpoint["path"]
 | 
			
		||||
  auth_token keystone["admin_token"]
 | 
			
		||||
  service_name "glance"
 | 
			
		||||
  service_type "image"
 | 
			
		||||
  service_description "Glance Image Service"
 | 
			
		||||
  action :create_service
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
# Register Image Endpoint
 | 
			
		||||
keystone_register "Register Image Endpoint" do
 | 
			
		||||
  auth_host ks_admin_endpoint["host"]
 | 
			
		||||
  auth_port ks_admin_endpoint["port"]
 | 
			
		||||
  auth_protocol ks_admin_endpoint["scheme"]
 | 
			
		||||
  api_ver ks_admin_endpoint["path"]
 | 
			
		||||
  auth_token keystone["admin_token"]
 | 
			
		||||
  service_type "image"
 | 
			
		||||
  endpoint_region "RegionOne"
 | 
			
		||||
  endpoint_adminurl api_endpoint["uri"]
 | 
			
		||||
  endpoint_internalurl api_endpoint["uri"]
 | 
			
		||||
  endpoint_publicurl api_endpoint["uri"]
 | 
			
		||||
  action :create_endpoint
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
if node["glance"]["image_upload"]
 | 
			
		||||
  # TODO(breu): the environment needs to be derived from a search
 | 
			
		||||
  # TODO(shep): this whole bit is super dirty.. and needs some love.
 | 
			
		||||
  node["glance"]["images"].each do |img|
 | 
			
		||||
    Chef::Log.info("Checking to see if #{img.to_s}-image should be uploaded.")
 | 
			
		||||
 | 
			
		||||
    keystone_admin_user = keystone["admin_user"]
 | 
			
		||||
    keystone_admin_password = keystone["users"][keystone_admin_user]["password"]
 | 
			
		||||
    keystone_tenant = keystone["users"][keystone_admin_user]["default_tenant"]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    bash "default image setup for #{img.to_s}" do
 | 
			
		||||
      cwd "/tmp"
 | 
			
		||||
      user "root"
 | 
			
		||||
      environment ({"OS_USERNAME" => keystone_admin_user,
 | 
			
		||||
                    "OS_PASSWORD" => keystone_admin_password,
 | 
			
		||||
                    "OS_TENANT_NAME" => keystone_tenant,
 | 
			
		||||
                    "OS_AUTH_URL" => ks_admin_endpoint["uri"]})
 | 
			
		||||
      code <<-EOH
 | 
			
		||||
        set -e
 | 
			
		||||
        set -x
 | 
			
		||||
        mkdir -p images/#{img.to_s}
 | 
			
		||||
        cd images/#{img.to_s}
 | 
			
		||||
 | 
			
		||||
        curl #{node["glance"]["image"][img.to_sym]} | tar -zx
 | 
			
		||||
        image_name=$(basename #{node["glance"]["image"][img]} .tar.gz)
 | 
			
		||||
 | 
			
		||||
        image_name=${image_name%-multinic}
 | 
			
		||||
 | 
			
		||||
        kernel_file=$(ls *vmlinuz-virtual | head -n1)
 | 
			
		||||
        if [ ${#kernel_file} -eq 0 ]; then
 | 
			
		||||
           kernel_file=$(ls *vmlinuz | head -n1)
 | 
			
		||||
        fi
 | 
			
		||||
 | 
			
		||||
        ramdisk=$(ls *-initrd | head -n1)
 | 
			
		||||
        if [ ${#ramdisk} -eq 0 ]; then
 | 
			
		||||
            ramdisk=$(ls *-loader | head -n1)
 | 
			
		||||
        fi
 | 
			
		||||
 | 
			
		||||
        kernel=$(ls *.img | head -n1)
 | 
			
		||||
 | 
			
		||||
        kid=$(glance --silent-upload add name="${image_name}-kernel" is_public=true disk_format=aki container_format=aki < ${kernel_file} | cut -d: -f2 | sed 's/ //')
 | 
			
		||||
        rid=$(glance --silent-upload add name="${image_name}-initrd" is_public=true disk_format=ari container_format=ari < ${ramdisk} | cut -d: -f2 | sed 's/ //')
 | 
			
		||||
        glance --silent-upload add name="#{img.to_s}-image" is_public=true disk_format=ami container_format=ami kernel_id=$kid ramdisk_id=$rid < ${kernel}
 | 
			
		||||
      EOH
 | 
			
		||||
      not_if "glance -f -I #{keystone_admin_user} -K #{keystone_admin_password} -T #{keystone_tenant} -N #{ks_admin_endpoint["uri"]} index | grep #{img.to_s}-image"
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@@ -2,6 +2,7 @@
 | 
			
		||||
# Cookbook Name:: glance
 | 
			
		||||
# Recipe:: default
 | 
			
		||||
#
 | 
			
		||||
# Copyright 2009-2012, Rackspace Hosting, Inc.
 | 
			
		||||
# Copyright 2012, Opscode, Inc.
 | 
			
		||||
#
 | 
			
		||||
# Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
@@ -17,3 +18,5 @@
 | 
			
		||||
# limitations under the License.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
include_recipe "glance::registry"
 | 
			
		||||
include_recipe "glance::api"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										31
									
								
								recipes/glance-rsyslog.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								recipes/glance-rsyslog.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
			
		||||
#
 | 
			
		||||
# Cookbook Name:: glance
 | 
			
		||||
# Recipe:: default
 | 
			
		||||
#
 | 
			
		||||
# Copyright 2009, Rackspace Hosting, 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.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
if node["glance"]["syslog"]["use"] 
 | 
			
		||||
    template "/etc/rsyslog.d/22-glance.conf" do
 | 
			
		||||
        source "22-glance.conf.erb"
 | 
			
		||||
        owner "root"
 | 
			
		||||
        group "root"
 | 
			
		||||
        mode "0644"
 | 
			
		||||
        variables(
 | 
			
		||||
            "use_syslog" => node["glance"]["syslog"]["use"],
 | 
			
		||||
            "log_facility" => node["glance"]["syslog"]["facility"]
 | 
			
		||||
        )
 | 
			
		||||
    end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										174
									
								
								recipes/registry.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								recipes/registry.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,174 @@
 | 
			
		||||
#
 | 
			
		||||
# Cookbook Name:: glance
 | 
			
		||||
# Recipe:: registry
 | 
			
		||||
#
 | 
			
		||||
# Copyright 2009, Rackspace Hosting, 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.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
::Chef::Recipe.send(:include, Opscode::OpenSSL::Password)
 | 
			
		||||
include_recipe "mysql::client"
 | 
			
		||||
include_recipe "glance::glance-rsyslog"
 | 
			
		||||
 | 
			
		||||
platform_options = node["glance"]["platform"]
 | 
			
		||||
 | 
			
		||||
# Allow for using a well known db password
 | 
			
		||||
if node["developer_mode"]
 | 
			
		||||
  node.set_unless['glance']['db']['password'] = "glance"
 | 
			
		||||
else
 | 
			
		||||
  node.set_unless['glance']['db']['password'] = secure_password
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
# Set a secure keystone service password
 | 
			
		||||
node.set_unless['glance']['service_pass'] = secure_password
 | 
			
		||||
 | 
			
		||||
package "python-keystone" do
 | 
			
		||||
    action :install
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
ks_admin_endpoint = get_access_endpoint("keystone", "keystone", "admin-api")
 | 
			
		||||
ks_service_endpoint = get_access_endpoint("keystone", "keystone", "service-api")
 | 
			
		||||
keystone = get_settings_by_role("keystone", "keystone")
 | 
			
		||||
 | 
			
		||||
registry_endpoint = get_bind_endpoint("glance", "registry")
 | 
			
		||||
 | 
			
		||||
#creates db and user
 | 
			
		||||
#returns connection info
 | 
			
		||||
#defined in osops-utils/libraries
 | 
			
		||||
mysql_info = create_db_and_user("mysql",
 | 
			
		||||
                                node["glance"]["db"]["name"],
 | 
			
		||||
                                node["glance"]["db"]["username"],
 | 
			
		||||
                                node["glance"]["db"]["password"])
 | 
			
		||||
 | 
			
		||||
package "curl" do
 | 
			
		||||
  action :install
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
platform_options["mysql_python_packages"].each do |pkg|
 | 
			
		||||
  package pkg do
 | 
			
		||||
    action :install
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
platform_options["glance_packages"].each do |pkg|
 | 
			
		||||
  package pkg do
 | 
			
		||||
    action :upgrade
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
service "glance-registry" do
 | 
			
		||||
  service_name platform_options["glance_registry_service"]
 | 
			
		||||
  supports :status => true, :restart => true
 | 
			
		||||
  action :enable
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
execute "glance-manage db_sync" do
 | 
			
		||||
        command "sudo -u glance glance-manage db_sync"
 | 
			
		||||
        action :nothing
 | 
			
		||||
        notifies :restart, resources(:service => "glance-registry"), :immediately
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
# Having to manually version the database because of Ubuntu bug
 | 
			
		||||
# https://bugs.launchpad.net/ubuntu/+source/glance/+bug/981111
 | 
			
		||||
execute "glance-manage version_control" do
 | 
			
		||||
  command "sudo -u glance glance-manage version_control 0"
 | 
			
		||||
  action :nothing
 | 
			
		||||
  not_if "sudo -u glance glance-manage db_version"
 | 
			
		||||
  notifies :run, resources(:execute => "glance-manage db_sync"), :immediately
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
file "/var/lib/glance/glance.sqlite" do
 | 
			
		||||
    action :delete
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
# Register Service Tenant
 | 
			
		||||
keystone_register "Register Service Tenant" do
 | 
			
		||||
  auth_host ks_admin_endpoint["host"]
 | 
			
		||||
  auth_port ks_admin_endpoint["port"]
 | 
			
		||||
  auth_protocol ks_admin_endpoint["scheme"]
 | 
			
		||||
  api_ver ks_admin_endpoint["path"]
 | 
			
		||||
  auth_token keystone["admin_token"]
 | 
			
		||||
  tenant_name node["glance"]["service_tenant_name"]
 | 
			
		||||
  tenant_description "Service Tenant"
 | 
			
		||||
  tenant_enabled "true" # Not required as this is the default
 | 
			
		||||
  action :create_tenant
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
# Register Service User
 | 
			
		||||
keystone_register "Register Service User" do
 | 
			
		||||
  auth_host ks_admin_endpoint["host"]
 | 
			
		||||
  auth_port ks_admin_endpoint["port"]
 | 
			
		||||
  auth_protocol ks_admin_endpoint["scheme"]
 | 
			
		||||
  api_ver ks_admin_endpoint["path"]
 | 
			
		||||
  auth_token keystone["admin_token"]
 | 
			
		||||
  tenant_name node["glance"]["service_tenant_name"]
 | 
			
		||||
  user_name node["glance"]["service_user"]
 | 
			
		||||
  user_pass node["glance"]["service_pass"]
 | 
			
		||||
  user_enabled "true" # Not required as this is the default
 | 
			
		||||
  action :create_user
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
## Grant Admin role to Service User for Service Tenant ##
 | 
			
		||||
keystone_register "Grant 'admin' Role to Service User for Service Tenant" do
 | 
			
		||||
  auth_host ks_admin_endpoint["host"]
 | 
			
		||||
  auth_port ks_admin_endpoint["port"]
 | 
			
		||||
  auth_protocol ks_admin_endpoint["scheme"]
 | 
			
		||||
  api_ver ks_admin_endpoint["path"]
 | 
			
		||||
  auth_token keystone["admin_token"]
 | 
			
		||||
  tenant_name node["glance"]["service_tenant_name"]
 | 
			
		||||
  user_name node["glance"]["service_user"]
 | 
			
		||||
  role_name node["glance"]["service_role"]
 | 
			
		||||
  action :grant_role
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
directory "/etc/glance" do
 | 
			
		||||
  action :create
 | 
			
		||||
  group "glance"
 | 
			
		||||
  owner "glance"
 | 
			
		||||
  mode "0700"
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
template "/etc/glance/glance-registry.conf" do
 | 
			
		||||
  source "glance-registry.conf.erb"
 | 
			
		||||
  owner "root"
 | 
			
		||||
  group "root"
 | 
			
		||||
  mode "0644"
 | 
			
		||||
  variables(
 | 
			
		||||
    "registry_bind_address" => registry_endpoint["host"],
 | 
			
		||||
    "registry_port" => registry_endpoint["port"],
 | 
			
		||||
    "db_ip_address" => mysql_info["bind_address"],
 | 
			
		||||
    "db_user" => node["glance"]["db"]["username"],
 | 
			
		||||
    "db_password" => node["glance"]["db"]["password"],
 | 
			
		||||
    "db_name" => node["glance"]["db"]["name"],
 | 
			
		||||
    "use_syslog" => node["glance"]["syslog"]["use"],
 | 
			
		||||
    "log_facility" => node["glance"]["syslog"]["facility"]
 | 
			
		||||
  )
 | 
			
		||||
  notifies :run, resources(:execute => "glance-manage version_control"), :immediately
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
template "/etc/glance/glance-registry-paste.ini" do
 | 
			
		||||
  source "glance-registry-paste.ini.erb"
 | 
			
		||||
  owner "root"
 | 
			
		||||
  group "root"
 | 
			
		||||
  mode "0644"
 | 
			
		||||
  variables(
 | 
			
		||||
    "keystone_api_ipaddress" => ks_admin_endpoint["host"],
 | 
			
		||||
    "keystone_service_port" => ks_service_endpoint["port"],
 | 
			
		||||
    "keystone_admin_port" => ks_admin_endpoint["port"],
 | 
			
		||||
    "service_tenant_name" => node["glance"]["service_tenant_name"],
 | 
			
		||||
    "service_user" => node["glance"]["service_user"],
 | 
			
		||||
    "service_pass" => node["glance"]["service_pass"]
 | 
			
		||||
  )
 | 
			
		||||
  notifies :restart, resources(:service => "glance-registry"), :immediately
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										7
									
								
								templates/default/22-glance.conf.erb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								templates/default/22-glance.conf.erb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
$DirGroup adm
 | 
			
		||||
$DirCreateMode 0755
 | 
			
		||||
$FileGroup adm
 | 
			
		||||
 | 
			
		||||
$template GlanceLog, "/var/log/glance/glance.log"
 | 
			
		||||
 | 
			
		||||
local2.* 		-?GlanceLog
 | 
			
		||||
							
								
								
									
										76
									
								
								templates/default/glance-api-paste.ini.erb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								templates/default/glance-api-paste.ini.erb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,76 @@
 | 
			
		||||
# Default minimal pipeline
 | 
			
		||||
[pipeline:glance-api]
 | 
			
		||||
pipeline = versionnegotiation context apiv1app
 | 
			
		||||
 | 
			
		||||
# Use the following pipeline for keystone auth
 | 
			
		||||
# i.e. in glance-api.conf:
 | 
			
		||||
#   [paste_deploy]
 | 
			
		||||
#   flavor = keystone
 | 
			
		||||
#
 | 
			
		||||
[pipeline:glance-api-keystone]
 | 
			
		||||
pipeline = versionnegotiation authtoken context apiv1app
 | 
			
		||||
 | 
			
		||||
# Use the following pipeline to enable transparent caching of image files
 | 
			
		||||
# i.e. in glance-api.conf:
 | 
			
		||||
#   [paste_deploy]
 | 
			
		||||
#   flavor = caching
 | 
			
		||||
#
 | 
			
		||||
[pipeline:glance-api-caching]
 | 
			
		||||
pipeline = versionnegotiation context cache apiv1app
 | 
			
		||||
 | 
			
		||||
# Use the following pipeline for keystone auth with caching
 | 
			
		||||
# i.e. in glance-api.conf:
 | 
			
		||||
#   [paste_deploy]
 | 
			
		||||
#   flavor = keystone+caching
 | 
			
		||||
#
 | 
			
		||||
[pipeline:glance-api-keystone+caching]
 | 
			
		||||
pipeline = versionnegotiation authtoken context cache apiv1app
 | 
			
		||||
 | 
			
		||||
# Use the following pipeline to enable the Image Cache Management API
 | 
			
		||||
# i.e. in glance-api.conf:
 | 
			
		||||
#   [paste_deploy]
 | 
			
		||||
#   flavor = cachemanagement
 | 
			
		||||
#
 | 
			
		||||
[pipeline:glance-api-cachemanagement]
 | 
			
		||||
pipeline = versionnegotiation context cache cachemanage apiv1app
 | 
			
		||||
 | 
			
		||||
# Use the following pipeline for keystone auth with cache management
 | 
			
		||||
# i.e. in glance-api.conf:
 | 
			
		||||
#   [paste_deploy]
 | 
			
		||||
#   flavor = keystone+cachemanagement
 | 
			
		||||
#
 | 
			
		||||
[pipeline:glance-api-keystone+cachemanagement]
 | 
			
		||||
pipeline = versionnegotiation authtoken context cache cachemanage apiv1app
 | 
			
		||||
 | 
			
		||||
[app:apiv1app]
 | 
			
		||||
paste.app_factory = glance.common.wsgi:app_factory
 | 
			
		||||
glance.app_factory = glance.api.v1.router:API
 | 
			
		||||
 | 
			
		||||
[filter:versionnegotiation]
 | 
			
		||||
paste.filter_factory = glance.common.wsgi:filter_factory
 | 
			
		||||
glance.filter_factory = glance.api.middleware.version_negotiation:VersionNegotiationFilter
 | 
			
		||||
 | 
			
		||||
[filter:cache]
 | 
			
		||||
paste.filter_factory = glance.common.wsgi:filter_factory
 | 
			
		||||
glance.filter_factory = glance.api.middleware.cache:CacheFilter
 | 
			
		||||
 | 
			
		||||
[filter:cachemanage]
 | 
			
		||||
paste.filter_factory = glance.common.wsgi:filter_factory
 | 
			
		||||
glance.filter_factory = glance.api.middleware.cache_manage:CacheManageFilter
 | 
			
		||||
 | 
			
		||||
[filter:context]
 | 
			
		||||
paste.filter_factory = glance.common.wsgi:filter_factory
 | 
			
		||||
glance.filter_factory = glance.common.context:ContextMiddleware
 | 
			
		||||
 | 
			
		||||
[filter:authtoken]
 | 
			
		||||
paste.filter_factory = keystone.middleware.auth_token:filter_factory
 | 
			
		||||
service_protocol = http
 | 
			
		||||
service_host = <%= @keystone_api_ipaddress %>
 | 
			
		||||
service_port = <%= @keystone_service_port %>
 | 
			
		||||
auth_host = <%= @keystone_api_ipaddress %>
 | 
			
		||||
auth_port = <%= @keystone_admin_port %>
 | 
			
		||||
auth_protocol = http
 | 
			
		||||
auth_uri = http://<%= @keystone_api_ipaddress %>:<%= @keystone_service_port %>/
 | 
			
		||||
admin_tenant_name = <%= @service_tenant_name %>
 | 
			
		||||
admin_user = <%= @service_user %>
 | 
			
		||||
admin_password = <%= @service_pass %>
 | 
			
		||||
							
								
								
									
										244
									
								
								templates/default/glance-api.conf.erb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										244
									
								
								templates/default/glance-api.conf.erb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,244 @@
 | 
			
		||||
[DEFAULT]
 | 
			
		||||
# Show more verbose log output (sets INFO log level output)
 | 
			
		||||
verbose = True
 | 
			
		||||
 | 
			
		||||
# Show debugging output in logs (sets DEBUG log level output)
 | 
			
		||||
debug = False
 | 
			
		||||
 | 
			
		||||
# Which backend store should Glance use by default is not specified
 | 
			
		||||
# in a request to add a new image to Glance? Default: 'file'
 | 
			
		||||
# Available choices are 'file', 'swift', and 's3'
 | 
			
		||||
default_store = file
 | 
			
		||||
 | 
			
		||||
# Address to bind the API server
 | 
			
		||||
bind_host = <%= @api_bind_address %>
 | 
			
		||||
 | 
			
		||||
# Port the bind the API server to
 | 
			
		||||
bind_port = <%= @api_bind_port %>
 | 
			
		||||
 | 
			
		||||
# Log to this file. Make sure you do not set the same log
 | 
			
		||||
# file for both the API and registry servers!
 | 
			
		||||
log_file = /var/log/glance/api.log
 | 
			
		||||
 | 
			
		||||
# Backlog requests when creating socket
 | 
			
		||||
backlog = 4096
 | 
			
		||||
 | 
			
		||||
# Number of Glance API worker processes to start.
 | 
			
		||||
# On machines with more than one CPU increasing this value
 | 
			
		||||
# may improve performance (especially if using SSL with
 | 
			
		||||
# compression turned on). It is typically recommended to set
 | 
			
		||||
# this value to the number of CPUs present on your machine.
 | 
			
		||||
workers = 0
 | 
			
		||||
 | 
			
		||||
# Role used to identify an authenticated user as administrator
 | 
			
		||||
#admin_role = admin
 | 
			
		||||
 | 
			
		||||
# ================= Syslog Options ============================
 | 
			
		||||
 | 
			
		||||
# Send logs to syslog (/dev/log) instead of to file specified
 | 
			
		||||
# by `log_file`
 | 
			
		||||
use_syslog = <%= @use_syslog %>
 | 
			
		||||
 | 
			
		||||
<% if @use_syslog == true %>
 | 
			
		||||
# Facility to use. If unset defaults to LOG_USER.
 | 
			
		||||
syslog_log_facility = <%= @log_facility %>
 | 
			
		||||
<% else %>
 | 
			
		||||
# syslog_log_facility = LOG_USER
 | 
			
		||||
<% end %>
 | 
			
		||||
 | 
			
		||||
# ================= SSL Options ===============================
 | 
			
		||||
 | 
			
		||||
# Certificate file to use when starting API server securely
 | 
			
		||||
# cert_file = /path/to/certfile
 | 
			
		||||
 | 
			
		||||
# Private key file to use when starting API server securely
 | 
			
		||||
# key_file = /path/to/keyfile
 | 
			
		||||
 | 
			
		||||
# ================= Security Options ==========================
 | 
			
		||||
 | 
			
		||||
# AES key for encrypting store 'location' metadata, including
 | 
			
		||||
# -- if used -- Swift or S3 credentials
 | 
			
		||||
# Should be set to a random string of length 16, 24 or 32 bytes
 | 
			
		||||
# metadata_encryption_key = <16, 24 or 32 char registry metadata key>
 | 
			
		||||
 | 
			
		||||
# ============ Registry Options ===============================
 | 
			
		||||
 | 
			
		||||
# Address to find the registry server
 | 
			
		||||
registry_host = <%= @registry_ip_address %>
 | 
			
		||||
 | 
			
		||||
# Port the registry server is listening on
 | 
			
		||||
registry_port = <%= @registry_port %>
 | 
			
		||||
 | 
			
		||||
# What protocol to use when connecting to the registry server?
 | 
			
		||||
# Set to https for secure HTTP communication
 | 
			
		||||
registry_client_protocol = http
 | 
			
		||||
 | 
			
		||||
# The path to the key file to use in SSL connections to the
 | 
			
		||||
# registry server, if any. Alternately, you may set the
 | 
			
		||||
# GLANCE_CLIENT_KEY_FILE environ variable to a filepath of the key file
 | 
			
		||||
# registry_client_key_file = /path/to/key/file
 | 
			
		||||
 | 
			
		||||
# The path to the cert file to use in SSL connections to the
 | 
			
		||||
# registry server, if any. Alternately, you may set the
 | 
			
		||||
# GLANCE_CLIENT_CERT_FILE environ variable to a filepath of the cert file
 | 
			
		||||
# registry_client_cert_file = /path/to/cert/file
 | 
			
		||||
 | 
			
		||||
# The path to the certifying authority cert file to use in SSL connections
 | 
			
		||||
# to the registry server, if any. Alternately, you may set the
 | 
			
		||||
# GLANCE_CLIENT_CA_FILE environ variable to a filepath of the CA cert file
 | 
			
		||||
# registry_client_ca_file = /path/to/ca/file
 | 
			
		||||
 | 
			
		||||
# ============ Notification System Options =====================
 | 
			
		||||
 | 
			
		||||
# Notifications can be sent when images are create, updated or deleted.
 | 
			
		||||
# There are three methods of sending notifications, logging (via the
 | 
			
		||||
# log_file directive), rabbit (via a rabbitmq queue), qpid (via a Qpid
 | 
			
		||||
# message queue), or noop (no notifications sent, the default)
 | 
			
		||||
notifier_strategy = noop
 | 
			
		||||
 | 
			
		||||
# Configuration options if sending notifications via rabbitmq (these are
 | 
			
		||||
# the defaults)
 | 
			
		||||
rabbit_host = <%= @rabbit_ipaddress %>
 | 
			
		||||
rabbit_port = 5672
 | 
			
		||||
rabbit_use_ssl = false
 | 
			
		||||
rabbit_userid = guest
 | 
			
		||||
rabbit_password = guest
 | 
			
		||||
rabbit_virtual_host = /
 | 
			
		||||
rabbit_notification_exchange = glance
 | 
			
		||||
rabbit_notification_topic = glance_notifications
 | 
			
		||||
 | 
			
		||||
# Configuration options if sending notifications via Qpid (these are
 | 
			
		||||
# the defaults)
 | 
			
		||||
qpid_notification_exchange = glance
 | 
			
		||||
qpid_notification_topic = glance_notifications
 | 
			
		||||
qpid_host = localhost
 | 
			
		||||
qpid_port = 5672
 | 
			
		||||
qpid_username =
 | 
			
		||||
qpid_password =
 | 
			
		||||
qpid_sasl_mechanisms =
 | 
			
		||||
qpid_reconnect_timeout = 0
 | 
			
		||||
qpid_reconnect_limit = 0
 | 
			
		||||
qpid_reconnect_interval_min = 0
 | 
			
		||||
qpid_reconnect_interval_max = 0
 | 
			
		||||
qpid_reconnect_interval = 0
 | 
			
		||||
qpid_heartbeat = 5
 | 
			
		||||
# Set to 'ssl' to enable SSL
 | 
			
		||||
qpid_protocol = tcp
 | 
			
		||||
qpid_tcp_nodelay = True
 | 
			
		||||
 | 
			
		||||
# ============ Filesystem Store Options ========================
 | 
			
		||||
 | 
			
		||||
# Directory that the Filesystem backend store
 | 
			
		||||
# writes image data to
 | 
			
		||||
filesystem_store_datadir = /var/lib/glance/images/
 | 
			
		||||
 | 
			
		||||
# ============ Swift Store Options =============================
 | 
			
		||||
 | 
			
		||||
# Address where the Swift authentication service lives
 | 
			
		||||
# Valid schemes are 'http://' and 'https://'
 | 
			
		||||
# If no scheme specified,  default to 'https://'
 | 
			
		||||
swift_store_auth_address = 127.0.0.1:8080/v1.0/
 | 
			
		||||
 | 
			
		||||
# User to authenticate against the Swift authentication service
 | 
			
		||||
# If you use Swift authentication service, set it to 'account':'user'
 | 
			
		||||
# where 'account' is a Swift storage account and 'user'
 | 
			
		||||
# is a user in that account
 | 
			
		||||
swift_store_user = jdoe:jdoe
 | 
			
		||||
 | 
			
		||||
# Auth key for the user authenticating against the
 | 
			
		||||
# Swift authentication service
 | 
			
		||||
swift_store_key = a86850deb2742ec3cb41518e26aa2d89
 | 
			
		||||
 | 
			
		||||
# Container within the account that the account should use
 | 
			
		||||
# for storing images in Swift
 | 
			
		||||
swift_store_container = glance
 | 
			
		||||
 | 
			
		||||
# Do we create the container if it does not exist?
 | 
			
		||||
swift_store_create_container_on_put = False
 | 
			
		||||
 | 
			
		||||
# What size, in MB, should Glance start chunking image files
 | 
			
		||||
# and do a large object manifest in Swift? By default, this is
 | 
			
		||||
# the maximum object size in Swift, which is 5GB
 | 
			
		||||
swift_store_large_object_size = 5120
 | 
			
		||||
 | 
			
		||||
# When doing a large object manifest, what size, in MB, should
 | 
			
		||||
# Glance write chunks to Swift? This amount of data is written
 | 
			
		||||
# to a temporary disk buffer during the process of chunking
 | 
			
		||||
# the image file, and the default is 200MB
 | 
			
		||||
swift_store_large_object_chunk_size = 200
 | 
			
		||||
 | 
			
		||||
# Whether to use ServiceNET to communicate with the Swift storage servers.
 | 
			
		||||
# (If you aren't RACKSPACE, leave this False!)
 | 
			
		||||
#
 | 
			
		||||
# To use ServiceNET for authentication, prefix hostname of
 | 
			
		||||
# `swift_store_auth_address` with 'snet-'.
 | 
			
		||||
# Ex. https://example.com/v1.0/ -> https://snet-example.com/v1.0/
 | 
			
		||||
swift_enable_snet = False
 | 
			
		||||
 | 
			
		||||
# ============ S3 Store Options =============================
 | 
			
		||||
 | 
			
		||||
# Address where the S3 authentication service lives
 | 
			
		||||
# Valid schemes are 'http://' and 'https://'
 | 
			
		||||
# If no scheme specified,  default to 'http://'
 | 
			
		||||
s3_store_host = 127.0.0.1:8080/v1.0/
 | 
			
		||||
 | 
			
		||||
# User to authenticate against the S3 authentication service
 | 
			
		||||
s3_store_access_key = <20-char AWS access key>
 | 
			
		||||
 | 
			
		||||
# Auth key for the user authenticating against the
 | 
			
		||||
# S3 authentication service
 | 
			
		||||
s3_store_secret_key = <40-char AWS secret key>
 | 
			
		||||
 | 
			
		||||
# Container within the account that the account should use
 | 
			
		||||
# for storing images in S3. Note that S3 has a flat namespace,
 | 
			
		||||
# so you need a unique bucket name for your glance images. An
 | 
			
		||||
# easy way to do this is append your AWS access key to "glance".
 | 
			
		||||
# S3 buckets in AWS *must* be lowercased, so remember to lowercase
 | 
			
		||||
# your AWS access key if you use it in your bucket name below!
 | 
			
		||||
s3_store_bucket = <lowercased 20-char aws access key>glance
 | 
			
		||||
 | 
			
		||||
# Do we create the bucket if it does not exist?
 | 
			
		||||
s3_store_create_bucket_on_put = False
 | 
			
		||||
 | 
			
		||||
# When sending images to S3, the data will first be written to a
 | 
			
		||||
# temporary buffer on disk. By default the platform's temporary directory
 | 
			
		||||
# will be used. If required, an alternative directory can be specified here.
 | 
			
		||||
# s3_store_object_buffer_dir = /path/to/dir
 | 
			
		||||
 | 
			
		||||
# ============ RBD Store Options =============================
 | 
			
		||||
 | 
			
		||||
# Ceph configuration file path
 | 
			
		||||
# If using cephx authentication, this file should
 | 
			
		||||
# include a reference to the right keyring
 | 
			
		||||
# in a client.<USER> section
 | 
			
		||||
rbd_store_ceph_conf = /etc/ceph/ceph.conf
 | 
			
		||||
 | 
			
		||||
# RADOS user to authenticate as (only applicable if using cephx)
 | 
			
		||||
rbd_store_user = glance
 | 
			
		||||
 | 
			
		||||
# RADOS pool in which images are stored
 | 
			
		||||
rbd_store_pool = images
 | 
			
		||||
 | 
			
		||||
# Images will be chunked into objects of this size (in megabytes).
 | 
			
		||||
# For best performance, this should be a power of two
 | 
			
		||||
rbd_store_chunk_size = 8
 | 
			
		||||
 | 
			
		||||
# ============ Delayed Delete Options =============================
 | 
			
		||||
 | 
			
		||||
# Turn on/off delayed delete
 | 
			
		||||
delayed_delete = False
 | 
			
		||||
 | 
			
		||||
# Delayed delete time in seconds
 | 
			
		||||
scrub_time = 43200
 | 
			
		||||
 | 
			
		||||
# Directory that the scrubber will use to remind itself of what to delete
 | 
			
		||||
# Make sure this is also set in glance-scrubber.conf
 | 
			
		||||
scrubber_datadir = /var/lib/glance/scrubber
 | 
			
		||||
 | 
			
		||||
# =============== Image Cache Options =============================
 | 
			
		||||
 | 
			
		||||
# Base directory that the Image Cache uses
 | 
			
		||||
image_cache_dir = /var/lib/glance/image-cache/
 | 
			
		||||
 | 
			
		||||
[paste_deploy]
 | 
			
		||||
flavor = keystone
 | 
			
		||||
							
								
								
									
										33
									
								
								templates/default/glance-registry-paste.ini.erb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								templates/default/glance-registry-paste.ini.erb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
# Default minimal pipeline
 | 
			
		||||
[pipeline:glance-registry]
 | 
			
		||||
pipeline = context registryapp
 | 
			
		||||
 | 
			
		||||
# Use the following pipeline for keystone auth
 | 
			
		||||
# i.e. in glance-registry.conf:
 | 
			
		||||
#   [paste_deploy]
 | 
			
		||||
#   flavor = keystone
 | 
			
		||||
#
 | 
			
		||||
[pipeline:glance-registry-keystone]
 | 
			
		||||
pipeline = authtoken context registryapp
 | 
			
		||||
 | 
			
		||||
[app:registryapp]
 | 
			
		||||
paste.app_factory = glance.common.wsgi:app_factory
 | 
			
		||||
glance.app_factory = glance.registry.api.v1:API
 | 
			
		||||
 | 
			
		||||
[filter:context]
 | 
			
		||||
context_class = glance.registry.context.RequestContext
 | 
			
		||||
paste.filter_factory = glance.common.wsgi:filter_factory
 | 
			
		||||
glance.filter_factory = glance.common.context:ContextMiddleware
 | 
			
		||||
 | 
			
		||||
[filter:authtoken]
 | 
			
		||||
paste.filter_factory = keystone.middleware.auth_token:filter_factory
 | 
			
		||||
service_protocol = http
 | 
			
		||||
service_host = <%= @keystone_api_ipaddress %>
 | 
			
		||||
service_port = <%= @keystone_service_port %>
 | 
			
		||||
auth_host = <%= @keystone_api_ipaddress %>
 | 
			
		||||
auth_port = <%= @keystone_admin_port %>
 | 
			
		||||
auth_protocol = http
 | 
			
		||||
auth_uri = http://<%= @keystone_api_ipaddress %>:<%= @keystone_service_port %>/
 | 
			
		||||
admin_tenant_name = <%= @service_tenant_name %>
 | 
			
		||||
admin_user = <%= @service_user %>
 | 
			
		||||
admin_password = <%= @service_pass %>
 | 
			
		||||
							
								
								
									
										65
									
								
								templates/default/glance-registry.conf.erb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								templates/default/glance-registry.conf.erb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,65 @@
 | 
			
		||||
[DEFAULT]
 | 
			
		||||
# Show more verbose log output (sets INFO log level output)
 | 
			
		||||
verbose = True
 | 
			
		||||
 | 
			
		||||
# Show debugging output in logs (sets DEBUG log level output)
 | 
			
		||||
debug = False
 | 
			
		||||
 | 
			
		||||
# Address to bind the registry server
 | 
			
		||||
bind_host = <%= @registry_bind_address %>
 | 
			
		||||
 | 
			
		||||
# Port the bind the registry server to
 | 
			
		||||
bind_port = <%= @registry_port %>
 | 
			
		||||
 | 
			
		||||
# Log to this file. Make sure you do not set the same log
 | 
			
		||||
# file for both the API and registry servers!
 | 
			
		||||
log_file = /var/log/glance/registry.log
 | 
			
		||||
 | 
			
		||||
# Backlog requests when creating socket
 | 
			
		||||
backlog = 4096
 | 
			
		||||
 | 
			
		||||
# SQLAlchemy connection string for the reference implementation
 | 
			
		||||
# registry server. Any valid SQLAlchemy connection string is fine.
 | 
			
		||||
# See: http://www.sqlalchemy.org/docs/05/reference/sqlalchemy/connections.html#sqlalchemy.create_engine
 | 
			
		||||
sql_connection = mysql://<%= @db_user %>:<%= @db_password %>@<%= @db_ip_address %>/<%= @db_name %>
 | 
			
		||||
 | 
			
		||||
# Period in seconds after which SQLAlchemy should reestablish its connection
 | 
			
		||||
# to the database.
 | 
			
		||||
#
 | 
			
		||||
# MySQL uses a default `wait_timeout` of 8 hours, after which it will drop
 | 
			
		||||
# idle connections. This can result in 'MySQL Gone Away' exceptions. If you
 | 
			
		||||
# notice this, you can lower this value to ensure that SQLAlchemy reconnects
 | 
			
		||||
# before MySQL can drop the connection.
 | 
			
		||||
sql_idle_timeout = 3600
 | 
			
		||||
 | 
			
		||||
# Limit the api to return `param_limit_max` items in a call to a container. If
 | 
			
		||||
# a larger `limit` query param is provided, it will be reduced to this value.
 | 
			
		||||
api_limit_max = 1000
 | 
			
		||||
 | 
			
		||||
# If a `limit` query param is not provided in an api request, it will
 | 
			
		||||
# default to `limit_param_default`
 | 
			
		||||
limit_param_default = 25
 | 
			
		||||
 | 
			
		||||
# ================= Syslog Options ============================
 | 
			
		||||
 | 
			
		||||
# Send logs to syslog (/dev/log) instead of to file specified
 | 
			
		||||
# by `log_file`
 | 
			
		||||
use_syslog = <%= @use_syslog %>
 | 
			
		||||
 | 
			
		||||
<% if @use_syslog == true %>
 | 
			
		||||
# Facility to use. If unset defaults to LOG_USER.
 | 
			
		||||
syslog_log_facility = <%= @log_facility %>
 | 
			
		||||
<% else %>
 | 
			
		||||
# syslog_log_facility = LOG_USER
 | 
			
		||||
<% end %>
 | 
			
		||||
 | 
			
		||||
# ================= SSL Options ===============================
 | 
			
		||||
 | 
			
		||||
# Certificate file to use when starting registry server securely
 | 
			
		||||
# cert_file = /path/to/certfile
 | 
			
		||||
 | 
			
		||||
# Private key file to use when starting registry server securely
 | 
			
		||||
# key_file = /path/to/keyfile
 | 
			
		||||
 | 
			
		||||
[paste_deploy]
 | 
			
		||||
flavor = keystone
 | 
			
		||||
							
								
								
									
										3
									
								
								templates/default/glance-scrubber-paste.ini.erb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								templates/default/glance-scrubber-paste.ini.erb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
[app:glance-scrubber]
 | 
			
		||||
paste.app_factory = glance.common.wsgi:app_factory
 | 
			
		||||
glance.app_factory = glance.store.scrubber:Scrubber
 | 
			
		||||
							
								
								
									
										35
									
								
								templates/default/glance-scrubber.conf.erb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								templates/default/glance-scrubber.conf.erb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
[DEFAULT]
 | 
			
		||||
# Show more verbose log output (sets INFO log level output)
 | 
			
		||||
verbose = True
 | 
			
		||||
 | 
			
		||||
# Show debugging output in logs (sets DEBUG log level output)
 | 
			
		||||
debug = False
 | 
			
		||||
 | 
			
		||||
# Log to this file. Make sure you do not set the same log
 | 
			
		||||
# file for both the API and registry servers!
 | 
			
		||||
log_file = /var/log/glance/scrubber.log
 | 
			
		||||
 | 
			
		||||
# Send logs to syslog (/dev/log) instead of to file specified by `log_file`
 | 
			
		||||
use_syslog = False
 | 
			
		||||
 | 
			
		||||
# Should we run our own loop or rely on cron/scheduler to run us
 | 
			
		||||
daemon = False
 | 
			
		||||
 | 
			
		||||
# Loop time between checking the db for new items to schedule for delete
 | 
			
		||||
wakeup_time = 300
 | 
			
		||||
 | 
			
		||||
# Directory that the scrubber will use to remind itself of what to delete
 | 
			
		||||
# Make sure this is also set in glance-api.conf
 | 
			
		||||
scrubber_datadir = /var/lib/glance/scrubber
 | 
			
		||||
 | 
			
		||||
# Only one server in your deployment should be designated the cleanup host
 | 
			
		||||
cleanup_scrubber = False
 | 
			
		||||
 | 
			
		||||
# pending_delete items older than this time are candidates for cleanup
 | 
			
		||||
cleanup_scrubber_time = 86400
 | 
			
		||||
 | 
			
		||||
# Address to find the registry server for cleanups
 | 
			
		||||
registry_host = <%= @registry_ip_address %>
 | 
			
		||||
 | 
			
		||||
# Port the registry server is listening on
 | 
			
		||||
registry_port = <%= @registry_port %>
 | 
			
		||||
							
								
								
									
										4
									
								
								templates/default/policy.json.erb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								templates/default/policy.json.erb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
{
 | 
			
		||||
    "default": [],
 | 
			
		||||
    "manage_image_cache": [["role:admin"]]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										565
									
								
								templates/default/swift.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										565
									
								
								templates/default/swift.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,565 @@
 | 
			
		||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
 | 
			
		||||
 | 
			
		||||
# Copyright 2010-2011 OpenStack, LLC
 | 
			
		||||
# 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.
 | 
			
		||||
 | 
			
		||||
"""Storage backend for SWIFT"""
 | 
			
		||||
 | 
			
		||||
from __future__ import absolute_import
 | 
			
		||||
 | 
			
		||||
import hashlib
 | 
			
		||||
import httplib
 | 
			
		||||
import logging
 | 
			
		||||
import math
 | 
			
		||||
import urlparse
 | 
			
		||||
 | 
			
		||||
from glance.common import cfg
 | 
			
		||||
from glance.common import exception
 | 
			
		||||
import glance.store
 | 
			
		||||
import glance.store.base
 | 
			
		||||
import glance.store.location
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    from swift.common import client as swift_client
 | 
			
		||||
except ImportError:
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
DEFAULT_CONTAINER = 'glance'
 | 
			
		||||
DEFAULT_LARGE_OBJECT_SIZE = 5 * 1024  # 5GB
 | 
			
		||||
DEFAULT_LARGE_OBJECT_CHUNK_SIZE = 200  # 200M
 | 
			
		||||
ONE_MB = 1000 * 1024
 | 
			
		||||
 | 
			
		||||
logger = logging.getLogger('glance.store.swift')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class StoreLocation(glance.store.location.StoreLocation):
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    Class describing a Swift URI. A Swift URI can look like any of
 | 
			
		||||
    the following:
 | 
			
		||||
 | 
			
		||||
        swift://user:pass@authurl.com/container/obj-id
 | 
			
		||||
        swift://account:user:pass@authurl.com/container/obj-id
 | 
			
		||||
        swift+http://user:pass@authurl.com/container/obj-id
 | 
			
		||||
        swift+https://user:pass@authurl.com/container/obj-id
 | 
			
		||||
 | 
			
		||||
    The swift+http:// URIs indicate there is an HTTP authentication URL.
 | 
			
		||||
    The default for Swift is an HTTPS authentication URL, so swift:// and
 | 
			
		||||
    swift+https:// are the same...
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def process_specs(self):
 | 
			
		||||
        self.scheme = self.specs.get('scheme', 'swift+https')
 | 
			
		||||
        self.user = self.specs.get('user')
 | 
			
		||||
        self.key = self.specs.get('key')
 | 
			
		||||
        self.authurl = self.specs.get('authurl')
 | 
			
		||||
        self.container = self.specs.get('container')
 | 
			
		||||
        self.obj = self.specs.get('obj')
 | 
			
		||||
 | 
			
		||||
    def _get_credstring(self):
 | 
			
		||||
        if self.user:
 | 
			
		||||
            return '%s:%s@' % (self.user, self.key)
 | 
			
		||||
        return ''
 | 
			
		||||
 | 
			
		||||
    def get_uri(self):
 | 
			
		||||
        authurl = self.authurl
 | 
			
		||||
        if authurl.startswith('http://'):
 | 
			
		||||
            authurl = authurl[7:]
 | 
			
		||||
        elif authurl.startswith('https://'):
 | 
			
		||||
            authurl = authurl[8:]
 | 
			
		||||
 | 
			
		||||
        credstring = self._get_credstring()
 | 
			
		||||
        authurl = authurl.strip('/')
 | 
			
		||||
        container = self.container.strip('/')
 | 
			
		||||
        obj = self.obj.strip('/')
 | 
			
		||||
 | 
			
		||||
        return '%s://%s%s/%s/%s' % (self.scheme, credstring, authurl,
 | 
			
		||||
                                    container, obj)
 | 
			
		||||
 | 
			
		||||
    def parse_uri(self, uri):
 | 
			
		||||
        """
 | 
			
		||||
        Parse URLs. This method fixes an issue where credentials specified
 | 
			
		||||
        in the URL are interpreted differently in Python 2.6.1+ than prior
 | 
			
		||||
        versions of Python. It also deals with the peculiarity that new-style
 | 
			
		||||
        Swift URIs have where a username can contain a ':', like so:
 | 
			
		||||
 | 
			
		||||
            swift://account:user:pass@authurl.com/container/obj
 | 
			
		||||
        """
 | 
			
		||||
        # Make sure that URIs that contain multiple schemes, such as:
 | 
			
		||||
        # swift://user:pass@http://authurl.com/v1/container/obj
 | 
			
		||||
        # are immediately rejected.
 | 
			
		||||
        if uri.count('://') != 1:
 | 
			
		||||
            reason = _(
 | 
			
		||||
                    "URI cannot contain more than one occurrence of a scheme."
 | 
			
		||||
                    "If you have specified a URI like "
 | 
			
		||||
                    "swift://user:pass@http://authurl.com/v1/container/obj"
 | 
			
		||||
                    ", you need to change it to use the swift+http:// scheme, "
 | 
			
		||||
                    "like so: "
 | 
			
		||||
                    "swift+http://user:pass@authurl.com/v1/container/obj"
 | 
			
		||||
                    )
 | 
			
		||||
            raise exception.BadStoreUri(uri, reason)
 | 
			
		||||
 | 
			
		||||
        pieces = urlparse.urlparse(uri)
 | 
			
		||||
        assert pieces.scheme in ('swift', 'swift+http', 'swift+https')
 | 
			
		||||
        self.scheme = pieces.scheme
 | 
			
		||||
        netloc = pieces.netloc
 | 
			
		||||
        path = pieces.path.lstrip('/')
 | 
			
		||||
        if netloc != '':
 | 
			
		||||
            # > Python 2.6.1
 | 
			
		||||
            if '@' in netloc:
 | 
			
		||||
                creds, netloc = netloc.split('@')
 | 
			
		||||
            else:
 | 
			
		||||
                creds = None
 | 
			
		||||
        else:
 | 
			
		||||
            # Python 2.6.1 compat
 | 
			
		||||
            # see lp659445 and Python issue7904
 | 
			
		||||
            if '@' in path:
 | 
			
		||||
                creds, path = path.split('@')
 | 
			
		||||
            else:
 | 
			
		||||
                creds = None
 | 
			
		||||
            netloc = path[0:path.find('/')].strip('/')
 | 
			
		||||
            path = path[path.find('/'):].strip('/')
 | 
			
		||||
        if creds:
 | 
			
		||||
            cred_parts = creds.split(':')
 | 
			
		||||
 | 
			
		||||
            # User can be account:user, in which case cred_parts[0:2] will be
 | 
			
		||||
            # the account and user. Combine them into a single username of
 | 
			
		||||
            # account:user
 | 
			
		||||
            if len(cred_parts) == 1:
 | 
			
		||||
                reason = (_("Badly formed credentials '%(creds)s' in Swift "
 | 
			
		||||
                            "URI") % locals())
 | 
			
		||||
                raise exception.BadStoreUri(uri, reason)
 | 
			
		||||
            elif len(cred_parts) == 3:
 | 
			
		||||
                user = ':'.join(cred_parts[0:2])
 | 
			
		||||
            else:
 | 
			
		||||
                user = cred_parts[0]
 | 
			
		||||
            key = cred_parts[-1]
 | 
			
		||||
            self.user = user
 | 
			
		||||
            self.key = key
 | 
			
		||||
        else:
 | 
			
		||||
            self.user = None
 | 
			
		||||
        path_parts = path.split('/')
 | 
			
		||||
        try:
 | 
			
		||||
            self.obj = path_parts.pop()
 | 
			
		||||
            self.container = path_parts.pop()
 | 
			
		||||
            if not netloc.startswith('http'):
 | 
			
		||||
                # push hostname back into the remaining to build full authurl
 | 
			
		||||
                path_parts.insert(0, netloc)
 | 
			
		||||
                self.authurl = '/'.join(path_parts)
 | 
			
		||||
        except IndexError:
 | 
			
		||||
            reason = _("Badly formed Swift URI")
 | 
			
		||||
            raise exception.BadStoreUri(uri, reason)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def swift_auth_url(self):
 | 
			
		||||
        """
 | 
			
		||||
        Creates a fully-qualified auth url that the Swift client library can
 | 
			
		||||
        use. The scheme for the auth_url is determined using the scheme
 | 
			
		||||
        included in the `location` field.
 | 
			
		||||
 | 
			
		||||
        HTTPS is assumed, unless 'swift+http' is specified.
 | 
			
		||||
        """
 | 
			
		||||
        if self.scheme in ('swift+https', 'swift'):
 | 
			
		||||
            auth_scheme = 'https://'
 | 
			
		||||
        else:
 | 
			
		||||
            auth_scheme = 'http://'
 | 
			
		||||
 | 
			
		||||
        full_url = ''.join([auth_scheme, self.authurl.rstrip("/"), "/"])
 | 
			
		||||
        return full_url
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Store(glance.store.base.Store):
 | 
			
		||||
    """An implementation of the swift backend adapter."""
 | 
			
		||||
 | 
			
		||||
    EXAMPLE_URL = "swift://<USER>:<KEY>@<AUTH_ADDRESS>/<CONTAINER>/<FILE>"
 | 
			
		||||
 | 
			
		||||
    CHUNKSIZE = 65536
 | 
			
		||||
 | 
			
		||||
    opts = [
 | 
			
		||||
        cfg.BoolOpt('swift_enable_snet', default=False),
 | 
			
		||||
        cfg.StrOpt('swift_store_auth_address'),
 | 
			
		||||
        cfg.StrOpt('swift_store_user', secret=True),
 | 
			
		||||
        cfg.StrOpt('swift_store_key', secret=True),
 | 
			
		||||
        cfg.StrOpt('swift_store_auth_version', default='2'),
 | 
			
		||||
        cfg.StrOpt('swift_store_container',
 | 
			
		||||
                   default=DEFAULT_CONTAINER),
 | 
			
		||||
        cfg.IntOpt('swift_store_large_object_size',
 | 
			
		||||
                   default=DEFAULT_LARGE_OBJECT_SIZE),
 | 
			
		||||
        cfg.IntOpt('swift_store_large_object_chunk_size',
 | 
			
		||||
                   default=DEFAULT_LARGE_OBJECT_CHUNK_SIZE),
 | 
			
		||||
        cfg.BoolOpt('swift_store_create_container_on_put', default=False),
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
    def configure(self):
 | 
			
		||||
        self.conf.register_opts(self.opts)
 | 
			
		||||
        self.snet = self.conf.swift_enable_snet
 | 
			
		||||
        self.auth_version = self._option_get('swift_store_auth_version')
 | 
			
		||||
 | 
			
		||||
    def configure_add(self):
 | 
			
		||||
        """
 | 
			
		||||
        Configure the Store to use the stored configuration options
 | 
			
		||||
        Any store that needs special configuration should implement
 | 
			
		||||
        this method. If the store was not able to successfully configure
 | 
			
		||||
        itself, it should raise `exception.BadStoreConfiguration`
 | 
			
		||||
        """
 | 
			
		||||
        self.auth_address = self._option_get('swift_store_auth_address')
 | 
			
		||||
        self.user = self._option_get('swift_store_user')
 | 
			
		||||
        self.key = self._option_get('swift_store_key')
 | 
			
		||||
        self.container = self.conf.swift_store_container
 | 
			
		||||
        try:
 | 
			
		||||
            # The config file has swift_store_large_object_*size in MB, but
 | 
			
		||||
            # internally we store it in bytes, since the image_size parameter
 | 
			
		||||
            # passed to add() is also in bytes.
 | 
			
		||||
            self.large_object_size = \
 | 
			
		||||
                self.conf.swift_store_large_object_size * ONE_MB
 | 
			
		||||
            self.large_object_chunk_size = \
 | 
			
		||||
                self.conf.swift_store_large_object_chunk_size * ONE_MB
 | 
			
		||||
        except cfg.ConfigFileValueError, e:
 | 
			
		||||
            reason = _("Error in configuration conf: %s") % e
 | 
			
		||||
            logger.error(reason)
 | 
			
		||||
            raise exception.BadStoreConfiguration(store_name="swift",
 | 
			
		||||
                                                  reason=reason)
 | 
			
		||||
 | 
			
		||||
        self.scheme = 'swift+https'
 | 
			
		||||
        if self.auth_address.startswith('http://'):
 | 
			
		||||
            self.scheme = 'swift+http'
 | 
			
		||||
            self.full_auth_address = self.auth_address
 | 
			
		||||
        elif self.auth_address.startswith('https://'):
 | 
			
		||||
            self.full_auth_address = self.auth_address
 | 
			
		||||
        else:  # Defaults https
 | 
			
		||||
            self.full_auth_address = 'https://' + self.auth_address
 | 
			
		||||
        self.full_auth_address = ''.join([self.full_auth_address.rstrip("/"), '/'])
 | 
			
		||||
    def get(self, location):
 | 
			
		||||
        """
 | 
			
		||||
        Takes a `glance.store.location.Location` object that indicates
 | 
			
		||||
        where to find the image file, and returns a tuple of generator
 | 
			
		||||
        (for reading the image file) and image_size
 | 
			
		||||
 | 
			
		||||
        :param location `glance.store.location.Location` object, supplied
 | 
			
		||||
                        from glance.store.location.get_location_from_uri()
 | 
			
		||||
        :raises `glance.exception.NotFound` if image does not exist
 | 
			
		||||
        """
 | 
			
		||||
        loc = location.store_location
 | 
			
		||||
        swift_conn = self._make_swift_connection(
 | 
			
		||||
            auth_url=loc.swift_auth_url, user=loc.user, key=loc.key)
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            (resp_headers, resp_body) = swift_conn.get_object(
 | 
			
		||||
                container=loc.container, obj=loc.obj,
 | 
			
		||||
                resp_chunk_size=self.CHUNKSIZE)
 | 
			
		||||
        except swift_client.ClientException, e:
 | 
			
		||||
            if e.http_status == httplib.NOT_FOUND:
 | 
			
		||||
                uri = location.get_store_uri()
 | 
			
		||||
                raise exception.NotFound(_("Swift could not find image at "
 | 
			
		||||
                                         "uri %(uri)s") % locals())
 | 
			
		||||
            else:
 | 
			
		||||
                raise
 | 
			
		||||
 | 
			
		||||
        class ResponseIndexable(glance.store.Indexable):
 | 
			
		||||
            def another(self):
 | 
			
		||||
                try:
 | 
			
		||||
                    return self.wrapped.next()
 | 
			
		||||
                except StopIteration:
 | 
			
		||||
                    return ''
 | 
			
		||||
 | 
			
		||||
        length = resp_headers.get('content-length')
 | 
			
		||||
        return (ResponseIndexable(resp_body, length), length)
 | 
			
		||||
 | 
			
		||||
    def get_size(self, location):
 | 
			
		||||
        """
 | 
			
		||||
        Takes a `glance.store.location.Location` object that indicates
 | 
			
		||||
        where to find the image file, and returns the image_size (or 0
 | 
			
		||||
        if unavailable)
 | 
			
		||||
 | 
			
		||||
        :param location `glance.store.location.Location` object, supplied
 | 
			
		||||
                        from glance.store.location.get_location_from_uri()
 | 
			
		||||
        """
 | 
			
		||||
        loc = location.store_location
 | 
			
		||||
        swift_conn = self._make_swift_connection(
 | 
			
		||||
            auth_url=loc.swift_auth_url, user=loc.user, key=loc.key)
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            resp_headers = swift_conn.head_object(container=loc.container,
 | 
			
		||||
                                                  obj=loc.obj)
 | 
			
		||||
            return resp_headers.get('content-length', 0)
 | 
			
		||||
        except Exception:
 | 
			
		||||
            return 0
 | 
			
		||||
 | 
			
		||||
    def _make_swift_connection(self, auth_url, user, key):
 | 
			
		||||
        """
 | 
			
		||||
        Creates a connection using the Swift client library.
 | 
			
		||||
        """
 | 
			
		||||
        snet = self.snet
 | 
			
		||||
        auth_version = self.auth_version
 | 
			
		||||
        logger.debug(_("Creating Swift connection with "
 | 
			
		||||
                     "(auth_address=%(auth_url)s, user=%(user)s, "
 | 
			
		||||
                     "snet=%(snet)s, auth_version=%(auth_version)s)") %
 | 
			
		||||
                     locals())
 | 
			
		||||
        return swift_client.Connection(
 | 
			
		||||
            authurl=auth_url, user=user, key=key, snet=snet,
 | 
			
		||||
            auth_version=auth_version)
 | 
			
		||||
 | 
			
		||||
    def _option_get(self, param):
 | 
			
		||||
        result = getattr(self.conf, param)
 | 
			
		||||
        if not result:
 | 
			
		||||
            reason = (_("Could not find %(param)s in configuration "
 | 
			
		||||
                        "options.") % locals())
 | 
			
		||||
            logger.error(reason)
 | 
			
		||||
            raise exception.BadStoreConfiguration(store_name="swift",
 | 
			
		||||
                                                  reason=reason)
 | 
			
		||||
        return result
 | 
			
		||||
 | 
			
		||||
    def add(self, image_id, image_file, image_size):
 | 
			
		||||
        """
 | 
			
		||||
        Stores an image file with supplied identifier to the backend
 | 
			
		||||
        storage system and returns an `glance.store.ImageAddResult` object
 | 
			
		||||
        containing information about the stored image.
 | 
			
		||||
 | 
			
		||||
        :param image_id: The opaque image identifier
 | 
			
		||||
        :param image_file: The image data to write, as a file-like object
 | 
			
		||||
        :param image_size: The size of the image data to write, in bytes
 | 
			
		||||
 | 
			
		||||
        :retval `glance.store.ImageAddResult` object
 | 
			
		||||
        :raises `glance.common.exception.Duplicate` if the image already
 | 
			
		||||
                existed
 | 
			
		||||
 | 
			
		||||
        Swift writes the image data using the scheme:
 | 
			
		||||
            ``swift://<USER>:<KEY>@<AUTH_ADDRESS>/<CONTAINER>/<ID>`
 | 
			
		||||
        where:
 | 
			
		||||
            <USER> = ``swift_store_user``
 | 
			
		||||
            <KEY> = ``swift_store_key``
 | 
			
		||||
            <AUTH_ADDRESS> = ``swift_store_auth_address``
 | 
			
		||||
            <CONTAINER> = ``swift_store_container``
 | 
			
		||||
            <ID> = The id of the image being added
 | 
			
		||||
 | 
			
		||||
        :note Swift auth URLs by default use HTTPS. To specify an HTTP
 | 
			
		||||
              auth URL, you can specify http://someurl.com for the
 | 
			
		||||
              swift_store_auth_address config option
 | 
			
		||||
 | 
			
		||||
        :note Swift cannot natively/transparently handle objects >5GB
 | 
			
		||||
              in size. So, if the image is greater than 5GB, we write
 | 
			
		||||
              chunks of image data to Swift and then write an manifest
 | 
			
		||||
              to Swift that contains information about the chunks.
 | 
			
		||||
              This same chunking process is used by default for images
 | 
			
		||||
              of an unknown size, as pushing them directly to swift would
 | 
			
		||||
              fail if the image turns out to be greater than 5GB.
 | 
			
		||||
        """
 | 
			
		||||
        swift_conn = self._make_swift_connection(
 | 
			
		||||
            auth_url=self.full_auth_address, user=self.user, key=self.key)
 | 
			
		||||
 | 
			
		||||
        create_container_if_missing(self.container, swift_conn, self.conf)
 | 
			
		||||
 | 
			
		||||
        obj_name = str(image_id)
 | 
			
		||||
        location = StoreLocation({'scheme': self.scheme,
 | 
			
		||||
                                  'container': self.container,
 | 
			
		||||
                                  'obj': obj_name,
 | 
			
		||||
                                  'authurl': self.auth_address,
 | 
			
		||||
                                  'user': self.user,
 | 
			
		||||
                                  'key': self.key})
 | 
			
		||||
 | 
			
		||||
        logger.debug(_("Adding image object '%(obj_name)s' "
 | 
			
		||||
                       "to Swift") % locals())
 | 
			
		||||
        try:
 | 
			
		||||
            if image_size > 0 and image_size < self.large_object_size:
 | 
			
		||||
                # Image size is known, and is less than large_object_size.
 | 
			
		||||
                # Send to Swift with regular PUT.
 | 
			
		||||
                obj_etag = swift_conn.put_object(self.container, obj_name,
 | 
			
		||||
                                                 image_file,
 | 
			
		||||
                                                 content_length=image_size)
 | 
			
		||||
            else:
 | 
			
		||||
                # Write the image into Swift in chunks.
 | 
			
		||||
                chunk_id = 1
 | 
			
		||||
                if image_size > 0:
 | 
			
		||||
                    total_chunks = str(int(
 | 
			
		||||
                        math.ceil(float(image_size) /
 | 
			
		||||
                                  float(self.large_object_chunk_size))))
 | 
			
		||||
                else:
 | 
			
		||||
                    # image_size == 0 is when we don't know the size
 | 
			
		||||
                    # of the image. This can occur with older clients
 | 
			
		||||
                    # that don't inspect the payload size.
 | 
			
		||||
                    logger.debug(_("Cannot determine image size. Adding as a "
 | 
			
		||||
                                   "segmented object to Swift."))
 | 
			
		||||
                    total_chunks = '?'
 | 
			
		||||
 | 
			
		||||
                checksum = hashlib.md5()
 | 
			
		||||
                combined_chunks_size = 0
 | 
			
		||||
                while True:
 | 
			
		||||
                    chunk_size = self.large_object_chunk_size
 | 
			
		||||
                    if image_size == 0:
 | 
			
		||||
                        content_length = None
 | 
			
		||||
                    else:
 | 
			
		||||
                        left = image_size - combined_chunks_size
 | 
			
		||||
                        if left == 0:
 | 
			
		||||
                            break
 | 
			
		||||
                        if chunk_size > left:
 | 
			
		||||
                            chunk_size = left
 | 
			
		||||
                        content_length = chunk_size
 | 
			
		||||
 | 
			
		||||
                    chunk_name = "%s-%05d" % (obj_name, chunk_id)
 | 
			
		||||
                    reader = ChunkReader(image_file, checksum, chunk_size)
 | 
			
		||||
                    chunk_etag = swift_conn.put_object(
 | 
			
		||||
                        self.container, chunk_name, reader,
 | 
			
		||||
                        content_length=content_length)
 | 
			
		||||
                    bytes_read = reader.bytes_read
 | 
			
		||||
                    logger.debug(_("Wrote chunk %(chunk_id)d/"
 | 
			
		||||
                                   "%(total_chunks)s of length %(bytes_read)d "
 | 
			
		||||
                                   "to Swift returning MD5 of content: "
 | 
			
		||||
                                   "%(chunk_etag)s")
 | 
			
		||||
                                 % locals())
 | 
			
		||||
 | 
			
		||||
                    if bytes_read == 0:
 | 
			
		||||
                        # Delete the last chunk, because it's of zero size.
 | 
			
		||||
                        # This will happen if image_size == 0.
 | 
			
		||||
                        logger.debug(_("Deleting final zero-length chunk"))
 | 
			
		||||
                        swift_conn.delete_object(self.container, chunk_name)
 | 
			
		||||
                        break
 | 
			
		||||
 | 
			
		||||
                    chunk_id += 1
 | 
			
		||||
                    combined_chunks_size += bytes_read
 | 
			
		||||
 | 
			
		||||
                # In the case we have been given an unknown image size,
 | 
			
		||||
                # set the image_size to the total size of the combined chunks.
 | 
			
		||||
                if image_size == 0:
 | 
			
		||||
                    image_size = combined_chunks_size
 | 
			
		||||
 | 
			
		||||
                # Now we write the object manifest and return the
 | 
			
		||||
                # manifest's etag...
 | 
			
		||||
                manifest = "%s/%s" % (self.container, obj_name)
 | 
			
		||||
                headers = {'ETag': hashlib.md5("").hexdigest(),
 | 
			
		||||
                           'X-Object-Manifest': manifest}
 | 
			
		||||
 | 
			
		||||
                # The ETag returned for the manifest is actually the
 | 
			
		||||
                # MD5 hash of the concatenated checksums of the strings
 | 
			
		||||
                # of each chunk...so we ignore this result in favour of
 | 
			
		||||
                # the MD5 of the entire image file contents, so that
 | 
			
		||||
                # users can verify the image file contents accordingly
 | 
			
		||||
                swift_conn.put_object(self.container, obj_name,
 | 
			
		||||
                                      None, headers=headers)
 | 
			
		||||
                obj_etag = checksum.hexdigest()
 | 
			
		||||
 | 
			
		||||
            # NOTE: We return the user and key here! Have to because
 | 
			
		||||
            # location is used by the API server to return the actual
 | 
			
		||||
            # image data. We *really* should consider NOT returning
 | 
			
		||||
            # the location attribute from GET /images/<ID> and
 | 
			
		||||
            # GET /images/details
 | 
			
		||||
 | 
			
		||||
            return (location.get_uri(), image_size, obj_etag)
 | 
			
		||||
        except swift_client.ClientException, e:
 | 
			
		||||
            if e.http_status == httplib.CONFLICT:
 | 
			
		||||
                raise exception.Duplicate(_("Swift already has an image at "
 | 
			
		||||
                                          "location %s") % location.get_uri())
 | 
			
		||||
            msg = (_("Failed to add object to Swift.\n"
 | 
			
		||||
                   "Got error from Swift: %(e)s") % locals())
 | 
			
		||||
            logger.error(msg)
 | 
			
		||||
            raise glance.store.BackendException(msg)
 | 
			
		||||
 | 
			
		||||
    def delete(self, location):
 | 
			
		||||
        """
 | 
			
		||||
        Takes a `glance.store.location.Location` object that indicates
 | 
			
		||||
        where to find the image file to delete
 | 
			
		||||
 | 
			
		||||
        :location `glance.store.location.Location` object, supplied
 | 
			
		||||
                  from glance.store.location.get_location_from_uri()
 | 
			
		||||
 | 
			
		||||
        :raises NotFound if image does not exist
 | 
			
		||||
        """
 | 
			
		||||
        loc = location.store_location
 | 
			
		||||
        swift_conn = self._make_swift_connection(
 | 
			
		||||
            auth_url=loc.swift_auth_url, user=loc.user, key=loc.key)
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            # We request the manifest for the object. If one exists,
 | 
			
		||||
            # that means the object was uploaded in chunks/segments,
 | 
			
		||||
            # and we need to delete all the chunks as well as the
 | 
			
		||||
            # manifest.
 | 
			
		||||
            manifest = None
 | 
			
		||||
            try:
 | 
			
		||||
                headers = swift_conn.head_object(loc.container, loc.obj)
 | 
			
		||||
                manifest = headers.get('x-object-manifest')
 | 
			
		||||
            except swift_client.ClientException, e:
 | 
			
		||||
                if e.http_status != httplib.NOT_FOUND:
 | 
			
		||||
                    raise
 | 
			
		||||
            if manifest:
 | 
			
		||||
                # Delete all the chunks before the object manifest itself
 | 
			
		||||
                obj_container, obj_prefix = manifest.split('/', 1)
 | 
			
		||||
                for segment in swift_conn.get_container(obj_container,
 | 
			
		||||
                                                        prefix=obj_prefix)[1]:
 | 
			
		||||
                    # TODO(jaypipes): This would be an easy area to parallelize
 | 
			
		||||
                    # since we're simply sending off parallelizable requests
 | 
			
		||||
                    # to Swift to delete stuff. It's not like we're going to
 | 
			
		||||
                    # be hogging up network or file I/O here...
 | 
			
		||||
                    swift_conn.delete_object(obj_container, segment['name'])
 | 
			
		||||
 | 
			
		||||
            else:
 | 
			
		||||
                swift_conn.delete_object(loc.container, loc.obj)
 | 
			
		||||
 | 
			
		||||
        except swift_client.ClientException, e:
 | 
			
		||||
            if e.http_status == httplib.NOT_FOUND:
 | 
			
		||||
                uri = location.get_store_uri()
 | 
			
		||||
                raise exception.NotFound(_("Swift could not find image at "
 | 
			
		||||
                                         "uri %(uri)s") % locals())
 | 
			
		||||
            else:
 | 
			
		||||
                raise
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ChunkReader(object):
 | 
			
		||||
    def __init__(self, fd, checksum, total):
 | 
			
		||||
        self.fd = fd
 | 
			
		||||
        self.checksum = checksum
 | 
			
		||||
        self.total = total
 | 
			
		||||
        self.bytes_read = 0
 | 
			
		||||
 | 
			
		||||
    def read(self, i):
 | 
			
		||||
        left = self.total - self.bytes_read
 | 
			
		||||
        if i > left:
 | 
			
		||||
            i = left
 | 
			
		||||
        result = self.fd.read(i)
 | 
			
		||||
        self.bytes_read += len(result)
 | 
			
		||||
        self.checksum.update(result)
 | 
			
		||||
        return result
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def create_container_if_missing(container, swift_conn, conf):
 | 
			
		||||
    """
 | 
			
		||||
    Creates a missing container in Swift if the
 | 
			
		||||
    ``swift_store_create_container_on_put`` option is set.
 | 
			
		||||
 | 
			
		||||
    :param container: Name of container to create
 | 
			
		||||
    :param swift_conn: Connection to Swift
 | 
			
		||||
    :param conf: Option mapping
 | 
			
		||||
    """
 | 
			
		||||
    try:
 | 
			
		||||
        swift_conn.head_container(container)
 | 
			
		||||
    except swift_client.ClientException, e:
 | 
			
		||||
        if e.http_status == httplib.NOT_FOUND:
 | 
			
		||||
            if conf.swift_store_create_container_on_put:
 | 
			
		||||
                try:
 | 
			
		||||
                    swift_conn.put_container(container)
 | 
			
		||||
                except swift_client.ClientException, e:
 | 
			
		||||
                    msg = _("Failed to add container to Swift.\n"
 | 
			
		||||
                           "Got error from Swift: %(e)s") % locals()
 | 
			
		||||
                    raise glance.store.BackendException(msg)
 | 
			
		||||
            else:
 | 
			
		||||
                msg = (_("The container %(container)s does not exist in "
 | 
			
		||||
                       "Swift. Please set the "
 | 
			
		||||
                       "swift_store_create_container_on_put option"
 | 
			
		||||
                       "to add container to Swift automatically.")
 | 
			
		||||
                       % locals())
 | 
			
		||||
                raise glance.store.BackendException(msg)
 | 
			
		||||
        else:
 | 
			
		||||
            raise
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
glance.store.register_store(__name__, ['swift', 'swift+http', 'swift+https'])
 | 
			
		||||
		Reference in New Issue
	
	Block a user