blob: 92281ab9e585da3c9eed96c1b5de57095981343a [file] [log] [blame]
#########
# Copyright (c) 2014 GigaSpaces Technologies Ltd. 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.
import os
import errno
from getpass import getuser
from cloudify import ctx
from cloudify.decorators import operation
from cloudify.exceptions import NonRecoverableError
from openstack_plugin_common import (
with_nova_client,
validate_resource,
use_external_resource,
transform_resource_name,
is_external_resource,
is_external_resource_not_conditionally_created,
delete_runtime_properties,
get_resource_id,
delete_resource_and_runtime_properties,
OPENSTACK_ID_PROPERTY,
OPENSTACK_TYPE_PROPERTY,
OPENSTACK_NAME_PROPERTY,
COMMON_RUNTIME_PROPERTIES_KEYS
)
RUNTIME_PROPERTIES_KEYS = COMMON_RUNTIME_PROPERTIES_KEYS
KEYPAIR_OPENSTACK_TYPE = 'keypair'
PRIVATE_KEY_PATH_PROP = 'private_key_path'
@operation
@with_nova_client
def create(nova_client, args, **kwargs):
private_key_path = _get_private_key_path()
pk_exists = _check_private_key_exists(private_key_path)
if use_external_resource(ctx, nova_client, KEYPAIR_OPENSTACK_TYPE):
if not pk_exists:
delete_runtime_properties(ctx, RUNTIME_PROPERTIES_KEYS)
raise NonRecoverableError(
'Failed to use external keypair (node {0}): the public key {1}'
' is available on Openstack, but the private key could not be '
'found at {2}'.format(ctx.node.id,
ctx.node.properties['resource_id'],
private_key_path))
return
if pk_exists:
raise NonRecoverableError(
"Can't create keypair - private key path already exists: {0}"
.format(private_key_path))
keypair = {
'name': get_resource_id(ctx, KEYPAIR_OPENSTACK_TYPE),
}
keypair.update(ctx.node.properties['keypair'], **args)
transform_resource_name(ctx, keypair)
keypair = nova_client.keypairs.create(keypair['name'],
keypair.get('public_key'))
ctx.instance.runtime_properties[OPENSTACK_ID_PROPERTY] = keypair.id
ctx.instance.runtime_properties[OPENSTACK_TYPE_PROPERTY] = \
KEYPAIR_OPENSTACK_TYPE
ctx.instance.runtime_properties[OPENSTACK_NAME_PROPERTY] = keypair.name
try:
# write private key file
_mkdir_p(os.path.dirname(private_key_path))
with open(private_key_path, 'w') as f:
f.write(keypair.private_key)
os.chmod(private_key_path, 0600)
except Exception:
_delete_private_key_file()
delete_resource_and_runtime_properties(ctx, nova_client,
RUNTIME_PROPERTIES_KEYS)
raise
@operation
@with_nova_client
def delete(nova_client, **kwargs):
if not is_external_resource(ctx):
ctx.logger.info('deleting keypair')
_delete_private_key_file()
nova_client.keypairs.delete(
ctx.instance.runtime_properties[OPENSTACK_ID_PROPERTY])
else:
ctx.logger.info('not deleting keypair since an external keypair is '
'being used')
delete_runtime_properties(ctx, RUNTIME_PROPERTIES_KEYS)
@operation
@with_nova_client
def creation_validation(nova_client, **kwargs):
def validate_private_key_permissions(private_key_path):
ctx.logger.debug('checking whether private key file {0} has the '
'correct permissions'.format(private_key_path))
if not os.access(private_key_path, os.R_OK):
err = 'private key file {0} is not readable'\
.format(private_key_path)
ctx.logger.error('VALIDATION ERROR: ' + err)
raise NonRecoverableError(err)
ctx.logger.debug('OK: private key file {0} has the correct '
'permissions'.format(private_key_path))
def validate_path_owner(path):
ctx.logger.debug('checking whether directory {0} is owned by the '
'current user'.format(path))
from pwd import getpwnam, getpwuid
user = getuser()
owner = getpwuid(os.stat(path).st_uid).pw_name
current_user_id = str(getpwnam(user).pw_uid)
owner_id = str(os.stat(path).st_uid)
if not current_user_id == owner_id:
err = '{0} is not owned by the current user (it is owned by {1})'\
.format(path, owner)
ctx.logger.warning('VALIDATION WARNING: {0}'.format(err))
return
ctx.logger.debug('OK: {0} is owned by the current user'.format(path))
validate_resource(ctx, nova_client, KEYPAIR_OPENSTACK_TYPE)
private_key_path = _get_private_key_path()
pk_exists = _check_private_key_exists(private_key_path)
if is_external_resource_not_conditionally_created(ctx):
if pk_exists:
if os.name == 'posix':
validate_private_key_permissions(private_key_path)
validate_path_owner(private_key_path)
else:
err = "can't use external keypair: the public key {0} is " \
"available on Openstack, but the private key could not be " \
"found at {1}".format(ctx.node.properties['resource_id'],
private_key_path)
ctx.logger.error('VALIDATION ERROR: {0}'.format(err))
raise NonRecoverableError(err)
else:
if pk_exists:
err = 'private key path already exists: {0}'.format(
private_key_path)
ctx.logger.error('VALIDATION ERROR: {0}'.format(err))
raise NonRecoverableError(err)
else:
err = 'private key directory {0} is not writable'
while private_key_path:
if os.path.isdir(private_key_path):
if not os.access(private_key_path, os.W_OK | os.X_OK):
raise NonRecoverableError(err.format(private_key_path))
else:
break
private_key_path, _ = os.path.split(private_key_path)
ctx.logger.debug('OK: keypair configuration is valid')
def _get_private_key_path():
return os.path.expanduser(ctx.node.properties[PRIVATE_KEY_PATH_PROP])
def _delete_private_key_file():
private_key_path = _get_private_key_path()
ctx.logger.debug('deleting private key file at {0}'.format(
private_key_path))
try:
os.remove(private_key_path)
except OSError as e:
if e.errno == errno.ENOENT:
# file was already deleted somehow
pass
raise
def _check_private_key_exists(private_key_path):
return os.path.isfile(private_key_path)
def _mkdir_p(path):
if path and not os.path.isdir(path):
os.makedirs(path)