import yaml
from heatclient.client import Client
from heatclient.common.template_format import yaml_loader
+from novaclient.exceptions import NotFound
from oslo_serialization import jsonutils
from snaps import file_utils
-from snaps.domain.stack import Stack
+from snaps.domain.stack import Stack, Resource, Output
-from snaps.openstack.utils import keystone_utils
+from snaps.openstack.utils import (
+ keystone_utils, neutron_utils, nova_utils, cinder_utils)
__author__ = 'spisarski'
"""
logger.debug('Retrieving Nova Client')
return Client(os_creds.heat_api_version,
- session=keystone_utils.keystone_session(os_creds))
+ session=keystone_utils.keystone_session(os_creds),
+ region_name=os_creds.region_name)
-def get_stack_by_name(heat_cli, stack_name):
+def get_stack(heat_cli, stack_settings=None, stack_name=None):
"""
- Returns a domain Stack object
+ Returns the first domain Stack object found. When stack_setting
+ is not None, the filter created will take the name attribute. When
+ stack_settings is None and stack_name is not, stack_name will be used
+ instead. When both are None, the first stack object received will be
+ returned, else None
:param heat_cli: the OpenStack heat client
- :param stack_name: the name of the heat stack
+ :param stack_settings: a StackSettings object
+ :param stack_name: the name of the heat stack to return
:return: the Stack domain object else None
"""
- stacks = heat_cli.stacks.list(**{'name': stack_name})
+
+ stack_filter = dict()
+ if stack_settings:
+ stack_filter['stack_name'] = stack_settings.name
+ elif stack_name:
+ stack_filter['stack_name'] = stack_name
+
+ stacks = heat_cli.stacks.list(**stack_filter)
for stack in stacks:
return Stack(name=stack.identifier, stack_id=stack.id)
- return None
-
def get_stack_by_id(heat_cli, stack_id):
"""
return heat_cli.stacks.get(stack_id).stack_status
-def get_stack_outputs(heat_cli, stack_id):
+def get_stack_status_reason(heat_cli, stack_id):
"""
- Returns a domain Stack object for a given ID
+ Returns the current status of the Heat stack
:param heat_cli: the OpenStack heat client
:param stack_id: the ID of the heat stack to retrieve
- :return: the Stack domain object else None
+ :return: reason for stack creation failure
"""
- stack = heat_cli.stacks.get(stack_id)
- return stack.outputs
+ return heat_cli.stacks.get(stack_id).stack_status_reason
def create_stack(heat_cli, stack_settings):
heat_cli.stacks.delete(stack.id)
+def __get_os_resources(heat_cli, stack):
+ """
+ Returns all of the OpenStack resource objects for a given stack
+ :param heat_cli: the OpenStack heat client
+ :param stack: the SNAPS-OO Stack domain object
+ :return: a list
+ """
+ return heat_cli.resources.list(stack.id)
+
+
+def get_resources(heat_cli, stack, res_type=None):
+ """
+ Returns all of the OpenStack resource objects for a given stack
+ :param heat_cli: the OpenStack heat client
+ :param stack: the SNAPS-OO Stack domain object
+ :param res_type: the type name to filter
+ :return: a list of Resource domain objects
+ """
+ os_resources = __get_os_resources(heat_cli, stack)
+
+ if os_resources:
+ out = list()
+ for os_resource in os_resources:
+ if ((res_type and os_resource.resource_type == res_type)
+ or not res_type):
+ out.append(Resource(
+ name=os_resource.resource_name,
+ resource_type=os_resource.resource_type,
+ resource_id=os_resource.physical_resource_id,
+ status=os_resource.resource_status,
+ status_reason=os_resource.resource_status_reason))
+ return out
+
+
+def get_outputs(heat_cli, stack):
+ """
+ Returns all of the SNAPS-OO Output domain objects for the defined outputs
+ for given stack
+ :param heat_cli: the OpenStack heat client
+ :param stack: the SNAPS-OO Stack domain object
+ :return: a list of Output domain objects
+ """
+ out = list()
+
+ os_stack = heat_cli.stacks.get(stack.id)
+
+ outputs = None
+ if os_stack:
+ outputs = os_stack.outputs
+
+ if outputs:
+ for output in outputs:
+ out.append(Output(**output))
+
+ return out
+
+
+def get_stack_networks(heat_cli, neutron, stack):
+ """
+ Returns a list of Network domain objects deployed by this stack
+ :param heat_cli: the OpenStack heat client object
+ :param neutron: the OpenStack neutron client object
+ :param stack: the SNAPS-OO Stack domain object
+ :return: a list of Network objects
+ """
+
+ out = list()
+ resources = get_resources(heat_cli, stack, 'OS::Neutron::Net')
+ for resource in resources:
+ network = neutron_utils.get_network_by_id(neutron, resource.id)
+ if network:
+ out.append(network)
+
+ return out
+
+
+def get_stack_routers(heat_cli, neutron, stack):
+ """
+ Returns a list of Network domain objects deployed by this stack
+ :param heat_cli: the OpenStack heat client object
+ :param neutron: the OpenStack neutron client object
+ :param stack: the SNAPS-OO Stack domain object
+ :return: a list of Network objects
+ """
+
+ out = list()
+ resources = get_resources(heat_cli, stack, 'OS::Neutron::Router')
+ for resource in resources:
+ router = neutron_utils.get_router_by_id(neutron, resource.id)
+ if router:
+ out.append(router)
+
+ return out
+
+
+def get_stack_servers(heat_cli, nova, stack):
+ """
+ Returns a list of VMInst domain objects associated with a Stack
+ :param heat_cli: the OpenStack heat client object
+ :param nova: the OpenStack nova client object
+ :param stack: the SNAPS-OO Stack domain object
+ :return: a list of VMInst domain objects
+ """
+
+ out = list()
+ resources = get_resources(heat_cli, stack, 'OS::Nova::Server')
+ for resource in resources:
+ try:
+ server = nova_utils.get_server_object_by_id(nova, resource.id)
+ if server:
+ out.append(server)
+ except NotFound:
+ logger.warn('VmInst cannot be located with ID %s', resource.id)
+
+ return out
+
+
+def get_stack_keypairs(heat_cli, nova, stack):
+ """
+ Returns a list of Keypair domain objects associated with a Stack
+ :param heat_cli: the OpenStack heat client object
+ :param nova: the OpenStack nova client object
+ :param stack: the SNAPS-OO Stack domain object
+ :return: a list of VMInst domain objects
+ """
+
+ out = list()
+ resources = get_resources(heat_cli, stack, 'OS::Nova::KeyPair')
+ for resource in resources:
+ try:
+ keypair = nova_utils.get_keypair_by_id(nova, resource.id)
+ if keypair:
+ out.append(keypair)
+ except NotFound:
+ logger.warn('Keypair cannot be located with ID %s', resource.id)
+
+ return out
+
+
+def get_stack_volumes(heat_cli, cinder, stack):
+ """
+ Returns an instance of Volume domain objects created by this stack
+ :param heat_cli: the OpenStack heat client object
+ :param cinder: the OpenStack cinder client object
+ :param stack: the SNAPS-OO Stack domain object
+ :return: a list of Volume domain objects
+ """
+
+ out = list()
+ resources = get_resources(heat_cli, stack, 'OS::Cinder::Volume')
+ for resource in resources:
+ try:
+ server = cinder_utils.get_volume_by_id(cinder, resource.id)
+ if server:
+ out.append(server)
+ except NotFound:
+ logger.warn('Volume cannot be located with ID %s', resource.id)
+
+ return out
+
+
+def get_stack_volume_types(heat_cli, cinder, stack):
+ """
+ Returns an instance of VolumeType domain objects created by this stack
+ :param heat_cli: the OpenStack heat client object
+ :param cinder: the OpenStack cinder client object
+ :param stack: the SNAPS-OO Stack domain object
+ :return: a list of VolumeType domain objects
+ """
+
+ out = list()
+ resources = get_resources(heat_cli, stack, 'OS::Cinder::VolumeType')
+ for resource in resources:
+ try:
+ vol_type = cinder_utils.get_volume_type_by_id(cinder, resource.id)
+ if vol_type:
+ out.append(vol_type)
+ except NotFound:
+ logger.warn('VolumeType cannot be located with ID %s', resource.id)
+
+ return out
+
+
+def get_stack_flavors(heat_cli, nova, stack):
+ """
+ Returns an instance of Flavor SNAPS domain object for each flavor created
+ by this stack
+ :param heat_cli: the OpenStack heat client object
+ :param nova: the OpenStack cinder client object
+ :param stack: the SNAPS-OO Stack domain object
+ :return: a list of Volume domain objects
+ """
+
+ out = list()
+ resources = get_resources(heat_cli, stack, 'OS::Nova::Flavor')
+ for resource in resources:
+ try:
+ flavor = nova_utils.get_flavor_by_id(nova, resource.id)
+ if flavor:
+ out.append(flavor)
+ except NotFound:
+ logger.warn('Flavor cannot be located with ID %s', resource.id)
+
+ return out
+
+
def parse_heat_template_str(tmpl_str):
"""
Takes a heat template string, performs some simple validation and returns a