aboutsummaryrefslogtreecommitdiffstats
path: root/aria/multivim-plugin/nova_plugin/server.py
diff options
context:
space:
mode:
Diffstat (limited to 'aria/multivim-plugin/nova_plugin/server.py')
-rw-r--r--aria/multivim-plugin/nova_plugin/server.py944
1 files changed, 0 insertions, 944 deletions
diff --git a/aria/multivim-plugin/nova_plugin/server.py b/aria/multivim-plugin/nova_plugin/server.py
deleted file mode 100644
index 6726f24804..0000000000
--- a/aria/multivim-plugin/nova_plugin/server.py
+++ /dev/null
@@ -1,944 +0,0 @@
-#########
-# 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 time
-import copy
-import operator
-
-from novaclient import exceptions as nova_exceptions
-
-from cloudify import ctx
-from cloudify.manager import get_rest_client
-from cloudify.decorators import operation
-from cloudify.exceptions import NonRecoverableError, RecoverableError
-from cinder_plugin import volume
-from openstack_plugin_common import (
- provider,
- transform_resource_name,
- get_resource_id,
- get_openstack_ids_of_connected_nodes_by_openstack_type,
- with_nova_client,
- with_cinder_client,
- assign_payload_as_runtime_properties,
- get_openstack_id_of_single_connected_node_by_openstack_type,
- get_openstack_names_of_connected_nodes_by_openstack_type,
- get_single_connected_node_by_openstack_type,
- is_external_resource,
- is_external_resource_by_properties,
- is_external_resource_not_conditionally_created,
- is_external_relationship_not_conditionally_created,
- use_external_resource,
- delete_runtime_properties,
- is_external_relationship,
- validate_resource,
- USE_EXTERNAL_RESOURCE_PROPERTY,
- OPENSTACK_AZ_PROPERTY,
- OPENSTACK_ID_PROPERTY,
- OPENSTACK_TYPE_PROPERTY,
- OPENSTACK_NAME_PROPERTY,
- COMMON_RUNTIME_PROPERTIES_KEYS,
- with_neutron_client)
-from nova_plugin.keypair import KEYPAIR_OPENSTACK_TYPE
-from nova_plugin import userdata
-from openstack_plugin_common.floatingip import (IP_ADDRESS_PROPERTY,
- get_server_floating_ip)
-from neutron_plugin.network import NETWORK_OPENSTACK_TYPE
-from neutron_plugin.port import PORT_OPENSTACK_TYPE
-from cinder_plugin.volume import VOLUME_OPENSTACK_TYPE
-from openstack_plugin_common.security_group import \
- SECURITY_GROUP_OPENSTACK_TYPE
-from glance_plugin.image import handle_image_from_relationship
-
-SERVER_OPENSTACK_TYPE = 'server'
-
-# server status constants. Full lists here: http://docs.openstack.org/api/openstack-compute/2/content/List_Servers-d1e2078.html # NOQA
-SERVER_STATUS_ACTIVE = 'ACTIVE'
-SERVER_STATUS_BUILD = 'BUILD'
-SERVER_STATUS_SHUTOFF = 'SHUTOFF'
-
-OS_EXT_STS_TASK_STATE = 'OS-EXT-STS:task_state'
-SERVER_TASK_STATE_POWERING_ON = 'powering-on'
-
-MUST_SPECIFY_NETWORK_EXCEPTION_TEXT = 'More than one possible network found.'
-SERVER_DELETE_CHECK_SLEEP = 2
-
-# Runtime properties
-NETWORKS_PROPERTY = 'networks' # all of the server's ips
-IP_PROPERTY = 'ip' # the server's private ip
-ADMIN_PASSWORD_PROPERTY = 'password' # the server's password
-RUNTIME_PROPERTIES_KEYS = COMMON_RUNTIME_PROPERTIES_KEYS + \
- [NETWORKS_PROPERTY, IP_PROPERTY, ADMIN_PASSWORD_PROPERTY]
-
-
-def _get_management_network_id_and_name(neutron_client, ctx):
- """Examine the context to find the management network id and name."""
- management_network_id = None
- management_network_name = None
- provider_context = provider(ctx)
-
- if ('management_network_name' in ctx.node.properties) and \
- ctx.node.properties['management_network_name']:
- management_network_name = \
- ctx.node.properties['management_network_name']
- management_network_name = transform_resource_name(
- ctx, management_network_name)
- management_network_id = neutron_client.cosmo_get_named(
- 'network', management_network_name)
- management_network_id = management_network_id['id']
- else:
- int_network = provider_context.int_network
- if int_network:
- management_network_id = int_network['id']
- management_network_name = int_network['name'] # Already transform.
-
- return management_network_id, management_network_name
-
-
-def _merge_nics(management_network_id, *nics_sources):
- """Merge nics_sources into a single nics list, insert mgmt network if
- needed.
- nics_sources are lists of networks received from several sources
- (server properties, relationships to networks, relationships to ports).
- Merge them into a single list, and if the management network isn't present
- there, prepend it as the first network.
- """
- merged = []
- for nics in nics_sources:
- merged.extend(nics)
- if management_network_id is not None and \
- not any(nic['net-id'] == management_network_id for nic in merged):
- merged.insert(0, {'net-id': management_network_id})
- return merged
-
-
-def _normalize_nics(nics):
- """Transform the NICs passed to the form expected by openstack.
-
- If both net-id and port-id are provided, remove net-id: it is ignored
- by openstack anyway.
- """
- def _normalize(nic):
- if 'port-id' in nic and 'net-id' in nic:
- nic = nic.copy()
- del nic['net-id']
- return nic
- return [_normalize(nic) for nic in nics]
-
-
-def _prepare_server_nics(neutron_client, ctx, server):
- """Update server['nics'] based on declared relationships.
-
- server['nics'] should contain the pre-declared nics, then the networks
- that the server has a declared relationship to, then the networks
- of the ports the server has a relationship to.
-
- If that doesn't include the management network, it should be prepended
- as the first network.
-
- The management network id and name are stored in the server meta properties
- """
- network_ids = get_openstack_ids_of_connected_nodes_by_openstack_type(
- ctx, NETWORK_OPENSTACK_TYPE)
- port_ids = get_openstack_ids_of_connected_nodes_by_openstack_type(
- ctx, PORT_OPENSTACK_TYPE)
- management_network_id, management_network_name = \
- _get_management_network_id_and_name(neutron_client, ctx)
- if management_network_id is None and (network_ids or port_ids):
- # Known limitation
- raise NonRecoverableError(
- "Nova server with NICs requires "
- "'management_network_name' in properties or id "
- "from provider context, which was not supplied")
-
- nics = _merge_nics(
- management_network_id,
- server.get('nics', []),
- [{'net-id': net_id} for net_id in network_ids],
- get_port_networks(neutron_client, port_ids))
-
- nics = _normalize_nics(nics)
-
- server['nics'] = nics
- if management_network_id is not None:
- server['meta']['cloudify_management_network_id'] = \
- management_network_id
- if management_network_name is not None:
- server['meta']['cloudify_management_network_name'] = \
- management_network_name
-
-
-def _get_boot_volume_relationships(type_name, ctx):
- ctx.logger.debug('Instance relationship target instances: {0}'.format(str([
- rel.target.instance.runtime_properties
- for rel in ctx.instance.relationships])))
- targets = [
- rel.target.instance
- for rel in ctx.instance.relationships
- if rel.target.instance.runtime_properties.get(
- OPENSTACK_TYPE_PROPERTY) == type_name and
- rel.target.node.properties.get('boot', False)]
-
- if not targets:
- return None
- elif len(targets) > 1:
- raise NonRecoverableError("2 boot volumes not supported")
- return targets[0]
-
-
-def _handle_boot_volume(server, ctx):
- boot_volume = _get_boot_volume_relationships(VOLUME_OPENSTACK_TYPE, ctx)
- if boot_volume:
- boot_volume_id = boot_volume.runtime_properties[OPENSTACK_ID_PROPERTY]
- ctx.logger.info('boot_volume_id: {0}'.format(boot_volume_id))
- az = boot_volume.runtime_properties[OPENSTACK_AZ_PROPERTY]
- # If a block device mapping already exists we shouldn't overwrite it
- # completely
- bdm = server.setdefault('block_device_mapping', {})
- bdm['vda'] = '{0}:::0'.format(boot_volume_id)
- # Some nova configurations allow cross-az server-volume connections, so
- # we can't treat that as an error.
- if not server.get('availability_zone'):
- server['availability_zone'] = az
-
-
-@operation
-@with_nova_client
-@with_neutron_client
-def create(nova_client, neutron_client, args, **kwargs):
- """
- Creates a server. Exposes the parameters mentioned in
- http://docs.openstack.org/developer/python-novaclient/api/novaclient.v1_1
- .servers.html#novaclient.v1_1.servers.ServerManager.create
- """
-
- external_server = use_external_resource(ctx, nova_client,
- SERVER_OPENSTACK_TYPE)
-
- if external_server:
- _set_network_and_ip_runtime_properties(external_server)
- if ctx._local:
- return
- else:
- network_ids = \
- get_openstack_ids_of_connected_nodes_by_openstack_type(
- ctx, NETWORK_OPENSTACK_TYPE)
- port_ids = get_openstack_ids_of_connected_nodes_by_openstack_type(
- ctx, PORT_OPENSTACK_TYPE)
- try:
- _validate_external_server_nics(
- neutron_client,
- network_ids,
- port_ids
- )
- _validate_external_server_keypair(nova_client)
- return
- except Exception:
- delete_runtime_properties(ctx, RUNTIME_PROPERTIES_KEYS)
- raise
-
- provider_context = provider(ctx)
-
- def rename(name):
- return transform_resource_name(ctx, name)
-
- server = {
- 'name': get_resource_id(ctx, SERVER_OPENSTACK_TYPE),
- }
- server.update(copy.deepcopy(ctx.node.properties['server']))
- server.update(copy.deepcopy(args))
-
- _handle_boot_volume(server, ctx)
- handle_image_from_relationship(server, 'image', ctx)
-
- if 'meta' not in server:
- server['meta'] = dict()
-
- transform_resource_name(ctx, server)
-
- ctx.logger.debug(
- "server.create() server before transformations: {0}".format(server))
-
- for key in 'block_device_mapping', 'block_device_mapping_v2':
- if key in server:
- # if there is a connected boot volume, don't require the `image`
- # property.
- # However, python-novaclient requires an `image` input anyway, and
- # checks it for truthiness when deciding whether to pass it along
- # to the API
- if 'image' not in server:
- server['image'] = ctx.node.properties.get('image')
- break
- else:
- _handle_image_or_flavor(server, nova_client, 'image')
- _handle_image_or_flavor(server, nova_client, 'flavor')
-
- if provider_context.agents_security_group:
- security_groups = server.get('security_groups', [])
- asg = provider_context.agents_security_group['name']
- if asg not in security_groups:
- security_groups.append(asg)
- server['security_groups'] = security_groups
- elif not server.get('security_groups', []):
- # Make sure that if the server is connected to a security group
- # from CREATE time so that there the user can control
- # that there is never a time that a running server is not protected.
- security_group_names = \
- get_openstack_names_of_connected_nodes_by_openstack_type(
- ctx,
- SECURITY_GROUP_OPENSTACK_TYPE)
- server['security_groups'] = security_group_names
-
- # server keypair handling
- keypair_id = get_openstack_id_of_single_connected_node_by_openstack_type(
- ctx, KEYPAIR_OPENSTACK_TYPE, True)
-
- if 'key_name' in server:
- if keypair_id:
- raise NonRecoverableError("server can't both have the "
- '"key_name" nested property and be '
- 'connected to a keypair via a '
- 'relationship at the same time')
- server['key_name'] = rename(server['key_name'])
- elif keypair_id:
- server['key_name'] = _get_keypair_name_by_id(nova_client, keypair_id)
- elif provider_context.agents_keypair:
- server['key_name'] = provider_context.agents_keypair['name']
- else:
- server['key_name'] = None
- ctx.logger.info(
- 'server must have a keypair, yet no keypair was connected to the '
- 'server node, the "key_name" nested property '
- "wasn't used, and there is no agent keypair in the provider "
- "context. Agent installation can have issues.")
-
- _fail_on_missing_required_parameters(
- server,
- ('name', 'flavor'),
- 'server')
-
- _prepare_server_nics(neutron_client, ctx, server)
-
- ctx.logger.debug(
- "server.create() server after transformations: {0}".format(server))
-
- userdata.handle_userdata(server)
-
- ctx.logger.info("Creating VM with parameters: {0}".format(str(server)))
- # Store the server dictionary contents in runtime properties
- assign_payload_as_runtime_properties(ctx, SERVER_OPENSTACK_TYPE, server)
- ctx.logger.debug(
- "Asking Nova to create server. All possible parameters are: {0})"
- .format(','.join(server.keys())))
-
- try:
- s = nova_client.servers.create(**server)
- except nova_exceptions.BadRequest as e:
- if 'Block Device Mapping is Invalid' in str(e):
- return ctx.operation.retry(
- message='Block Device Mapping is not created yet',
- retry_after=30)
- if str(e).startswith(MUST_SPECIFY_NETWORK_EXCEPTION_TEXT):
- raise NonRecoverableError(
- "Can not provision server: management_network_name or id"
- " is not specified but there are several networks that the "
- "server can be connected to.")
- raise
- ctx.instance.runtime_properties[OPENSTACK_ID_PROPERTY] = s.id
- ctx.instance.runtime_properties[OPENSTACK_TYPE_PROPERTY] = \
- SERVER_OPENSTACK_TYPE
- ctx.instance.runtime_properties[OPENSTACK_NAME_PROPERTY] = server['name']
-
-
-def get_port_networks(neutron_client, port_ids):
-
- def get_network(port_id):
- port = neutron_client.show_port(port_id)
- return {
- 'net-id': port['port']['network_id'],
- 'port-id': port['port']['id']
- }
-
- return map(get_network, port_ids)
-
-
-@operation
-@with_nova_client
-def start(nova_client, start_retry_interval, private_key_path, **kwargs):
- server = get_server_by_context(nova_client)
-
- if is_external_resource_not_conditionally_created(ctx):
- ctx.logger.info('Validating external server is started')
- if server.status != SERVER_STATUS_ACTIVE:
- raise NonRecoverableError(
- 'Expected external resource server {0} to be in '
- '"{1}" status'.format(server.id, SERVER_STATUS_ACTIVE))
- return
-
- if server.status == SERVER_STATUS_ACTIVE:
- ctx.logger.info('Server is {0}'.format(server.status))
-
- if ctx.node.properties['use_password']:
- private_key = _get_private_key(private_key_path)
- ctx.logger.debug('retrieving password for server')
- password = server.get_password(private_key)
-
- if not password:
- return ctx.operation.retry(
- message='Waiting for server to post generated password',
- retry_after=start_retry_interval)
-
- ctx.instance.runtime_properties[ADMIN_PASSWORD_PROPERTY] = password
- ctx.logger.info('Server has been set with a password')
-
- _set_network_and_ip_runtime_properties(server)
- return
-
- server_task_state = getattr(server, OS_EXT_STS_TASK_STATE)
-
- if server.status == SERVER_STATUS_SHUTOFF and \
- server_task_state != SERVER_TASK_STATE_POWERING_ON:
- ctx.logger.info('Server is in {0} status - starting server...'.format(
- SERVER_STATUS_SHUTOFF))
- server.start()
- server_task_state = SERVER_TASK_STATE_POWERING_ON
-
- if server.status == SERVER_STATUS_BUILD or \
- server_task_state == SERVER_TASK_STATE_POWERING_ON:
- return ctx.operation.retry(
- message='Waiting for server to be in {0} state but is in {1}:{2} '
- 'state. Retrying...'.format(SERVER_STATUS_ACTIVE,
- server.status,
- server_task_state),
- retry_after=start_retry_interval)
-
- raise NonRecoverableError(
- 'Unexpected server state {0}:{1}'.format(server.status,
- server_task_state))
-
-
-@operation
-@with_nova_client
-def stop(nova_client, **kwargs):
- """
- Stop server.
-
- Depends on OpenStack implementation, server.stop() might not be supported.
- """
- if is_external_resource(ctx):
- ctx.logger.info('Not stopping server since an external server is '
- 'being used')
- return
-
- server = get_server_by_context(nova_client)
-
- if server.status != SERVER_STATUS_SHUTOFF:
- nova_client.servers.stop(server)
- else:
- ctx.logger.info('Server is already stopped')
-
-
-@operation
-@with_nova_client
-def delete(nova_client, **kwargs):
- if not is_external_resource(ctx):
- ctx.logger.info('deleting server')
- server = get_server_by_context(nova_client)
- nova_client.servers.delete(server)
- _wait_for_server_to_be_deleted(nova_client, server)
- else:
- ctx.logger.info('not deleting server since an external server is '
- 'being used')
-
- delete_runtime_properties(ctx, RUNTIME_PROPERTIES_KEYS)
-
-
-def _wait_for_server_to_be_deleted(nova_client,
- server,
- timeout=120,
- sleep_interval=5):
- timeout = time.time() + timeout
- while time.time() < timeout:
- try:
- server = nova_client.servers.get(server)
- ctx.logger.debug('Waiting for server "{}" to be deleted. current'
- ' status: {}'.format(server.id, server.status))
- time.sleep(sleep_interval)
- except nova_exceptions.NotFound:
- return
- # recoverable error
- raise RuntimeError('Server {} has not been deleted. waited for {} seconds'
- .format(server.id, timeout))
-
-
-def get_server_by_context(nova_client):
- return nova_client.servers.get(
- ctx.instance.runtime_properties[OPENSTACK_ID_PROPERTY])
-
-
-def _set_network_and_ip_runtime_properties(server):
-
- ips = {}
-
- if not server.networks:
- raise NonRecoverableError(
- 'The server was created but not attached to a network. '
- 'Cloudify requires that a server is connected to '
- 'at least one port.'
- )
-
- manager_network_ip = None
- management_network_name = server.metadata.get(
- 'cloudify_management_network_name')
-
- for network, network_ips in server.networks.items():
- if (management_network_name and
- network == management_network_name) or not \
- manager_network_ip:
- manager_network_ip = next(iter(network_ips or []), None)
- ips[network] = network_ips
- ctx.instance.runtime_properties[NETWORKS_PROPERTY] = ips
- # The ip of this instance in the management network
- ctx.instance.runtime_properties[IP_PROPERTY] = manager_network_ip
-
-
-@operation
-@with_nova_client
-def connect_floatingip(nova_client, fixed_ip, **kwargs):
- server_id = ctx.source.instance.runtime_properties[OPENSTACK_ID_PROPERTY]
- floating_ip_id = ctx.target.instance.runtime_properties[
- OPENSTACK_ID_PROPERTY]
-
- if is_external_relationship_not_conditionally_created(ctx):
- ctx.logger.info('Validating external floatingip and server '
- 'are associated')
- if nova_client.floating_ips.get(floating_ip_id).instance_id ==\
- server_id:
- return
- raise NonRecoverableError(
- 'Expected external resources server {0} and floating-ip {1} to be '
- 'connected'.format(server_id, floating_ip_id))
-
- floating_ip_address = ctx.target.instance.runtime_properties[
- IP_ADDRESS_PROPERTY]
- server = nova_client.servers.get(server_id)
- server.add_floating_ip(floating_ip_address, fixed_ip or None)
-
- server = nova_client.servers.get(server_id)
- all_server_ips = reduce(operator.add, server.networks.values())
- if floating_ip_address not in all_server_ips:
- return ctx.operation.retry(message='Failed to assign floating ip {0}'
- ' to machine {1}.'
- .format(floating_ip_address, server_id))
-
-
-@operation
-@with_nova_client
-@with_neutron_client
-def disconnect_floatingip(nova_client, neutron_client, **kwargs):
- if is_external_relationship(ctx):
- ctx.logger.info('Not disassociating floatingip and server since '
- 'external floatingip and server are being used')
- return
-
- server_id = ctx.source.instance.runtime_properties[OPENSTACK_ID_PROPERTY]
- ctx.logger.info("Remove floating ip {0}".format(
- ctx.target.instance.runtime_properties[IP_ADDRESS_PROPERTY]))
- server_floating_ip = get_server_floating_ip(neutron_client, server_id)
- if server_floating_ip:
- server = nova_client.servers.get(server_id)
- server.remove_floating_ip(server_floating_ip['floating_ip_address'])
- ctx.logger.info("Floating ip {0} detached from server"
- .format(server_floating_ip['floating_ip_address']))
-
-
-@operation
-@with_nova_client
-def connect_security_group(nova_client, **kwargs):
- server_id = ctx.source.instance.runtime_properties[OPENSTACK_ID_PROPERTY]
- security_group_id = ctx.target.instance.runtime_properties[
- OPENSTACK_ID_PROPERTY]
- security_group_name = ctx.target.instance.runtime_properties[
- OPENSTACK_NAME_PROPERTY]
-
- if is_external_relationship_not_conditionally_created(ctx):
- ctx.logger.info('Validating external security group and server '
- 'are associated')
- server = nova_client.servers.get(server_id)
- if [sg for sg in server.list_security_group() if sg.id ==
- security_group_id]:
- return
- raise NonRecoverableError(
- 'Expected external resources server {0} and security-group {1} to '
- 'be connected'.format(server_id, security_group_id))
-
- server = nova_client.servers.get(server_id)
- for security_group in server.list_security_group():
- # Since some security groups are already attached in
- # create this will ensure that they are not attached twice.
- if security_group_id != security_group.id and \
- security_group_name != security_group.name:
- # to support nova security groups as well,
- # we connect the security group by name
- # (as connecting by id
- # doesn't seem to work well for nova SGs)
- server.add_security_group(security_group_name)
-
- _validate_security_group_and_server_connection_status(nova_client,
- server_id,
- security_group_id,
- security_group_name,
- is_connected=True)
-
-
-@operation
-@with_nova_client
-def disconnect_security_group(nova_client, **kwargs):
- if is_external_relationship(ctx):
- ctx.logger.info('Not disconnecting security group and server since '
- 'external security group and server are being used')
- return
-
- server_id = ctx.source.instance.runtime_properties[OPENSTACK_ID_PROPERTY]
- security_group_id = ctx.target.instance.runtime_properties[
- OPENSTACK_ID_PROPERTY]
- security_group_name = ctx.target.instance.runtime_properties[
- OPENSTACK_NAME_PROPERTY]
- server = nova_client.servers.get(server_id)
- # to support nova security groups as well, we disconnect the security group
- # by name (as disconnecting by id doesn't seem to work well for nova SGs)
- server.remove_security_group(security_group_name)
-
- _validate_security_group_and_server_connection_status(nova_client,
- server_id,
- security_group_id,
- security_group_name,
- is_connected=False)
-
-
-@operation
-@with_nova_client
-@with_cinder_client
-def attach_volume(nova_client, cinder_client, status_attempts,
- status_timeout, **kwargs):
- server_id = ctx.target.instance.runtime_properties[OPENSTACK_ID_PROPERTY]
- volume_id = ctx.source.instance.runtime_properties[OPENSTACK_ID_PROPERTY]
-
- if is_external_relationship_not_conditionally_created(ctx):
- ctx.logger.info('Validating external volume and server '
- 'are connected')
- attachment = volume.get_attachment(cinder_client=cinder_client,
- volume_id=volume_id,
- server_id=server_id)
- if attachment:
- return
- else:
- raise NonRecoverableError(
- 'Expected external resources server {0} and volume {1} to be '
- 'connected'.format(server_id, volume_id))
-
- # Note: The 'device_name' property should actually be a property of the
- # relationship between a server and a volume; It'll move to that
- # relationship type once relationship properties are better supported.
- device = ctx.source.node.properties[volume.DEVICE_NAME_PROPERTY]
- nova_client.volumes.create_server_volume(
- server_id,
- volume_id,
- device if device != 'auto' else None)
- try:
- vol, wait_succeeded = volume.wait_until_status(
- cinder_client=cinder_client,
- volume_id=volume_id,
- status=volume.VOLUME_STATUS_IN_USE,
- num_tries=status_attempts,
- timeout=status_timeout
- )
- if not wait_succeeded:
- raise RecoverableError(
- 'Waiting for volume status {0} failed - detaching volume and '
- 'retrying..'.format(volume.VOLUME_STATUS_IN_USE))
- if device == 'auto':
- # The device name was assigned automatically so we
- # query the actual device name
- attachment = volume.get_attachment(
- cinder_client=cinder_client,
- volume_id=volume_id,
- server_id=server_id
- )
- device_name = attachment['device']
- ctx.logger.info('Detected device name for attachment of volume '
- '{0} to server {1}: {2}'
- .format(volume_id, server_id, device_name))
- ctx.source.instance.runtime_properties[
- volume.DEVICE_NAME_PROPERTY] = device_name
- except Exception, e:
- if not isinstance(e, NonRecoverableError):
- _prepare_attach_volume_to_be_repeated(
- nova_client, cinder_client, server_id, volume_id,
- status_attempts, status_timeout)
- raise
-
-
-def _prepare_attach_volume_to_be_repeated(
- nova_client, cinder_client, server_id, volume_id,
- status_attempts, status_timeout):
-
- ctx.logger.info('Cleaning after a failed attach_volume() call')
- try:
- _detach_volume(nova_client, cinder_client, server_id, volume_id,
- status_attempts, status_timeout)
- except Exception, e:
- ctx.logger.error('Cleaning after a failed attach_volume() call failed '
- 'raising a \'{0}\' exception.'.format(e))
- raise NonRecoverableError(e)
-
-
-def _detach_volume(nova_client, cinder_client, server_id, volume_id,
- status_attempts, status_timeout):
- attachment = volume.get_attachment(cinder_client=cinder_client,
- volume_id=volume_id,
- server_id=server_id)
- if attachment:
- nova_client.volumes.delete_server_volume(server_id, attachment['id'])
- volume.wait_until_status(cinder_client=cinder_client,
- volume_id=volume_id,
- status=volume.VOLUME_STATUS_AVAILABLE,
- num_tries=status_attempts,
- timeout=status_timeout)
-
-
-@operation
-@with_nova_client
-@with_cinder_client
-def detach_volume(nova_client, cinder_client, status_attempts,
- status_timeout, **kwargs):
- if is_external_relationship(ctx):
- ctx.logger.info('Not detaching volume from server since '
- 'external volume and server are being used')
- return
-
- server_id = ctx.target.instance.runtime_properties[OPENSTACK_ID_PROPERTY]
- volume_id = ctx.source.instance.runtime_properties[OPENSTACK_ID_PROPERTY]
-
- _detach_volume(nova_client, cinder_client, server_id, volume_id,
- status_attempts, status_timeout)
-
-
-def _fail_on_missing_required_parameters(obj, required_parameters, hint_where):
- for k in required_parameters:
- if k not in obj:
- raise NonRecoverableError(
- "Required parameter '{0}' is missing (under host's "
- "properties.{1}). Required parameters are: {2}"
- .format(k, hint_where, required_parameters))
-
-
-def _validate_external_server_keypair(nova_client):
- keypair_id = get_openstack_id_of_single_connected_node_by_openstack_type(
- ctx, KEYPAIR_OPENSTACK_TYPE, True)
- if not keypair_id:
- return
-
- keypair_instance_id = \
- [node_instance_id for node_instance_id, runtime_props in
- ctx.capabilities.get_all().iteritems() if
- runtime_props.get(OPENSTACK_ID_PROPERTY) == keypair_id][0]
- keypair_node_properties = _get_properties_by_node_instance_id(
- keypair_instance_id)
- if not is_external_resource_by_properties(keypair_node_properties):
- raise NonRecoverableError(
- "Can't connect a new keypair node to a server node "
- "with '{0}'=True".format(USE_EXTERNAL_RESOURCE_PROPERTY))
-
- server = get_server_by_context(nova_client)
- if keypair_id == _get_keypair_name_by_id(nova_client, server.key_name):
- return
- raise NonRecoverableError(
- "Expected external resources server {0} and keypair {1} to be "
- "connected".format(server.id, keypair_id))
-
-
-def _get_keypair_name_by_id(nova_client, key_name):
- keypair = nova_client.cosmo_get_named(KEYPAIR_OPENSTACK_TYPE, key_name)
- return keypair.id
-
-
-def _validate_external_server_nics(neutron_client, network_ids, port_ids):
- # validate no new nics are being assigned to an existing server (which
- # isn't possible on Openstack)
- new_nic_nodes = \
- [node_instance_id for node_instance_id, runtime_props in
- ctx.capabilities.get_all().iteritems() if runtime_props.get(
- OPENSTACK_TYPE_PROPERTY) in (PORT_OPENSTACK_TYPE,
- NETWORK_OPENSTACK_TYPE) and
- not is_external_resource_by_properties(
- _get_properties_by_node_instance_id(node_instance_id))]
- if new_nic_nodes:
- raise NonRecoverableError(
- "Can't connect new port and/or network nodes to a server node "
- "with '{0}'=True".format(USE_EXTERNAL_RESOURCE_PROPERTY))
-
- # validate all expected connected networks and ports are indeed already
- # connected to the server. note that additional networks (e.g. the
- # management network) may be connected as well with no error raised
- if not network_ids and not port_ids:
- return
-
- server_id = ctx.instance.runtime_properties[OPENSTACK_ID_PROPERTY]
- connected_ports = neutron_client.list_ports(device_id=server_id)['ports']
-
- # not counting networks connected by a connected port since allegedly
- # the connection should be on a separate port
- connected_ports_networks = {port['network_id'] for port in
- connected_ports if port['id'] not in port_ids}
- connected_ports_ids = {port['id'] for port in
- connected_ports}
- disconnected_networks = [network_id for network_id in network_ids if
- network_id not in connected_ports_networks]
- disconnected_ports = [port_id for port_id in port_ids if port_id not
- in connected_ports_ids]
- if disconnected_networks or disconnected_ports:
- raise NonRecoverableError(
- 'Expected external resources to be connected to external server {'
- '0}: Networks - {1}; Ports - {2}'.format(server_id,
- disconnected_networks,
- disconnected_ports))
-
-
-def _get_properties_by_node_instance_id(node_instance_id):
- client = get_rest_client()
- node_instance = client.node_instances.get(node_instance_id)
- node = client.nodes.get(ctx.deployment.id, node_instance.node_id)
- return node.properties
-
-
-@operation
-@with_nova_client
-def creation_validation(nova_client, args, **kwargs):
-
- def validate_server_property_value_exists(server_props, property_name):
- ctx.logger.debug(
- 'checking whether {0} exists...'.format(property_name))
-
- serv_props_copy = server_props.copy()
- try:
- handle_image_from_relationship(serv_props_copy, 'image', ctx)
- _handle_image_or_flavor(serv_props_copy, nova_client,
- property_name)
- except (NonRecoverableError, nova_exceptions.NotFound) as e:
- # temporary error - once image/flavor_name get removed, these
- # errors won't be relevant anymore
- err = str(e)
- ctx.logger.error('VALIDATION ERROR: ' + err)
- raise NonRecoverableError(err)
-
- prop_value_id = str(serv_props_copy[property_name])
- prop_values = list(nova_client.cosmo_list(property_name))
- for f in prop_values:
- if prop_value_id == f.id:
- ctx.logger.debug('OK: {0} exists'.format(property_name))
- return
- err = '{0} {1} does not exist'.format(property_name, prop_value_id)
- ctx.logger.error('VALIDATION ERROR: ' + err)
- if prop_values:
- ctx.logger.info('list of available {0}s:'.format(property_name))
- for f in prop_values:
- ctx.logger.info(' {0:>10} - {1}'.format(f.id, f.name))
- else:
- ctx.logger.info('there are no available {0}s'.format(
- property_name))
- raise NonRecoverableError(err)
-
- validate_resource(ctx, nova_client, SERVER_OPENSTACK_TYPE)
-
- server_props = dict(ctx.node.properties['server'], **args)
- validate_server_property_value_exists(server_props, 'flavor')
-
-
-def _get_private_key(private_key_path):
- pk_node_by_rel = \
- get_single_connected_node_by_openstack_type(
- ctx, KEYPAIR_OPENSTACK_TYPE, True)
-
- if private_key_path:
- if pk_node_by_rel:
- raise NonRecoverableError("server can't both have a "
- '"private_key_path" input and be '
- 'connected to a keypair via a '
- 'relationship at the same time')
- key_path = private_key_path
- else:
- if pk_node_by_rel and pk_node_by_rel.properties['private_key_path']:
- key_path = pk_node_by_rel.properties['private_key_path']
- else:
- key_path = ctx.bootstrap_context.cloudify_agent.agent_key_path
-
- if key_path:
- key_path = os.path.expanduser(key_path)
- if os.path.isfile(key_path):
- return key_path
-
- err_message = 'Cannot find private key file'
- if key_path:
- err_message += '; expected file path was {0}'.format(key_path)
- raise NonRecoverableError(err_message)
-
-
-def _validate_security_group_and_server_connection_status(
- nova_client, server_id, sg_id, sg_name, is_connected):
-
- # verifying the security group got connected or disconnected
- # successfully - this is due to Openstack concurrency issues that may
- # take place when attempting to connect/disconnect multiple SGs to the
- # same server at the same time
- server = nova_client.servers.get(server_id)
-
- if is_connected ^ any(sg for sg in server.list_security_group() if
- sg.id == sg_id):
- raise RecoverableError(
- message='Security group {0} did not get {2} server {1} '
- 'properly'
- .format(
- sg_name,
- server.name,
- 'connected to' if is_connected else 'disconnected from'))
-
-
-def _handle_image_or_flavor(server, nova_client, prop_name):
- if prop_name not in server and '{0}_name'.format(prop_name) not in server:
- # setting image or flavor - looking it up by name; if not found, then
- # the value is assumed to be the id
- server[prop_name] = ctx.node.properties[prop_name]
-
- # temporary error message: once the 'image' and 'flavor' properties
- # become mandatory, this will become less relevant
- if not server[prop_name]:
- raise NonRecoverableError(
- 'must set {0} by either setting a "{0}" property or by setting'
- ' a "{0}" or "{0}_name" (deprecated) field under the "server" '
- 'property'.format(prop_name))
-
- image_or_flavor = \
- nova_client.cosmo_get_if_exists(prop_name, name=server[prop_name])
- if image_or_flavor:
- server[prop_name] = image_or_flavor.id
- else: # Deprecated sugar
- if '{0}_name'.format(prop_name) in server:
- prop_name_plural = nova_client.cosmo_plural(prop_name)
- server[prop_name] = \
- getattr(nova_client, prop_name_plural).find(
- name=server['{0}_name'.format(prop_name)]).id
- del server['{0}_name'.format(prop_name)]