1
# vim: tabstop=4 shiftwidth=4 softtabstop=4
4
# Licensed under the Apache License, Version 2.0 (the "License"); you may
5
# not use this file except in compliance with the License. You may obtain
6
# a copy of the License at
8
# http://www.apache.org/licenses/LICENSE-2.0
10
# Unless required by applicable law or agreed to in writing, software
11
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
# License for the specific language governing permissions and limitations
15
"""Utilities for Resources that use the Openstack Nova API."""
17
from email.mime.multipart import MIMEMultipart
18
from email.mime.text import MIMEText
24
from urlparse import urlparse
26
from oslo.config import cfg
28
from heat.common import exception
29
from heat.engine import clients
30
from heat.openstack.common import log as logging
31
from heat.openstack.common import uuidutils
33
logger = logging.getLogger(__name__)
36
def get_image_id(nova_client, image_identifier):
38
Return an id for the specified image name or identifier.
40
:param nova_client: the nova client to use
41
:param image_identifier: image name or a UUID-like identifier
42
:returns: the id of the requested :image_identifier:
43
:raises: exception.ImageNotFound, exception.NoUniqueImageFound
46
if uuidutils.is_uuid_like(image_identifier):
48
image_id = nova_client.images.get(image_identifier).id
49
except clients.novaclient.exceptions.NotFound:
50
logger.info("Image %s was not found in glance"
52
raise exception.ImageNotFound(image_name=image_identifier)
55
image_list = nova_client.images.list()
56
except clients.novaclient.exceptions.ClientException as ex:
57
raise exception.ServerError(message=str(ex))
60
for o in image_list if o.name == image_identifier)
61
if len(image_names) == 0:
62
logger.info("Image %s was not found in glance" %
64
raise exception.ImageNotFound(image_name=image_identifier)
65
elif len(image_names) > 1:
66
logger.info("Mulitple images %s were found in glance with name"
68
raise exception.NoUniqueImageFound(image_name=image_identifier)
69
image_id = image_names.popitem()[0]
73
def get_flavor_id(nova_client, flavor):
75
Get the id for the specified flavor name.
77
:param nova_client: the nova client to use
78
:param flavor: the name of the flavor to find
79
:returns: the id of :flavor:
80
:raises: exception.FlavorMissing
83
flavor_list = nova_client.flavors.list()
89
raise exception.FlavorMissing(flavor_id=flavor)
93
def get_keypair(nova_client, key_name):
95
Get the public key specified by :key_name:
97
:param nova_client: the nova client to use
98
:param key_name: the name of the key to look for
99
:returns: the keypair (name, public_key) for :key_name:
100
:raises: exception.UserKeyPairMissing
102
for keypair in nova_client.keypairs.list():
103
if keypair.name == key_name:
105
raise exception.UserKeyPairMissing(key_name=key_name)
108
def build_userdata(resource, userdata=None):
110
Build multipart data blob for CloudInit which includes user-supplied
111
Metadata, user data, and the required Heat in-instance configuration.
113
:param resource: the resource implementation
114
:type resource: heat.engine.Resource
115
:param userdata: user data string
116
:type userdata: str or None
117
:returns: multipart mime as a string
120
def make_subpart(content, filename, subtype=None):
122
subtype = os.path.splitext(filename)[0]
123
msg = MIMEText(content, _subtype=subtype)
124
msg.add_header('Content-Disposition', 'attachment',
128
def read_cloudinit_file(fn):
129
data = pkgutil.get_data('heat', 'cloudinit/%s' % fn)
130
data = data.replace('@INSTANCE_USER@',
131
cfg.CONF.instance_user)
134
attachments = [(read_cloudinit_file('config'), 'cloud-config'),
135
(read_cloudinit_file('boothook.sh'), 'boothook.sh',
137
(read_cloudinit_file('part_handler.py'),
139
(userdata, 'cfn-userdata', 'x-cfninitdata'),
140
(read_cloudinit_file('loguserdata.py'),
141
'loguserdata.py', 'x-shellscript')]
143
if 'Metadata' in resource.t:
144
attachments.append((json.dumps(resource.metadata),
145
'cfn-init-data', 'x-cfninitdata'))
147
attachments.append((cfg.CONF.heat_watch_server_url,
148
'cfn-watch-server', 'x-cfninitdata'))
150
attachments.append((cfg.CONF.heat_metadata_server_url,
151
'cfn-metadata-server', 'x-cfninitdata'))
153
# Create a boto config which the cfntools on the host use to know
154
# where the cfn and cw API's are to be accessed
155
cfn_url = urlparse(cfg.CONF.heat_metadata_server_url)
156
cw_url = urlparse(cfg.CONF.heat_watch_server_url)
157
is_secure = cfg.CONF.instance_connection_is_secure
158
vcerts = cfg.CONF.instance_connection_https_validate_certificates
159
boto_cfg = "\n".join(["[Boto]",
161
"is_secure = %s" % is_secure,
162
"https_validate_certificates = %s" % vcerts,
163
"cfn_region_name = heat",
164
"cfn_region_endpoint = %s" %
166
"cloudwatch_region_name = heat",
167
"cloudwatch_region_endpoint = %s" %
169
attachments.append((boto_cfg,
170
'cfn-boto-cfg', 'x-cfninitdata'))
172
subparts = [make_subpart(*args) for args in attachments]
173
mime_blob = MIMEMultipart(_subparts=subparts)
175
return mime_blob.as_string()