import logging
import os
+import time
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from novaclient.client import Client
from novaclient.exceptions import NotFound
+from snaps import file_utils
from snaps.domain.flavor import Flavor
from snaps.domain.keypair import Keypair
from snaps.domain.project import ComputeQuotas
image = glance_utils.get_image(glance, image_settings=image_settings)
if image:
+ userdata = None
+ if instance_settings.userdata:
+ if isinstance(instance_settings.userdata, str):
+ userdata = instance_settings.userdata + '\n'
+ elif (isinstance(instance_settings.userdata, dict) and
+ 'script_file' in instance_settings.userdata):
+ try:
+ userdata = file_utils.read_file(
+ instance_settings.userdata['script_file'])
+ except Exception as e:
+ logger.warn('error reading userdata file %s - %s',
+ instance_settings.userdata, e)
args = {'name': instance_settings.name,
'flavor': flavor,
'image': image,
'key_name': keypair_name,
'security_groups':
instance_settings.security_group_names,
- 'userdata': instance_settings.userdata}
+ 'userdata': userdata}
if instance_settings.availability_zone:
args['availability_zone'] = instance_settings.availability_zone
return __map_os_server_obj_to_vm_inst(server)
+def get_server_connection(nova, vm_inst_settings=None, server_name=None):
+ """
+ Returns a VmInst object for the first server instance found.
+ :param nova: the Nova client
+ :param vm_inst_settings: the VmInstanceSettings object from which to build
+ the query if not None
+ :param server_name: the server with this name to return if vm_inst_settings
+ is not None
+ :return: a snaps.domain.VmInst object or None if not found
+ """
+ search_opts = dict()
+ if vm_inst_settings:
+ search_opts['name'] = vm_inst_settings.name
+ elif server_name:
+ search_opts['name'] = server_name
+
+ servers = nova.servers.list(search_opts=search_opts)
+ for server in servers:
+ return server.links[0]
+
+
def __map_os_server_obj_to_vm_inst(os_server):
"""
Returns a VmInst object for an OpenStack Server object
if sec_group.get('name'):
sec_grp_names.append(sec_group.get('name'))
+ volumes = None
+ if hasattr(os_server, 'os-extended-volumes:volumes_attached'):
+ volumes = getattr(os_server, 'os-extended-volumes:volumes_attached')
+
return VmInst(
name=os_server.name, inst_id=os_server.id,
image_id=os_server.image['id'], flavor_id=os_server.flavor['id'],
networks=os_server.networks, keypair_name=os_server.key_name,
- sec_grp_names=sec_grp_names)
+ sec_grp_names=sec_grp_names, volume_ids=volumes)
def __get_latest_server_os_object(nova, server):
serialization.PublicFormat.OpenSSH)
+def save_keys_to_files(keys=None, pub_file_path=None, priv_file_path=None):
+ """
+ Saves the generated RSA generated keys to the filesystem
+ :param keys: the keys to save generated by cryptography
+ :param pub_file_path: the path to the public keys
+ :param priv_file_path: the path to the private keys
+ """
+ if keys:
+ if pub_file_path:
+ # To support '~'
+ pub_expand_file = os.path.expanduser(pub_file_path)
+ pub_dir = os.path.dirname(pub_expand_file)
+
+ if not os.path.isdir(pub_dir):
+ os.mkdir(pub_dir)
+
+ public_handle = None
+ try:
+ public_handle = open(pub_expand_file, 'wb')
+ public_bytes = keys.public_key().public_bytes(
+ serialization.Encoding.OpenSSH,
+ serialization.PublicFormat.OpenSSH)
+ public_handle.write(public_bytes)
+ finally:
+ if public_handle:
+ public_handle.close()
+
+ os.chmod(pub_expand_file, 0o600)
+ logger.info("Saved public key to - " + pub_expand_file)
+ if priv_file_path:
+ # To support '~'
+ priv_expand_file = os.path.expanduser(priv_file_path)
+ priv_dir = os.path.dirname(priv_expand_file)
+ if not os.path.isdir(priv_dir):
+ os.mkdir(priv_dir)
+
+ private_handle = None
+ try:
+ private_handle = open(priv_expand_file, 'wb')
+ private_handle.write(
+ keys.private_bytes(
+ encoding=serialization.Encoding.PEM,
+ format=serialization.PrivateFormat.TraditionalOpenSSL,
+ encryption_algorithm=serialization.NoEncryption()))
+ finally:
+ if private_handle:
+ private_handle.close()
+
+ os.chmod(priv_expand_file, 0o600)
+ logger.info("Saved private key to - " + priv_expand_file)
+
+
def upload_keypair_file(nova, name, file_path):
"""
Uploads a public key from a file
return None
+def get_keypair_by_id(nova, kp_id):
+ """
+ Returns a list of all available keypairs
+ :param nova: the Nova client
+ :param kp_id: the ID of the keypair to return
+ :return: the keypair object
+ """
+ keypair = nova.keypairs.get(kp_id)
+ return Keypair(name=keypair.name, kp_id=keypair.id,
+ public_key=keypair.public_key)
+
+
def delete_keypair(nova, key):
"""
Deletes a keypair object from OpenStack
update_values['cores'] = compute_quotas.cores
update_values['instances'] = compute_quotas.instances
update_values['injected_files'] = compute_quotas.injected_files
- update_values['injected_file_content_bytes'] = compute_quotas.injected_file_content_bytes
+ update_values['injected_file_content_bytes'] = (
+ compute_quotas.injected_file_content_bytes)
update_values['ram'] = compute_quotas.ram
update_values['fixed_ips'] = compute_quotas.fixed_ips
update_values['key_pairs'] = compute_quotas.key_pairs
return nova.quotas.update(project_id, **update_values)
+def attach_volume(nova, server, volume, timeout=None):
+ """
+ Attaches a volume to a server
+ :param nova: the nova client
+ :param server: the VMInst domain object
+ :param volume: the Volume domain object
+ :param timeout: denotes the amount of time to block to determine if the
+ has been properly attached. When None, do not wait.
+ :return: the value from the nova call
+ """
+ nova.volumes.create_server_volume(server.id, volume.id)
+
+ if timeout:
+ start_time = time.time()
+ while time.time() < start_time + timeout:
+ vm = get_server_object_by_id(nova, server.id)
+ for vol_dict in vm.volume_ids:
+ if volume.id == vol_dict['id']:
+ return vm
+
+ return None
+ else:
+ return get_server_object_by_id(nova, server.id)
+
+
+def detach_volume(nova, server, volume, timeout=None):
+ """
+ Attaches a volume to a server
+ :param nova: the nova client
+ :param server: the VMInst domain object
+ :param volume: the Volume domain object
+ :param timeout: denotes the amount of time to block to determine if the
+ has been properly detached. When None, do not wait.
+ :return: the value from the nova call
+ """
+ nova.volumes.delete_server_volume(server.id, volume.id)
+
+ if timeout:
+ start_time = time.time()
+ while time.time() < start_time + timeout:
+ vm = get_server_object_by_id(nova, server.id)
+ found = False
+ for vol_dict in vm.volume_ids:
+ if volume.id == vol_dict['id']:
+ found = True
+
+ if not found:
+ return vm
+
+ return None
+ else:
+ return get_server_object_by_id(nova, server.id)
+
+
class NovaException(Exception):
"""
Exception when calls to the Keystone client cannot be served properly