def detach(nova_client, neutron_client, **kwargs): if is_external_relationship(ctx): ctx.logger.info('Not detaching port from server since ' 'external port and server are being used') return port_id = get_openstack_id(ctx.target) server_id = get_openstack_id(ctx.source) server_floating_ip = get_server_floating_ip(neutron_client, server_id) if server_floating_ip: ctx.logger.info('We have floating ip {0} attached to server'.format( server_floating_ip['floating_ip_address'])) server = nova_client.servers.get(server_id) server.remove_floating_ip(server_floating_ip['floating_ip_address']) return ctx.operation.retry( message='Waiting for the floating ip {0} to ' 'detach from server {1}..'.format( server_floating_ip['floating_ip_address'], server_id), retry_after=10) change = {PORT_OPENSTACK_TYPE: {'device_id': '', 'device_owner': ''}} ctx.logger.info('Detaching port {0}...'.format(port_id)) neutron_client.update_port(port_id, change) ctx.logger.info('Successfully detached port {0}'.format(port_id))
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 = get_openstack_id(ctx.source) security_group_id = get_openstack_id(ctx.target) 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) try: server.remove_security_group(security_group_name) except nova_exceptions.NotFound: ctx.logger.warn("Security group '{0}' (id: {1}) is not attached " "to server instance {2}; skipping".format( security_group_name, security_group_id, server_id)) else: _validate_security_group_and_server_connection_status( nova_client, server_id, security_group_id, security_group_name, is_connected=False)
def connect_security_group(neutron_client, **kwargs): port_id = get_openstack_id(ctx.source) security_group_id = get_openstack_id(ctx.target) if is_external_relationship_not_conditionally_created(ctx): ctx.logger.info('Validating external port and security-group are ' 'connected') if any(sg for sg in neutron_client.show_port(port_id)['port'].get( 'security_groups', []) if sg == security_group_id): return raise NonRecoverableError( 'Expected external resources port {0} and security-group {1} to ' 'be connected'.format(port_id, security_group_id)) # WARNING: non-atomic operation port = neutron_client.cosmo_get(PORT_OPENSTACK_TYPE, id=port_id) ctx.logger.info( "connect_security_group(): source_id={0} target={1}".format( port_id, ctx.target.instance.runtime_properties)) sgs = port['security_groups'] + [security_group_id] neutron_client.update_port(port_id, {PORT_OPENSTACK_TYPE: {'security_groups': sgs}}) # Double check if SG has been actually updated (a race-condition # in OpenStack): port_info = neutron_client.show_port(port_id)['port'] port_security_groups = port_info.get('security_groups', []) if security_group_id not in port_security_groups: return ctx.operation.retry( message='Security group connection (`{0}\' -> `{1}\')' ' has not been established!'.format(port_id, security_group_id), retry_after=NO_SG_PORT_CONNECTION_RETRY_INTERVAL )
def connect_floatingip(nova_client, fixed_ip, **kwargs): server_id = get_openstack_id(ctx.source) floating_ip_id = get_openstack_id(ctx.target) 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))
def connect_security_group(nova_client, **kwargs): server_id = get_openstack_id(ctx.source) security_group_id = get_openstack_id(ctx.target) 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)
def attach_volume(nova_client, cinder_client, status_attempts, status_timeout, **kwargs): server_id = get_openstack_id(ctx.target) volume_id = get_openstack_id(ctx.source) 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 connect_port(neutron_client, **kwargs): if is_external_relationship_not_conditionally_created(ctx): return port_id = get_openstack_id(ctx.source) floating_ip_id = get_openstack_id(ctx.target) fip = {'port_id': port_id} neutron_client.update_floatingip(floating_ip_id, {FLOATINGIP_OPENSTACK_TYPE: fip})
def update(nova_client, args, **kwargs): if HOST_AGGREGATE_OPENSTACK_TYPE in args: host_aggregate = nova_client.aggregates.update( get_openstack_id(ctx), args.get(HOST_AGGREGATE_OPENSTACK_TYPE)) set_openstack_runtime_properties(ctx, host_aggregate, HOST_AGGREGATE_OPENSTACK_TYPE) _set_metadata(ctx, nova_client, get_openstack_id(ctx), args)
def delete(nova_client, **kwargs): if not is_external_resource(ctx): host_aggregate = nova_client.aggregates.get(get_openstack_id(ctx)) _remove_hosts(ctx, nova_client, get_openstack_id(ctx), host_aggregate.hosts) if HOSTS_PROPERTY in ctx.instance.runtime_properties: ctx.instance.runtime_properties.pop(HOSTS_PROPERTY, None) delete_resource_and_runtime_properties(ctx, nova_client, RUNTIME_PROPERTIES_KEYS)
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 = get_openstack_id(ctx.target) volume_id = get_openstack_id(ctx.source) _detach_volume(nova_client, cinder_client, server_id, volume_id, status_attempts, status_timeout)
def update(nova_client, args, **kwargs): host_aggregate_dict = create_object_dict(ctx, HOST_AGGREGATE_OPENSTACK_TYPE, args) _remove_hosts(ctx, nova_client, get_openstack_id(ctx), kwargs) host_aggregate = nova_client.aggregates.update(get_openstack_id(ctx), host_aggregate_dict) _add_hosts(ctx, nova_client, host_aggregate, kwargs) _set_metadata(ctx, nova_client, host_aggregate, kwargs) set_openstack_runtime_properties(ctx, host_aggregate, HOST_AGGREGATE_OPENSTACK_TYPE)
def disconnect_subnet(neutron_client, update_args=None, **kwargs): if update_args is not None and isinstance(update_args, dict): update_router(neutron_client, args=update_args, ctx=ctx) if is_external_relationship(ctx): ctx.logger.info('Not connecting subnet and router since external ' 'subnet and router are being used') return neutron_client.remove_interface_router( get_openstack_id(ctx.target), {'subnet_id': get_openstack_id(ctx.source)})
def attach(nova_client, neutron_client, **kwargs): if is_external_relationship(ctx): ctx.logger.info('Not attaching port from server since ' 'external port and server are being used') return server_id = get_openstack_id(ctx.source) port_id = get_openstack_id(ctx.target) port = neutron_client.show_port(port_id) server = nova_client.servers.get(server_id) network = neutron_client.show_network(port['port']['network_id']) network_name = network['network']['name'] floating_ip_address = None for target in ctx.target.instance.relationships: if target.type == PORT_ADDRESS_REL_TYPE: target_instance = target.target.instance floatingip_id = \ target_instance.runtime_properties[OPENSTACK_ID_PROPERTY] floating_ip = neutron_client.show_floatingip(floatingip_id) floating_ip_address = \ floating_ip['floatingip']['floating_ip_address'] server_addresses = \ [addr['addr'] for addr in server.addresses[network_name]] if floating_ip_address and floating_ip_address not in server_addresses: ctx.logger.info('We will attach floating ip {0} to server {1}'.format( floating_ip_address, server_id)) server.add_floating_ip(floating_ip_address) return ctx.operation.retry( message='Waiting for the floating ip {0} to ' 'attach to server {1}..'.format(floating_ip_address, server_id), retry_after=10) change = { PORT_OPENSTACK_TYPE: { 'device_id': server_id, } } device_id = port['port'].get('device_id') if not device_id or device_id != server_id: ctx.logger.info('Attaching port {0}...'.format(port_id)) neutron_client.update_port(port_id, change) ctx.logger.info('Successfully attached port {0}'.format(port_id)) else: ctx.logger.info('Skipping port {0} attachment, ' 'because it is already attached ' 'to device (server) id {1}.'.format( port_id, device_id))
def disconnect_subnet(neutron_client, **kwargs): if is_external_relationship(ctx): ctx.logger.info('Not connecting subnet and router since external ' 'subnet and router are being used') return node_routes = ctx.source.instance.runtime_properties.get( ROUTES_OPENSTACK_TYPE) # Only delete routes only if it has "routes" as runtime properties if node_routes: _delete_routes(neutron_client) neutron_client.remove_interface_router( get_openstack_id(ctx.target), {'subnet_id': get_openstack_id(ctx.source)})
def update_project(keystone_client, args, **kwargs): project_dict = create_object_dict(ctx, PROJECT_OPENSTACK_TYPE, args, {'domain': 'default'}) project_dict[PROJECT_OPENSTACK_TYPE] = get_openstack_id(ctx) project = keystone_client.projects.update(**project_dict) set_openstack_runtime_properties(ctx, project, PROJECT_OPENSTACK_TYPE)
def create(neutron_client, args, **kwargs): if use_external_resource(ctx, neutron_client, SUBNET_OPENSTACK_TYPE): try: net_id = \ get_openstack_id_of_single_connected_node_by_openstack_type( ctx, NETWORK_OPENSTACK_TYPE, True) if net_id: subnet_id = get_openstack_id(ctx) if neutron_client.show_subnet( subnet_id)[SUBNET_OPENSTACK_TYPE][NETWORK_ID] \ != net_id: raise NonRecoverableError( 'Expected external resources subnet {0} and network' ' {1} to be connected'.format(subnet_id, net_id)) return except Exception: delete_runtime_properties(ctx, RUNTIME_PROPERTIES_KEYS) raise net_id = get_openstack_id_of_single_connected_node_by_openstack_type( ctx, NETWORK_OPENSTACK_TYPE) subnet = create_object_dict(ctx, SUBNET_OPENSTACK_TYPE, args, {NETWORK_ID: net_id}) s = neutron_client.create_subnet( {SUBNET_OPENSTACK_TYPE: subnet})[SUBNET_OPENSTACK_TYPE] set_neutron_runtime_properties(ctx, s, SUBNET_OPENSTACK_TYPE)
def snapshot_apply(cinder_client, **kwargs): volume_id = get_openstack_id(ctx) backup_name = _get_snapshot_name(ctx, kwargs) snapshot_incremental = kwargs["snapshot_incremental"] if not snapshot_incremental: ctx.logger.info("Backup apply {} to {}".format(backup_name, volume_id)) search_opts = { 'volume_id': volume_id, VOLUME_OPENSTACK_ID_KEY: backup_name } backups = cinder_client.backups.list( search_opts=search_opts) for backup in backups: # if returned more than one backup, use first if backup.name == backup_name: ctx.logger.debug("Used first with {} to {}" .format(backup.id, volume_id)) cinder_client.restores.restore(backup.id, volume_id) break else: raise cfy_exc.NonRecoverableError("No such {} backup." .format(backup_name)) else: ctx.logger.error("Apply snapshot is unsuported")
def connect_subnet(neutron_client, **kwargs): router_id = get_openstack_id(ctx.target) subnet_id = get_openstack_id(ctx.source) if is_external_relationship_not_conditionally_created(ctx): ctx.logger.info('Validating external subnet and router ' 'are associated') for port in neutron_client.list_ports(device_id=router_id)['ports']: for fixed_ip in port.get('fixed_ips', []): if fixed_ip.get('subnet_id') == subnet_id: return raise NonRecoverableError( 'Expected external resources router {0} and subnet {1} to be ' 'connected'.format(router_id, subnet_id)) neutron_client.add_interface_router(router_id, {'subnet_id': subnet_id})
def get_project_quota(nova_client, cinder_client, neutron_client, **kwargs): project_id = get_openstack_id(ctx) quota = ctx.instance.runtime_properties.get(QUOTA, {}) quota[NOVA] = get_quota(project_id, nova_client, NOVA) quota[NEUTRON] = get_quota(project_id, neutron_client, NEUTRON) quota[CINDER] = get_quota(project_id, cinder_client, CINDER) ctx.instance.runtime_properties[QUOTA] = quota
def create(neutron_client, args, **kwargs): if use_external_resource(ctx, neutron_client, ROUTER_OPENSTACK_TYPE): try: ext_net_id_by_rel = _get_connected_ext_net_id(neutron_client) if ext_net_id_by_rel: router_id = get_openstack_id(ctx) router = neutron_client.show_router(router_id)['router'] if not (router['external_gateway_info'] and 'network_id' in router['external_gateway_info'] and router['external_gateway_info']['network_id'] == ext_net_id_by_rel): raise NonRecoverableError( 'Expected external resources router {0} and ' 'external network {1} to be connected'.format( router_id, ext_net_id_by_rel)) return except Exception: delete_runtime_properties(ctx, RUNTIME_PROPERTIES_KEYS) raise router = create_object_dict(ctx, ROUTER_OPENSTACK_TYPE, args, {}) ctx.logger.info('router: {0}'.format(router)) _handle_external_network_config(router, neutron_client) r = neutron_client.create_router({ROUTER_OPENSTACK_TYPE: router})[ROUTER_OPENSTACK_TYPE] set_neutron_runtime_properties(ctx, r, ROUTER_OPENSTACK_TYPE)
def update_project_quota(nova_client, cinder_client, neutron_client, quota, **kwargs): project_id = get_openstack_id(ctx) update_quota(project_id, quota, nova_client, NOVA) update_quota(project_id, quota, neutron_client, NEUTRON) update_quota(project_id, quota, cinder_client, CINDER)
def _remove_protected(glance_client): if use_external_resource(ctx, glance_client, IMAGE_OPENSTACK_TYPE): return is_protected = ctx.node.properties[IMAGE_OPENSTACK_TYPE].get( 'protected', False) if is_protected: img_id = get_openstack_id(ctx) glance_client.images.update(img_id, protected=False)
def delete(keystone_client, nova_client, cinder_client, neutron_client, **kwargs): project_id = get_openstack_id(ctx) quota = ctx.node.properties[PROJECT_QUOTA_TYPE] delete_quota(project_id, quota, nova_client, NOVA) delete_quota(project_id, quota, neutron_client, NEUTRON) delete_quota(project_id, quota, cinder_client, CINDER) delete_resource_and_runtime_properties(ctx, keystone_client, RUNTIME_PROPERTIES_KEYS)
def update_router(neutron_client, args, **kwargs): from copy import deepcopy def dict_merge(a, b): if isinstance(a, list) and isinstance(b, list): a.append(b) return a if not isinstance(b, dict): return b result = deepcopy(a) for k, v in b.iteritems(): if k in result and isinstance(result[k], dict): result[k] = dict_merge(result[k], v) else: result[k] = deepcopy(v) return result # Find out if the update script is being called # from a relationship or a node operation. if ctx.type == RELATIONSHIP_INSTANCE: if ROUTER_OPENSTACK_TYPE in get_openstack_type(ctx.source): subject = ctx.source elif ROUTER_OPENSTACK_TYPE in get_openstack_type(ctx.target): subject = ctx.target else: raise NonRecoverableError( 'Neither target nor source is {0}'.format( ROUTER_OPENSTACK_TYPE)) else: subject = ctx try: router = neutron_client.show_router(get_openstack_id(subject)) except NeutronClientException as e: raise NonRecoverableError('Error: {0}'.format(str(e))) if not isinstance(router, dict) or \ 'router' not in router.keys() or \ 'id' not in router['router'].keys(): raise NonRecoverableError( 'API returned unexpected structure.: {0}'.format(router)) router_id = router['router'].pop('id') new_router = {'router': {}} for key, value in args.items(): new_router['router'][key] = value for ro_attribute in ['status', 'tenant_id']: try: del router['router'][ro_attribute] except KeyError: pass dict_merge(new_router, router) ctx.logger.info(new_router) neutron_client.update_router(router_id, new_router)
def delete(cinder_client, **kwargs): # seach snapshots for volume search_opts = { 'volume_id': get_openstack_id(ctx), } _delete_snapshot(cinder_client, search_opts) # remove volume itself delete_resource_and_runtime_properties(ctx, cinder_client, RUNTIME_PROPERTIES_KEYS)
def disconnect_port(neutron_client, **kwargs): if is_external_relationship(ctx): ctx.logger.info('Not disassociating floatingip and port since ' 'external floatingip and port are being used') return floating_ip_id = get_openstack_id(ctx.target) fip = {'port_id': None} neutron_client.update_floatingip(floating_ip_id, {FLOATINGIP_OPENSTACK_TYPE: fip})
def delete(nova_client, **kwargs): if not is_external_resource(ctx): ctx.logger.info('deleting server group') nova_client.server_groups.delete(get_openstack_id(ctx)) else: ctx.logger.info('not deleting server group since an external server ' 'group is being used') delete_runtime_properties(ctx, RUNTIME_PROPERTIES_KEYS)
def stop(neutron_client, **kwargs): if is_external_resource(ctx): ctx.logger.info('Not stopping network since an external network is ' 'being used') return neutron_client.update_network( get_openstack_id(ctx), {NETWORK_OPENSTACK_TYPE: { ADMIN_STATE_UP: False }})
def assign_users(users, keystone_client, **kwargs): project_id = get_openstack_id(ctx) for user in users: roles = user[ROLES] u = keystone_client.users.find(name=user['name']) for role in roles: r = keystone_client.roles.find(name=role) keystone_client.roles.grant(user=u.id, project=project_id, role=r.id) ctx.logger.debug("Assigned user {0} to project {1} with role {2}" .format(u.id, project_id, r.id))
def delete(nova_client, **kwargs): if not is_external_resource(ctx): ctx.logger.info('deleting keypair') _delete_private_key_file() nova_client.keypairs.delete(get_openstack_id(ctx)) else: ctx.logger.info('not deleting keypair since an external keypair is ' 'being used') delete_runtime_properties(ctx, RUNTIME_PROPERTIES_KEYS)