mistral/mistral/actions/openstack/actions.py

567 lines
16 KiB
Python

# Copyright 2014 - Mirantis, 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.
import functools
from barbicanclient import client as barbicanclient
from ceilometerclient.v2 import client as ceilometerclient
from cinderclient.v2 import client as cinderclient
from glanceclient.v2 import client as glanceclient
from heatclient.v1 import client as heatclient
from ironic_inspector_client import v1 as ironic_inspector_client
from ironicclient.v1 import client as ironicclient
from keystoneclient.auth import identity
from keystoneclient import httpclient
from keystoneclient.v3 import client as keystoneclient
from mistralclient.api.v2 import client as mistralclient
from neutronclient.v2_0 import client as neutronclient
from novaclient import client as novaclient
from oslo_config import cfg
from oslo_log import log
from swiftclient import client as swift_client
from troveclient import client as troveclient
from zaqarclient.queues.v2 import client as zaqarclient
from mistral.actions.openstack import base
from mistral import context
from mistral.utils import inspect_utils
from mistral.utils.openstack import keystone as keystone_utils
LOG = log.getLogger(__name__)
CONF = cfg.CONF
class NovaAction(base.OpenStackAction):
def _get_client(self):
ctx = context.ctx()
LOG.debug("Nova action security context: %s" % ctx)
keystone_endpoint = keystone_utils.get_keystone_endpoint_v2()
nova_endpoint = keystone_utils.get_endpoint_for_project('nova')
client = novaclient.Client(
2,
username=None,
api_key=None,
endpoint_type='publicURL',
service_type='compute',
auth_token=ctx.auth_token,
tenant_id=ctx.project_id,
region_name=keystone_endpoint.region,
auth_url=keystone_endpoint.url
)
client.client.management_url = keystone_utils.format_url(
nova_endpoint.url,
{'tenant_id': ctx.project_id}
)
return client
@classmethod
def _get_fake_client(cls):
return novaclient.Client(2)
class GlanceAction(base.OpenStackAction):
_client_class = glanceclient.Client
def _get_client(self):
ctx = context.ctx()
LOG.debug("Glance action security context: %s" % ctx)
glance_endpoint = keystone_utils.get_endpoint_for_project('glance')
return self._client_class(
glance_endpoint.url,
region_name=glance_endpoint.region,
token=ctx.auth_token
)
@classmethod
def _get_fake_client(cls):
return cls._client_class("")
class KeystoneAction(base.OpenStackAction):
_client_class = keystoneclient.Client
def _get_client(self):
ctx = context.ctx()
LOG.debug("Keystone action security context: %s" % ctx)
kwargs = {
'token': ctx.auth_token,
'auth_url': CONF.keystone_authtoken.auth_uri,
'project_id': ctx.project_id,
'cacert': CONF.keystone_authtoken.cafile,
}
# In case of trust-scoped token explicitly pass endpoint parameter.
if (ctx.is_trust_scoped
or keystone_utils.is_token_trust_scoped(ctx.auth_token)):
kwargs['endpoint'] = CONF.keystone_authtoken.auth_uri
client = self._client_class(**kwargs)
client.management_url = CONF.keystone_authtoken.auth_uri
return client
@classmethod
def _get_fake_client(cls):
# Here we need to replace httpclient authenticate method temporarily
authenticate = httpclient.HTTPClient.authenticate
httpclient.HTTPClient.authenticate = lambda x: True
fake_client = cls._client_class()
# Once we get fake client, return back authenticate method
httpclient.HTTPClient.authenticate = authenticate
return fake_client
class CeilometerAction(base.OpenStackAction):
_client_class = ceilometerclient.Client
def _get_client(self):
ctx = context.ctx()
LOG.debug("Ceilometer action security context: %s" % ctx)
ceilometer_endpoint = keystone_utils.get_endpoint_for_project(
'ceilometer'
)
endpoint_url = keystone_utils.format_url(
ceilometer_endpoint.url,
{'tenant_id': ctx.project_id}
)
return self._client_class(
endpoint_url,
region_name=ceilometer_endpoint.region,
token=ctx.auth_token,
username=ctx.user_name
)
@classmethod
def _get_fake_client(cls):
return cls._client_class("")
class HeatAction(base.OpenStackAction):
_client_class = heatclient.Client
def _get_client(self):
ctx = context.ctx()
LOG.debug("Heat action security context: %s" % ctx)
heat_endpoint = keystone_utils.get_endpoint_for_project('heat')
endpoint_url = keystone_utils.format_url(
heat_endpoint.url,
{'tenant_id': ctx.project_id}
)
return self._client_class(
endpoint_url,
region_name=heat_endpoint.region,
token=ctx.auth_token,
username=ctx.user_name
)
@classmethod
def _get_fake_client(cls):
return cls._client_class("")
class NeutronAction(base.OpenStackAction):
_client_class = neutronclient.Client
def _get_client(self):
ctx = context.ctx()
LOG.debug("Neutron action security context: %s" % ctx)
neutron_endpoint = keystone_utils.get_endpoint_for_project('neutron')
return self._client_class(
endpoint_url=neutron_endpoint.url,
region_name=neutron_endpoint.region,
token=ctx.auth_token,
auth_url=CONF.keystone_authtoken.auth_uri
)
class CinderAction(base.OpenStackAction):
_client_class = cinderclient.Client
def _get_client(self):
ctx = context.ctx()
LOG.debug("Cinder action security context: %s" % ctx)
cinder_endpoint = keystone_utils.get_endpoint_for_project(
service_type='volumev2'
)
cinder_url = keystone_utils.format_url(
cinder_endpoint.url,
{'tenant_id': ctx.project_id}
)
client = self._client_class(
ctx.user_name,
ctx.auth_token,
project_id=ctx.project_id,
auth_url=cinder_url,
region_name=cinder_endpoint.region
)
client.client.auth_token = ctx.auth_token
client.client.management_url = cinder_url
return client
@classmethod
def _get_fake_client(cls):
return cls._client_class()
class MistralAction(base.OpenStackAction):
_client_class = mistralclient.Client
def _get_client(self):
ctx = context.ctx()
LOG.debug("Mistral action security context: %s" % ctx)
# Check for trust scope token. This may occur if the action is
# called from a workflow triggered by a Mistral cron trigger.
if ctx.is_trust_scoped:
auth_url = None
mistral_endpoint = keystone_utils.get_endpoint_for_project(
'mistral'
)
mistral_url = mistral_endpoint.url
else:
keystone_endpoint = keystone_utils.get_keystone_endpoint_v2()
auth_url = keystone_endpoint.url
mistral_url = None
return self._client_class(
mistral_url=mistral_url,
auth_token=ctx.auth_token,
project_id=ctx.project_id,
user_id=ctx.user_id,
auth_url=auth_url
)
@classmethod
def _get_fake_client(cls):
return cls._client_class()
class TroveAction(base.OpenStackAction):
_client_class = troveclient.Client
def _get_client(self):
ctx = context.ctx()
LOG.debug("Trove action security context: %s" % ctx)
trove_endpoint = keystone_utils.get_endpoint_for_project(
service_type='database'
)
trove_url = keystone_utils.format_url(
trove_endpoint.url,
{'tenant_id': ctx.project_id}
)
client = self._client_class(
ctx.user_name,
ctx.auth_token,
project_id=ctx.project_id,
auth_url=trove_url,
region_name=trove_endpoint.region
)
client.client.auth_token = ctx.auth_token
client.client.management_url = trove_url
return client
@classmethod
def _get_fake_client(cls):
return cls._client_class()
class IronicAction(base.OpenStackAction):
_client_class = ironicclient.Client
def _get_client(self):
ctx = context.ctx()
LOG.debug("Ironic action security context: %s" % ctx)
ironic_endpoint = keystone_utils.get_endpoint_for_project('ironic')
return self._client_class(
ironic_endpoint.url,
token=ctx.auth_token,
region_name=ironic_endpoint.region
)
@classmethod
def _get_fake_client(cls):
return cls._client_class("http://127.0.0.1:6385/")
class BaremetalIntrospectionAction(base.OpenStackAction):
_client_class = ironic_inspector_client.ClientV1
def _get_client(self):
ctx = context.ctx()
LOG.debug("Baremetal introspection action security context: %s" % ctx)
inspector_endpoint = keystone_utils.get_endpoint_for_project(
service_type='baremetal-introspection'
)
return self._client_class(
api_version=1,
inspector_url=inspector_endpoint.url,
auth_token=ctx.auth_token,
)
class SwiftAction(base.OpenStackAction):
_client_class = swift_client.Connection
def _get_client(self):
ctx = context.ctx()
LOG.debug("Swift action security context: %s" % ctx)
swift_endpoint = keystone_utils.get_endpoint_for_project('swift')
kwargs = {
'preauthurl': swift_endpoint.url % {'tenant_id': ctx.project_id},
'preauthtoken': ctx.auth_token
}
return self._client_class(**kwargs)
class ZaqarAction(base.OpenStackAction):
_client_class = zaqarclient.Client
def _get_client(self):
ctx = context.ctx()
LOG.debug("Zaqar action security context: %s" % ctx)
zaqar_endpoint = keystone_utils.get_endpoint_for_project(
service_type='messaging')
keystone_endpoint = keystone_utils.get_keystone_endpoint_v2()
opts = {
'os_auth_token': ctx.auth_token,
'os_auth_url': keystone_endpoint.url,
'os_project_id': ctx.project_id,
}
auth_opts = {'backend': 'keystone', 'options': opts}
conf = {'auth_opts': auth_opts}
return self._client_class(zaqar_endpoint.url, conf=conf)
@classmethod
def _get_fake_client(cls):
return cls._client_class("")
@classmethod
def _get_client_method(cls, client):
method = getattr(cls, cls.client_method_name)
# We can't use partial as it's not supported by getargspec
@functools.wraps(method)
def wrap(*args, **kwargs):
return method(client, *args, **kwargs)
args = inspect_utils.get_arg_list_as_str(method)
# Remove client
wrap.__arguments__ = args.split(', ', 1)[1]
return wrap
@staticmethod
def queue_messages(client, queue_name, **params):
"""Gets a list of messages from the queue.
:param queue_name: Name of the target queue.
:type queue_name: `six.string_type`
:param params: Filters to use for getting messages.
:type params: **kwargs dict
:returns: List of messages.
:rtype: `list`
"""
queue = client.queue(queue_name)
return queue.messages(**params)
@staticmethod
def queue_post(client, queue_name, messages):
"""Posts one or more messages to a queue.
:param queue_name: Name of the target queue.
:type queue_name: `six.string_type`
:param messages: One or more messages to post.
:type messages: `list` or `dict`
:returns: A dict with the result of this operation.
:rtype: `dict`
"""
queue = client.queue(queue_name)
return queue.post(messages)
@staticmethod
def queue_pop(client, queue_name, count=1):
"""Pop `count` messages from the queue.
:param queue_name: Name of the target queue.
:type queue_name: `six.string_type`
:param count: Number of messages to pop.
:type count: int
:returns: List of messages.
:rtype: `list`
"""
queue = client.queue(queue_name)
return queue.pop(count)
class BarbicanAction(base.OpenStackAction):
_client_class = barbicanclient.Client
def _get_client(self):
ctx = context.ctx()
LOG.debug("Barbican action security context: %s" % ctx)
barbican_endpoint = keystone_utils.get_endpoint_for_project('barbican')
keystone_endpoint = keystone_utils.get_keystone_endpoint_v2()
auth = identity.v2.Token(
auth_url=keystone_endpoint.url,
tenant_name=ctx.user_name,
token=ctx.auth_token,
tenant_id=ctx.project_id
)
return self._client_class(
project_id=ctx.project_id,
endpoint=barbican_endpoint.url,
auth=auth
)
@classmethod
def _get_fake_client(cls):
return cls._client_class(
project_id="1",
endpoint="http://127.0.0.1:9311"
)
@classmethod
def _get_client_method(cls, client):
if cls.client_method_name != "secrets_store":
return super(BarbicanAction, cls)._get_client_method(client)
method = getattr(cls, cls.client_method_name)
@functools.wraps(method)
def wrap(*args, **kwargs):
return method(client, *args, **kwargs)
args = inspect_utils.get_arg_list_as_str(method)
# Remove client.
wrap.__arguments__ = args.split(', ', 1)[1]
return wrap
@staticmethod
def secrets_store(client,
name=None,
payload=None,
algorithm=None,
bit_length=None,
secret_type=None,
mode=None, expiration=None):
"""Create and Store a secret in Barbican.
:param name: A friendly name for the Secret
:type name: string
:param payload: The unencrypted secret data
:type payload: string
:param algorithm: The algorithm associated with this secret key
:type algorithm: string
:param bit_length: The bit length of this secret key
:type bit_length: int
:param secret_type: The secret type for this secret key
:type secret_type: string
:param mode: The algorithm mode used with this secret keybit_length:
:type mode: string
:param expiration: The expiration time of the secret in ISO 8601 format
:type expiration: string
:returns: A new Secret object
:rtype: class:`barbicanclient.secrets.Secret'
"""
entity = client.secrets.create(
name,
payload,
algorithm,
bit_length,
secret_type,
mode,
expiration
)
entity.store()
return entity._get_formatted_entity()