567 lines
16 KiB
Python
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()
|