blob: 34aca589f44a871ce45d7c8278666d16cfbdfb38 [file] [log] [blame]
import logging
import tempfile
import shutil
import os
try:
from urllib.parse import urlsplit
except ImportError:
from urlparse import urlsplit
GLANCE_LOADED = False
CMD_LOADED = False
def __virtual__():
if 'glancev2.image_list' in __salt__:
global GLANCE_LOADED
GLANCE_LOADED = True
if 'cmd.run_all' in __salt__:
global CMD_LOADED
CMD_LOADED = True
return 'barbicanv1' if 'barbicanv1.secret_list' in __salt__ else False
log = logging.getLogger(__name__)
def _barbicanv1_call(fname, *args, **kwargs):
return __salt__['barbicanv1.{}'.format(fname)](*args, **kwargs)
def _glancev2_call(fname, *args, **kwargs):
return __salt__['glancev2.{}'.format(fname)](*args, **kwargs)
def _cmd_call(fname, *args, **kwargs):
return __salt__['cmd.{}'.format(fname)](*args, **kwargs)
def secret_present(name, cloud_name, **kwargs):
try:
exact_secret = _barbicanv1_call(
'secret_get_details', name=name, cloud_name=cloud_name
)
except Exception as e:
if 'ResourceNotFound' in repr(e):
try:
if not kwargs:
kwargs = {}
resp = _barbicanv1_call(
'secret_create', name=name, cloud_name=cloud_name, **kwargs
)
except Exception as e:
log.error('Barbicanv1 create secret failed with {}'.format(e))
return _create_failed(name, 'secret')
return _created(name, 'secret', resp)
if 'MultipleResourcesFound' in repr(e):
return _find_failed(name, 'secret')
if 'payload' in kwargs:
try:
_barbicanv1_call(
'secret_payload_get', name=name, cloud_name=cloud_name
)
except Exception:
try:
_barbicanv1_call(
'secret_payload_set', name=name,
cloud_name=cloud_name, **kwargs
)
except Exception as e:
log.error(
'Barbicanv1 Secret set payload failed with {}'.format(e)
)
return _update_failed(name, 'secret_payload')
return _updated(
name, 'secret_payload', {'payload': kwargs['payload']}
)
return _no_changes(name, 'secret')
def secret_absent(name, cloud_name, **kwargs):
try:
secret = _barbicanv1_call(
'secret_get_details', name=name, cloud_name=cloud_name
)
except Exception as e:
if 'ResourceNotFound' in repr(e):
return _absent(name, 'secret')
if 'MultipleResourcesFound' in repr(e):
return _find_failed(name, 'secret')
try:
_barbicanv1_call('secret_delete', name=name, cloud_name=cloud_name)
except Exception as e:
log.error('Barbicanv1 delete failed with {}'.format(e))
return _delete_failed(name, 'secret')
return _deleted(name, 'secret')
def glance_image_signed(name, secret_name, pk_fname, out_fname,
cloud_name, file_name=None, force_resign=False,
image_name=None):
"""
:param name: The name of the image to sign
:param secret_name: Secret's name with certificate
:param pk_fname: private_key file name
:param out_fname: output filename for signature
:param cloud_name: name of the cloud in cloud_yaml
:param file_name: name of the file where downloaded image is.
:param force_resign: if the image is already signed, resign it.
:param image_name: The name of the image to sign DEPRECATED, use name instead.
"""
# TODO(vsaeinko): remove once deprecation period passed in Q2.2019.
if image_name is not None:
name = image_name
if not GLANCE_LOADED or not CMD_LOADED:
return {
'name': name,
'changes': {},
'comment': 'Cant sign an image, glancev2 and/or cmd module '
'are/is absent',
'result': False,
}
try:
image = _glancev2_call(
'image_get_details', name=name, cloud_name=cloud_name
)
except Exception as e:
log.error('Barbicanv1 sign_image find image failed with {}'.format(e))
return _create_failed(name, 'image')
sign_properties = (
'img_signature', 'img_signature_certificate_uuid',
'img_signature_hash_method', 'img_signature_key_type',
)
if not force_resign and all(key in image for key in sign_properties):
return _no_changes(name, 'image_signature')
file_name = file_name or image['id']
dir_path = tempfile.mkdtemp()
try:
file_path = os.path.join(dir_path, file_name)
_glancev2_call(
'image_download', name=name,
file_name=file_path,
cloud_name=cloud_name
)
except Exception as e:
log.error(
"Barbicanv1 sign image can't download image."
" failed with {}".format(e)
)
return _create_failed(name, 'downloading_image')
try:
retcode = _cmd_call(
'run_all',
'openssl dgst -sha256 -sign {} '.format(pk_fname) +
'-sigopt rsa_padding_mode:pss -out {} '.format(out_fname) +
file_path
)['retcode']
if not retcode == 0:
raise Exception('Cant sign image')
image_signature = _cmd_call(
'run_all', 'base64 -w 0 {}'.format(out_fname)
)['stdout']
except Exception as e:
log.error(
'Barbicanv1 sign image failed because of cmd with {}'.format(e)
)
return _create_failed(name, 'cmd_module')
shutil.rmtree(dir_path)
secret_ref = _barbicanv1_call(
'secret_get_details', name=secret_name, cloud_name=cloud_name
)['secret_ref']
def _parse_secret_href(href):
return urlsplit(href).path.split('/')[-1]
secret_uuid = _parse_secret_href(secret_ref)
to_update = [
{
'op': 'add',
'path': '/img_signature',
'value': image_signature,
},
{
'op': 'add',
'path': '/img_signature_certificate_uuid',
'value': secret_uuid,
},
{
'op': 'add',
'path': '/img_signature_hash_method',
'value': 'SHA-256',
},
{
'op': 'add',
'path': '/img_signature_key_type',
'value': 'RSA-PSS'
}
]
try:
resp = _glancev2_call(
'image_update', name, to_update, cloud_name=cloud_name,
headers={
"Content-Type": "application/openstack-images-v2.1-json-patch"
}
)
except Exception as e:
log.error('Barbicanv1 sign image failed with {}'.format(e))
return _create_failed(name, 'sign_image')
return _created(name, 'sign_image', resp)
def secret_acl_present(name, cloud_name, **kwargs):
try:
secret = _barbicanv1_call(
'secret_get_details', name=name, cloud_name=cloud_name
)
except Exception as e:
if 'ResourceNotFound' in repr(e):
return _absent(name, 'secret')
if 'MultipleResourcesFound' in repr(e):
return _find_failed(name, 'secret')
try:
resp = _barbicanv1_call('secret_acl_get', name=name,
cloud_name=cloud_name)
except Exception as e:
if 'ResourceNotFound' in repr(e):
resp = _barbicanv1_call('secret_acl_put', name=name,
cloud_name=cloud_name, **kwargs)
return _created(name, 'acl', resp)
else:
log.error('Add acl for user faild with {}'.format(e))
return _create_failed(name, 'acl')
missing_users = [user_id
for user_id in kwargs.get('users', [])
if user_id not in resp['read'].get('users', [])]
if missing_users:
kwargs['users'] = missing_users
resp = _barbicanv1_call('secret_acl_put', name=name,
cloud_name=cloud_name, **kwargs)
return _created(name, 'acl', resp)
return _no_changes(name, 'acl')
def _created(name, resource, resource_definition):
changes_dict = {
'name': name,
'changes': resource_definition,
'result': True,
'comment': '{}{} created'.format(resource, name)
}
return changes_dict
def _updated(name, resource, resource_definition):
changes_dict = {
'name': name,
'changes': resource_definition,
'result': True,
'comment': '{}{} updated'.format(resource, name)
}
return changes_dict
def _no_changes(name, resource):
changes_dict = {
'name': name,
'changes': {},
'result': True,
'comment': '{}{} is in desired state'.format(resource, name)
}
return changes_dict
def _deleted(name, resource):
changes_dict = {
'name': name,
'changes': {},
'result': True,
'comment': '{}{} removed'.format(resource, name)
}
return changes_dict
def _absent(name, resource):
changes_dict = {'name': name,
'changes': {},
'comment': '{0} {1} not present'.format(resource, name),
'result': True}
return changes_dict
def _delete_failed(name, resource):
changes_dict = {'name': name,
'changes': {},
'comment': '{0} {1} failed to delete'.format(resource,
name),
'result': False}
return changes_dict
def _create_failed(name, resource):
changes_dict = {'name': name,
'changes': {},
'comment': '{0} {1} failed to create'.format(resource,
name),
'result': False}
return changes_dict
def _update_failed(name, resource):
changes_dict = {'name': name,
'changes': {},
'comment': '{0} {1} failed to update'.format(resource,
name),
'result': False}
return changes_dict
def _find_failed(name, resource):
changes_dict = {
'name': name,
'changes': {},
'comment': '{0} {1} found multiple {0}'.format(resource, name),
'result': False,
}
return changes_dict