self.image_settings = image_settings
self.keypair_settings = keypair_settings
- # TODO - get rid of FIP list and only use the dict(). Need to fix
- # populating this object when already exists
- self.__floating_ips = list()
self.__floating_ip_dict = dict()
# Instantiated in self.create()
def create(self, cleanup=False, block=False):
"""
Creates a VM instance
- :param cleanup: When true, only perform lookups for OpenStack objects.
+ :param cleanup: When true, this object is initialized only via queries,
+ else objects will be created when the queries return
+ None. The name of this parameter should be changed to
+ something like 'readonly' as the same goes with all of
+ the other creator classes.
:param block: Thread will block until instance has either become
active, error, or timeout waiting.
Additionally, when True, floating IPs will not be applied
VM with the same name already exists
within the project
"""
- servers = nova_utils.get_servers_by_name(self.__nova,
- self.instance_settings.name)
- for server in servers:
+ server = nova_utils.get_server(
+ self.__nova, vm_inst_settings=self.instance_settings)
+ if server:
if server.name == self.instance_settings.name:
self.__vm = server
logger.info(
'Found existing machine with name - %s',
self.instance_settings.name)
- fips = self.__nova.floating_ips.list()
- for fip in fips:
- if fip.instance_id == server.id:
- self.__floating_ips.append(fip)
- # TODO - Determine a means to associate to the FIP
- # configuration and add to FIP map
+
+ fips = neutron_utils.get_floating_ips(self.__neutron,
+ self.__ports)
+ for port_id, fip in fips:
+ settings = self.instance_settings.floating_ip_settings
+ for fip_setting in settings:
+ if port_id == fip_setting.port_id:
+ self.__floating_ip_dict[fip_setting.name] = fip
+ else:
+ port = neutron_utils.get_port_by_id(
+ self.__neutron, port_id)
+ if port and port.name == fip_setting.port_name:
+ self.__floating_ip_dict[fip_setting.name] = fip
def __create_vm(self, block=False):
"""
active, error, or timeout waiting. Floating IPs will be
assigned after active when block=True
"""
- nics = []
- for key, port in self.__ports:
- kv = dict()
- kv['port-id'] = port['port']['id']
- nics.append(kv)
-
- logger.info('Creating VM with name - ' + self.instance_settings.name)
- keypair_name = None
- if self.keypair_settings:
- keypair_name = self.keypair_settings.name
-
- flavor = nova_utils.get_flavor_by_name(self.__nova,
- self.instance_settings.flavor)
- if not flavor:
- raise Exception(
- 'Flavor not found with name - %s',
- self.instance_settings.flavor)
-
- image = glance_utils.get_image(
- glance_utils.glance_client(self.__os_creds),
- self.image_settings.name)
- if image:
- self.__vm = self.__nova.servers.create(
- name=self.instance_settings.name,
- flavor=flavor,
- image=image,
- nics=nics,
- key_name=keypair_name,
- security_groups=self.instance_settings.security_group_names,
- userdata=self.instance_settings.userdata,
- availability_zone=self.instance_settings.availability_zone)
-
- else:
- raise Exception(
- 'Cannot create instance, image cannot be located with name %s',
- self.image_settings.name)
-
- logger.info(
- 'Created instance with name - %s', self.instance_settings.name)
+ glance = glance_utils.glance_client(self.__os_creds)
+ self.__vm = nova_utils.create_server(
+ self.__nova, self.__neutron, glance, self.instance_settings,
+ self.image_settings, self.keypair_settings)
+ logger.info('Created instance with name - %s',
+ self.instance_settings.name)
if block:
if not self.vm_active(block=True):
- raise Exception(
+ raise VmInstanceCreationError(
'Fatal error, VM did not become ACTIVE within the alloted '
'time')
- # TODO - the call above should add security groups. The return object
- # shows they exist but the association had never been made by
- # OpenStack. This call is here to ensure they have been added
+ # Create server should do this but found it needed to occur here
for sec_grp_name in self.instance_settings.security_group_names:
if self.vm_active(block=True):
nova_utils.add_security_group(self.__nova, self.__vm,
sec_grp_name)
else:
- raise Exception(
+ raise VmInstanceCreationError(
'Cannot applying security group with name ' +
sec_grp_name +
' to VM that did not activate with name - ' +
port = port_dict.get(floating_ip_setting.port_name)
if not port:
- raise Exception(
+ raise VmInstanceCreationError(
'Cannot find port object with name - ' +
floating_ip_setting.port_name)
ext_gateway = self.__ext_gateway_by_router(
floating_ip_setting.router_name)
if ext_gateway:
- subnet = neutron_utils.get_subnet_by_name(
- self.__neutron, floating_ip_setting.subnet_name)
- floating_ip = nova_utils.create_floating_ip(self.__nova,
- ext_gateway)
- self.__floating_ips.append(floating_ip)
+ subnet = neutron_utils.get_subnet(
+ self.__neutron,
+ subnet_name=floating_ip_setting.subnet_name)
+ floating_ip = neutron_utils.create_floating_ip(
+ self.__neutron, ext_gateway)
self.__floating_ip_dict[floating_ip_setting.name] = floating_ip
logger.info(
floating_ip_setting.router_name)
self.__add_floating_ip(floating_ip, port, subnet)
else:
- raise Exception('Unable to add floating IP to port,'
- ' cannot locate router with an external '
- 'gateway ')
+ raise VmInstanceCreationError(
+ 'Unable to add floating IP to port, cannot locate router '
+ 'with an external gateway ')
def __ext_gateway_by_router(self, router_name):
"""
:param router_name: The name of the router to lookup
:return: the external network name or None
"""
- router = neutron_utils.get_router_by_name(self.__neutron, router_name)
- if router and router['router'].get('external_gateway_info'):
+ router = neutron_utils.get_router(
+ self.__neutron, router_name=router_name)
+ if router and router.external_gateway_info:
network = neutron_utils.get_network_by_id(
self.__neutron,
- router['router']['external_gateway_info']['network_id'])
+ router.external_gateway_info['network_id'])
if network:
- return network['network']['name']
+ return network.name
return None
def clean(self):
"""
# Cleanup floating IPs
- for floating_ip in self.__floating_ips:
+ for name, floating_ip in self.__floating_ip_dict.items():
try:
logger.info('Deleting Floating IP - ' + floating_ip.ip)
- nova_utils.delete_floating_ip(self.__nova, floating_ip)
+ neutron_utils.delete_floating_ip(self.__neutron, floating_ip)
except Exception as e:
logger.error('Error deleting Floating IP - ' + str(e))
- self.__floating_ips = list()
self.__floating_ip_dict = dict()
# Cleanup ports
for name, port in self.__ports:
- logger.info('Deleting Port - ' + name)
+ logger.info('Deleting Port with ID - %S ' + port.id)
try:
neutron_utils.delete_port(self.__neutron, port)
except PortNotFoundClient as e:
ports = list()
for port_setting in port_settings:
- # First check to see if network already has this port
- # TODO/FIXME - this could potentially cause problems if another
- # port with the same name exists
- # VM has the same network/port name pair
- found = False
-
- # TODO/FIXME - should we not be iterating on ports for the specific
- # network in question as unique port names
- # seem to only be important by network
- existing_ports = self.__neutron.list_ports()['ports']
- for existing_port in existing_ports:
- if existing_port['name'] == port_setting.name:
- ports.append((port_setting.name, {'port': existing_port}))
- found = True
- break
-
- if not found and not cleanup:
- ports.append((port_setting.name,
- neutron_utils.create_port(self.__neutron,
- self.__os_creds,
- port_setting)))
+ port = neutron_utils.get_port(
+ self.__neutron, port_settings=port_setting)
+ if not port:
+ network = neutron_utils.get_network(
+ self.__neutron, network_name=port_setting.network_name)
+ net_ports = neutron_utils.get_ports(self.__neutron, network)
+ for net_port in net_ports:
+ if port_setting.mac_address == net_port.mac_address:
+ port = net_port
+ break
+ if port:
+ ports.append((port_setting.name, port))
+ elif not cleanup:
+ # Exception will be raised when port with same name already
+ # exists
+ ports.append(
+ (port_setting.name, neutron_utils.create_port(
+ self.__neutron, self.__os_creds, port_setting)))
return ports
if subnet:
# Take IP of subnet if there is one configured on which to place
# the floating IP
- for fixed_ip in port['port']['fixed_ips']:
- if fixed_ip['subnet_id'] == subnet['subnet']['id']:
+ for fixed_ip in port.ips:
+ if fixed_ip['subnet_id'] == subnet.id:
ip = fixed_ip['ip_address']
break
else:
# Simply take the first
- ip = port['port']['fixed_ips'][0]['ip_address']
+ ip = port.ips[0]['ip_address']
if ip:
count = timeout / poll_interval
while count > 0:
logger.debug('Attempting to add floating IP to instance')
try:
- self.__vm.add_floating_ip(floating_ip, ip)
+ nova_utils.add_floating_ip_to_server(
+ self.__nova, self.__vm, floating_ip, ip)
logger.info(
'Added floating IP %s to port IP %s on instance %s',
floating_ip.ip, ip, self.instance_settings.name)
count -= 1
pass
else:
- raise Exception(
+ raise VmInstanceCreationError(
'Unable find IP address on which to place the floating IP')
logger.error('Timeout attempting to add the floating IP to instance.')
- raise Exception('Timeout while attempting add floating IP to instance')
+ raise VmInstanceCreationError(
+ 'Timeout while attempting add floating IP to instance')
def get_os_creds(self):
"""
Returns the latest version of this server object from OpenStack
:return: Server object
"""
- return nova_utils.get_latest_server_object(self.__nova, self.__vm)
+ return nova_utils.get_server_object_by_id(self.__nova, self.__vm.id)
+
+ def get_console_output(self):
+ """
+ Returns the vm console object for parsing logs
+ :return: the console output object
+ """
+ return nova_utils.get_server_console_output(self.__nova, self.__vm)
def get_port_ip(self, port_name, subnet_name=None):
"""
"""
port = self.get_port_by_name(port_name)
if port:
- port_dict = port['port']
if subnet_name:
- subnet = neutron_utils.get_subnet_by_name(self.__neutron,
- subnet_name)
+ subnet = neutron_utils.get_subnet(
+ self.__neutron, subnet_name=subnet_name)
if not subnet:
logger.warning('Cannot retrieve port IP as subnet could '
'not be located with name - %s',
subnet_name)
return None
- for fixed_ip in port_dict['fixed_ips']:
- if fixed_ip['subnet_id'] == subnet['subnet']['id']:
+ for fixed_ip in port.ips:
+ if fixed_ip['subnet_id'] == subnet.id:
return fixed_ip['ip_address']
else:
- fixed_ips = port_dict['fixed_ips']
- if fixed_ips and len(fixed_ips) > 0:
- return fixed_ips[0]['ip_address']
+ if port.ips and len(port.ips) > 0:
+ return port.ips[0]['ip_address']
return None
def get_port_mac(self, port_name):
"""
port = self.get_port_by_name(port_name)
if port:
- port_dict = port['port']
- return port_dict['mac_address']
+ return port.mac_address
return None
def get_port_by_name(self, port_name):
logger.warning('Cannot find port with name - ' + port_name)
return None
+ def get_vm_info(self):
+ """
+ Returns a dictionary of a VMs info as returned by OpenStack
+ :return: a dict()
+ """
+ return nova_utils.get_server_info(self.__nova, self.__vm)
+
def config_nics(self):
"""
Responsible for configuring NICs on RPM systems where the instance has
more than one configured port
- :return: None
+ :return: the value returned by ansible_utils.apply_ansible_playbook()
"""
- if len(self.__ports) > 1 and len(self.__floating_ips) > 0:
+ if len(self.__ports) > 1 and len(self.__floating_ip_dict) > 0:
if self.vm_active(block=True) and self.vm_ssh_active(block=True):
for key, port in self.__ports:
port_index = self.__ports.index((key, port))
if port_index > 0:
nic_name = 'eth' + repr(port_index)
- self.__config_nic(
+ retval = self.__config_nic(
nic_name, port,
self.__get_first_provisioning_floating_ip().ip)
logger.info('Configured NIC - %s on VM - %s',
nic_name, self.instance_settings.name)
+ return retval
def __get_first_provisioning_floating_ip(self):
"""
fip = self.__floating_ip_dict.get(floating_ip_setting.name)
if fip:
return fip
- elif len(self.__floating_ips) > 0:
- return self.__floating_ips[0]
+ elif len(self.__floating_ip_dict) > 0:
+ for key, fip in self.__floating_ip_dict.items():
+ return fip
- def __config_nic(self, nic_name, port, floating_ip):
+ def __config_nic(self, nic_name, port, ip):
"""
Although ports/NICs can contain multiple IPs, this code currently only
supports the first.
:param nic_name: Name of the interface
:param port: The port information containing the expected IP values.
- :param floating_ip: The floating IP on which to apply the playbook.
+ :param ip: The IP on which to apply the playbook.
:return: the return value from ansible
"""
- ip = port['port']['fixed_ips'][0]['ip_address']
+ port_ip = port.ips[0]['ip_address']
variables = {
- 'floating_ip': floating_ip,
+ 'floating_ip': ip,
'nic_name': nic_name,
- 'nic_ip': ip
+ 'nic_ip': port_ip
}
if self.image_settings.nic_config_pb_loc and self.keypair_settings:
def vm_active(self, block=False, poll_interval=POLL_INTERVAL):
"""
- Returns true when the VM status returns the value of
- expected_status_code
+ Returns true when the VM status returns the value of the constant
+ STATUS_ACTIVE
:param block: When true, thread will block until active or timeout
value in seconds has been exceeded (False)
:param poll_interval: The polling interval in seconds
:return: T/F
"""
if not self.__vm:
- return False
+ if expected_status_code == STATUS_DELETED:
+ return True
+ else:
+ return False
- instance = self.__nova.servers.get(self.__vm.id)
- if not instance:
+ status = nova_utils.get_server_status(self.__nova, self.__vm)
+ if not status:
logger.warning('Cannot find instance with id - ' + self.__vm.id)
return False
- if instance.status == 'ERROR':
- raise Exception('Instance had an error during deployment')
+ if status == 'ERROR':
+ raise VmInstanceCreationError(
+ 'Instance had an error during deployment')
logger.debug(
'Instance status [%s] is - %s', self.instance_settings.name,
- instance.status)
- return instance.status == expected_status_code
+ status)
+ return status == expected_status_code
def vm_ssh_active(self, block=False, poll_interval=POLL_INTERVAL):
"""
Returns True when can create a SSH session else False
:return: T/F
"""
- if len(self.__floating_ips) > 0:
+ if len(self.__floating_ip_dict) > 0:
ssh = self.ssh_client()
if ssh:
+ ssh.close()
return True
return False
fip = None
if fip_name and self.__floating_ip_dict.get(fip_name):
return self.__floating_ip_dict.get(fip_name)
- if not fip and len(self.__floating_ips) > 0:
- return self.__floating_ips[0]
- return None
+ if not fip:
+ return self.__get_first_provisioning_floating_ip()
def ssh_client(self, fip_name=None):
"""
fip = self.get_floating_ip(fip_name)
if fip:
return ansible_utils.ssh_client(
- self.__floating_ips[0].ip, self.get_image_user(),
+ self.__get_first_provisioning_floating_ip().ip,
+ self.get_image_user(),
self.keypair_settings.private_filepath,
proxy_settings=self.__os_creds.proxy_settings)
else:
def add_security_group(self, security_group):
"""
Adds a security group to this VM. Call will block until VM is active.
- :param security_group: the OpenStack security group object
+ :param security_group: the SNAPS SecurityGroup domain object
:return True if successful else False
"""
self.vm_active(block=True)
try:
nova_utils.add_security_group(self.__nova, self.get_vm_inst(),
- security_group['security_group'][
- 'name'])
+ security_group.name)
return True
except NotFound as e:
logger.warning('Security group not added - ' + str(e))
"""
Constructor
:param name: the name of the VM
- :param flavor: the VM's flavor
+ :param flavor: the VM's flavor name
:param port_settings: the port configuration settings (required)
:param security_group_names: a set of names of the security groups to
add to the VM
if kwargs.get('security_group_names'):
if isinstance(kwargs['security_group_names'], list):
- self.security_group_names = kwargs['security_group_names']
+ self.security_group_names = kwargs['security_group_names']
elif isinstance(kwargs['security_group_names'], set):
self.security_group_names = kwargs['security_group_names']
elif isinstance(kwargs['security_group_names'], str):
self.security_group_names = [kwargs['security_group_names']]
else:
- raise Exception(
+ raise VmInstanceSettingsError(
'Invalid data type for security_group_names attribute')
else:
self.security_group_names = set()
self.availability_zone = None
if not self.name or not self.flavor:
- raise Exception(
+ raise VmInstanceSettingsError(
'Instance configuration requires the attributes: name, flavor')
if len(self.port_settings) == 0:
- raise Exception(
+ raise VmInstanceSettingsError(
'Instance configuration requires port settings (aka. NICS)')
"""
self.name = kwargs.get('name')
self.port_name = kwargs.get('port_name')
+ self.port_id = kwargs.get('port_id')
self.router_name = kwargs.get('router_name')
self.subnet_name = kwargs.get('subnet_name')
if kwargs.get('provisioning') is not None:
else:
self.provisioning = True
- if not self.name or not self.port_name or not self.router_name:
- raise Exception(
- 'The attributes name, port_name and router_name are required '
- 'for FloatingIPSettings')
+ # if not self.name or not self.port_name or not self.router_name:
+ if not self.name or not self.router_name:
+ raise FloatingIpSettingsError(
+ 'The attributes name, port_name and router_name are required')
+
+ if not self.port_name and not self.port_id:
+ raise FloatingIpSettingsError(
+ 'The attributes port_name or port_id are required')
+
+
+class VmInstanceSettingsError(Exception):
+ """
+ Exception to be thrown when an VM instance settings are incorrect
+ """
+
+
+class FloatingIpSettingsError(Exception):
+ """
+ Exception to be thrown when an VM instance settings are incorrect
+ """
+
+
+class VmInstanceCreationError(Exception):
+ """
+ Exception to be thrown when an VM instance cannot be created
+ """