Changed the way floating IPs are getting assigned to VMs as the previous
[snaps.git] / snaps / openstack / create_instance.py
1 # Copyright (c) 2017 Cable Television Laboratories, Inc. ("CableLabs")
2 #                    and others.  All rights reserved.
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at:
7 #
8 #     http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 import logging
16 import time
17
18 from novaclient.exceptions import NotFound
19
20 from snaps.config.vm_inst import VmInstanceConfig, FloatingIpConfig
21 from snaps.openstack.openstack_creator import OpenStackComputeObject
22 from snaps.openstack.utils import glance_utils, cinder_utils, settings_utils
23 from snaps.openstack.utils import neutron_utils
24 from snaps.openstack.utils import nova_utils
25 from snaps.openstack.utils.nova_utils import RebootType
26 from snaps.provisioning import ansible_utils
27
28 __author__ = 'spisarski'
29
30 logger = logging.getLogger('create_instance')
31
32 POLL_INTERVAL = 3
33 STATUS_ACTIVE = 'ACTIVE'
34 STATUS_DELETED = 'DELETED'
35
36
37 class OpenStackVmInstance(OpenStackComputeObject):
38     """
39     Class responsible for managing a VM instance in OpenStack
40     """
41
42     def __init__(self, os_creds, instance_settings, image_settings,
43                  keypair_settings=None):
44         """
45         Constructor
46         :param os_creds: The connection credentials to the OpenStack API
47         :param instance_settings: Contains the settings for this VM
48         :param image_settings: The OpenStack image object settings
49         :param keypair_settings: The keypair metadata (Optional)
50         :raises Exception
51         """
52         super(self.__class__, self).__init__(os_creds)
53
54         self.__neutron = None
55
56         self.instance_settings = instance_settings
57         self.image_settings = image_settings
58         self.keypair_settings = keypair_settings
59
60         self.__floating_ip_dict = dict()
61
62         # Instantiated in self.create()
63         self.__ports = list()
64
65         # Note: this object does not change after the VM becomes active
66         self.__vm = None
67
68     def initialize(self):
69         """
70         Loads the existing VMInst, Port, FloatingIps
71         :return: VMInst domain object
72         """
73         super(self.__class__, self).initialize()
74
75         self.__neutron = neutron_utils.neutron_client(self._os_creds)
76
77         self.__ports = self.__query_ports(self.instance_settings.port_settings)
78         self.__lookup_existing_vm_by_name()
79
80     def create(self, block=False):
81         """
82         Creates a VM instance and associated objects unless they already exist
83         :param block: Thread will block until instance has either become
84                       active, error, or timeout waiting.
85                       Additionally, when True, floating IPs will not be applied
86                       until VM is active.
87         :return: VMInst domain object
88         """
89         self.initialize()
90
91         if len(self.__ports) == 0:
92             self.__ports = self.__create_ports(
93                 self.instance_settings.port_settings)
94         if not self.__vm:
95             self.__create_vm(block)
96
97         return self.__vm
98
99     def __lookup_existing_vm_by_name(self):
100         """
101         Populates the member variables 'self.vm' and 'self.floating_ips' if a
102         VM with the same name already exists
103         within the project
104         """
105         server = nova_utils.get_server(
106             self._nova, self.__neutron,
107             vm_inst_settings=self.instance_settings)
108         if server:
109             if server.name == self.instance_settings.name:
110                 self.__vm = server
111                 logger.info(
112                     'Found existing machine with name - %s',
113                     self.instance_settings.name)
114
115                 fips = neutron_utils.get_floating_ips(self.__neutron,
116                                                       self.__ports)
117                 for port_id, fip in fips:
118                     settings = self.instance_settings.floating_ip_settings
119                     for fip_setting in settings:
120                         if port_id == fip_setting.port_id:
121                             self.__floating_ip_dict[fip_setting.name] = fip
122                         else:
123                             port = neutron_utils.get_port_by_id(
124                                 self.__neutron, port_id)
125                             if port and port.name == fip_setting.port_name:
126                                 self.__floating_ip_dict[fip_setting.name] = fip
127
128     def __create_vm(self, block=False):
129         """
130         Responsible for creating the VM instance
131         :param block: Thread will block until instance has either become
132                       active, error, or timeout waiting. Floating IPs will be
133                       assigned after active when block=True
134         """
135         glance = glance_utils.glance_client(self._os_creds)
136         self.__vm = nova_utils.create_server(
137             self._nova, self.__neutron, glance, self.instance_settings,
138             self.image_settings, self.keypair_settings)
139         logger.info('Created instance with name - %s',
140                     self.instance_settings.name)
141
142         if block:
143             if not self.vm_active(block=True):
144                 raise VmInstanceCreationError(
145                     'Fatal error, VM did not become ACTIVE within the alloted '
146                     'time')
147
148         # Create server should do this but found it needed to occur here
149         for sec_grp_name in self.instance_settings.security_group_names:
150             if self.vm_active(block=True):
151                 nova_utils.add_security_group(self._nova, self.__vm,
152                                               sec_grp_name)
153             else:
154                 raise VmInstanceCreationError(
155                     'Cannot applying security group with name ' +
156                     sec_grp_name +
157                     ' to VM that did not activate with name - ' +
158                     self.instance_settings.name)
159
160         if self.instance_settings.volume_names:
161             for volume_name in self.instance_settings.volume_names:
162                 cinder = cinder_utils.cinder_client(self._os_creds)
163                 volume = cinder_utils.get_volume(
164                     cinder, volume_name=volume_name)
165
166                 if volume and self.vm_active(block=True):
167                     timeout = 30
168                     vm = nova_utils.attach_volume(
169                         self._nova, self.__neutron, self.__vm, volume, timeout)
170
171                     if vm:
172                         self.__vm = vm
173                     else:
174                         logger.warn('Volume [%s] not attached within timeout '
175                                     'of [%s]', volume.name, timeout)
176                 else:
177                     logger.warn('Unable to attach volume named [%s]',
178                                 volume_name)
179
180         self.__apply_floating_ips()
181
182     def __apply_floating_ips(self):
183         """
184         Applies the configured floating IPs to the necessary ports
185         """
186         port_dict = dict()
187         for key, port in self.__ports:
188             port_dict[key] = port
189
190         # Apply floating IPs
191         for floating_ip_setting in self.instance_settings.floating_ip_settings:
192             self.add_floating_ip(floating_ip_setting)
193
194     def add_floating_ip(self, floating_ip_setting):
195         """
196         Adds a floating IP to a running instance
197         :param floating_ip_setting - the floating IP configuration
198         :return: the floating ip object
199         """
200         port_dict = dict()
201         for key, port in self.__ports:
202             port_dict[key] = port
203
204         # Apply floating IP
205         port = port_dict.get(floating_ip_setting.port_name)
206
207         if not port:
208             raise VmInstanceCreationError(
209                 'Cannot find port object with name - ' +
210                 floating_ip_setting.port_name)
211
212         # Setup Floating IP only if there is a router with an external
213         # gateway
214         ext_gateway = self.__ext_gateway_by_router(
215             floating_ip_setting.router_name)
216         if ext_gateway and self.vm_active(block=True):
217             floating_ip = neutron_utils.create_floating_ip(
218                 self.__neutron, ext_gateway, port.id)
219             self.__floating_ip_dict[floating_ip_setting.name] = floating_ip
220
221             logger.info(
222                 'Created floating IP %s via router - %s', floating_ip.ip,
223                 floating_ip_setting.router_name)
224
225             return floating_ip
226         else:
227             raise VmInstanceCreationError(
228                 'Unable to add floating IP to port, cannot locate router '
229                 'with an external gateway ')
230
231     def __ext_gateway_by_router(self, router_name):
232         """
233         Returns network name for the external network attached to a router or
234         None if not found
235         :param router_name: The name of the router to lookup
236         :return: the external network name or None
237         """
238         router = neutron_utils.get_router(
239             self.__neutron, router_name=router_name)
240         if router and router.external_network_id:
241             network = neutron_utils.get_network_by_id(
242                 self.__neutron, router.external_network_id)
243             if network:
244                 return network.name
245         return None
246
247     def clean(self):
248         """
249         Destroys the VM instance
250         """
251
252         # Cleanup floating IPs
253         for name, floating_ip in self.__floating_ip_dict.items():
254             logger.info('Deleting Floating IP - ' + floating_ip.ip)
255             neutron_utils.delete_floating_ip(self.__neutron, floating_ip)
256
257         self.__floating_ip_dict = dict()
258
259         # Cleanup ports
260         for name, port in self.__ports:
261             logger.info('Deleting Port with ID - %s ', port.id)
262             neutron_utils.delete_port(self.__neutron, port)
263
264         self.__ports = list()
265
266         if self.__vm:
267             # Detach Volume
268             for volume_rec in self.__vm.volume_ids:
269                 cinder = cinder_utils.cinder_client(self._os_creds)
270                 volume = cinder_utils.get_volume_by_id(
271                     cinder, volume_rec['id'])
272                 if volume:
273                     vm = nova_utils.detach_volume(
274                         self._nova, self.__neutron, self.__vm, volume, 30)
275                     if vm:
276                         self.__vm = vm
277                     else:
278                         logger.warn(
279                             'Timeout waiting to detach volume %s', volume.name)
280                 else:
281                     logger.warn('Unable to detach volume with ID - [%s]',
282                                 volume_rec['id'])
283
284             # Cleanup VM
285             logger.info(
286                 'Deleting VM instance - ' + self.instance_settings.name)
287
288             try:
289                 nova_utils.delete_vm_instance(self._nova, self.__vm)
290             except NotFound as e:
291                 logger.warn('Instance already deleted - %s', e)
292
293             # Block until instance cannot be found or returns the status of
294             # DELETED
295             logger.info('Checking deletion status')
296
297             if self.vm_deleted(block=True):
298                 logger.info(
299                     'VM has been properly deleted VM with name - %s',
300                     self.instance_settings.name)
301                 self.__vm = None
302             else:
303                 logger.error(
304                     'VM not deleted within the timeout period of %s '
305                     'seconds', self.instance_settings.vm_delete_timeout)
306
307     def __query_ports(self, port_settings):
308         """
309         Returns the previously configured ports or an empty list if none
310         exist
311         :param port_settings: A list of PortSetting objects
312         :return: a list of OpenStack port tuples where the first member is the
313                  port name and the second is the port object
314         """
315         ports = list()
316
317         for port_setting in port_settings:
318             port = neutron_utils.get_port(
319                 self.__neutron, port_settings=port_setting)
320             if port:
321                 ports.append((port_setting.name, port))
322
323         return ports
324
325     def __create_ports(self, port_settings):
326         """
327         Returns the previously configured ports or creates them if they do not
328         exist
329         :param port_settings: A list of PortSetting objects
330         :return: a list of OpenStack port tuples where the first member is the
331                  port name and the second is the port object
332         """
333         ports = list()
334
335         for port_setting in port_settings:
336             port = neutron_utils.get_port(
337                 self.__neutron, port_settings=port_setting)
338             if not port:
339                 port = neutron_utils.create_port(
340                     self.__neutron, self._os_creds, port_setting)
341                 if port:
342                     ports.append((port_setting.name, port))
343
344         return ports
345
346     def get_os_creds(self):
347         """
348         Returns the OpenStack credentials used to create these objects
349         :return: the credentials
350         """
351         return self._os_creds
352
353     def get_vm_inst(self):
354         """
355         Returns the latest version of this server object from OpenStack
356         :return: Server object
357         """
358         return nova_utils.get_server_object_by_id(
359             self._nova, self.__neutron, self.__vm.id)
360
361     def get_console_output(self):
362         """
363         Returns the vm console object for parsing logs
364         :return: the console output object
365         """
366         return nova_utils.get_server_console_output(self._nova, self.__vm)
367
368     def get_port_ip(self, port_name, subnet_name=None):
369         """
370         Returns the first IP for the port corresponding with the port_name
371         parameter when subnet_name is None else returns the IP address that
372         corresponds to the subnet_name parameter
373         :param port_name: the name of the port from which to return the IP
374         :param subnet_name: the name of the subnet attached to this IP
375         :return: the IP or None if not found
376         """
377         port = self.get_port_by_name(port_name)
378         if port:
379             if subnet_name:
380                 subnet = neutron_utils.get_subnet(
381                     self.__neutron, subnet_name=subnet_name)
382                 if not subnet:
383                     logger.warning('Cannot retrieve port IP as subnet could '
384                                    'not be located with name - %s',
385                                    subnet_name)
386                     return None
387                 for fixed_ip in port.ips:
388                     if fixed_ip['subnet_id'] == subnet.id:
389                         return fixed_ip['ip_address']
390             else:
391                 if port.ips and len(port.ips) > 0:
392                     return port.ips[0]['ip_address']
393         return None
394
395     def get_port_mac(self, port_name):
396         """
397         Returns the first IP for the port corresponding with the port_name
398         parameter
399         TODO - Add in the subnet as an additional parameter as a port may have
400         multiple fixed_ips
401         :param port_name: the name of the port from which to return the IP
402         :return: the IP or None if not found
403         """
404         port = self.get_port_by_name(port_name)
405         if port:
406             return port.mac_address
407         return None
408
409     def get_port_by_name(self, port_name):
410         """
411         Retrieves the OpenStack port object by its given name
412         :param port_name: the name of the port
413         :return: the OpenStack port object or None if not exists
414         """
415         for key, port in self.__ports:
416             if key == port_name:
417                 return port
418         logger.warning('Cannot find port with name - ' + port_name)
419         return None
420
421     def get_vm_info(self):
422         """
423         Returns a dictionary of a VMs info as returned by OpenStack
424         :return: a dict()
425         """
426         return nova_utils.get_server_info(self._nova, self.__vm)
427
428     def __get_first_provisioning_floating_ip(self):
429         """
430         Returns the first floating IP tagged with the Floating IP name if
431         exists else the first one found
432         :return:
433         """
434         for floating_ip_setting in self.instance_settings.floating_ip_settings:
435             if floating_ip_setting.provisioning:
436                 fip = self.__floating_ip_dict.get(floating_ip_setting.name)
437                 if fip:
438                     return fip
439                 elif len(self.__floating_ip_dict) > 0:
440                     for key, fip in self.__floating_ip_dict.items():
441                         return fip
442
443         # When cannot be found above
444         if len(self.__floating_ip_dict) > 0:
445             for key, fip in self.__floating_ip_dict.items():
446                 return fip
447
448     def apply_ansible_playbook(self, pb_file_loc, variables=None,
449                                fip_name=None):
450         """
451         Applies a playbook to a VM
452         :param pb_file_loc: the file location of the playbook to be applied
453         :param variables: a dict() of substitution values required by the
454                           playbook
455         :param fip_name: the name of the floating IP to use for applying the
456                          playbook (default - will take the first)
457         :return: the return value from ansible
458         """
459         return ansible_utils.apply_playbook(
460             pb_file_loc, [self.get_floating_ip(fip_name=fip_name).ip],
461             self.get_image_user(),
462             ssh_priv_key_file_path=self.keypair_settings.private_filepath,
463             variables=variables, proxy_setting=self._os_creds.proxy_settings)
464
465     def get_image_user(self):
466         """
467         Returns the instance sudo_user if it has been configured in the
468         instance_settings else it returns the image_settings.image_user value
469         """
470         if self.instance_settings.sudo_user:
471             return self.instance_settings.sudo_user
472         else:
473             return self.image_settings.image_user
474
475     def vm_deleted(self, block=False, poll_interval=POLL_INTERVAL):
476         """
477         Returns true when the VM status returns the value of
478         expected_status_code or instance retrieval throws a NotFound exception.
479         :param block: When true, thread will block until active or timeout
480                       value in seconds has been exceeded (False)
481         :param poll_interval: The polling interval in seconds
482         :return: T/F
483         """
484         try:
485             return self.__vm_status_check(
486                 STATUS_DELETED, block,
487                 self.instance_settings.vm_delete_timeout, poll_interval)
488         except NotFound as e:
489             logger.debug(
490                 "Instance not found when querying status for %s with message "
491                 "%s", STATUS_DELETED, e)
492             return True
493
494     def vm_active(self, block=False, poll_interval=POLL_INTERVAL):
495         """
496         Returns true when the VM status returns the value of the constant
497         STATUS_ACTIVE
498         :param block: When true, thread will block until active or timeout
499                       value in seconds has been exceeded (False)
500         :param poll_interval: The polling interval in seconds
501         :return: T/F
502         """
503         if self.__vm_status_check(
504                 STATUS_ACTIVE, block, self.instance_settings.vm_boot_timeout,
505                 poll_interval):
506             self.__vm = nova_utils.get_server_object_by_id(
507                 self._nova, self.__neutron, self.__vm.id)
508             return True
509         return False
510
511     def __vm_status_check(self, expected_status_code, block, timeout,
512                           poll_interval):
513         """
514         Returns true when the VM status returns the value of
515         expected_status_code
516         :param expected_status_code: instance status evaluated with this
517                                      string value
518         :param block: When true, thread will block until active or timeout
519                       value in seconds has been exceeded (False)
520         :param timeout: The timeout value
521         :param poll_interval: The polling interval in seconds
522         :return: T/F
523         """
524         # sleep and wait for VM status change
525         if block:
526             start = time.time()
527         else:
528             return self.__status(expected_status_code)
529
530         while timeout > time.time() - start:
531             status = self.__status(expected_status_code)
532             if status:
533                 logger.info('VM is - ' + expected_status_code)
534                 return True
535
536             logger.debug('Retry querying VM status in ' + str(
537                 poll_interval) + ' seconds')
538             time.sleep(poll_interval)
539             logger.debug('VM status query timeout in ' + str(
540                 timeout - (time.time() - start)))
541
542         logger.error(
543             'Timeout checking for VM status for ' + expected_status_code)
544         return False
545
546     def __status(self, expected_status_code):
547         """
548         Returns True when active else False
549         :param expected_status_code: instance status evaluated with this string
550                                      value
551         :return: T/F
552         """
553         if not self.__vm:
554             if expected_status_code == STATUS_DELETED:
555                 return True
556             else:
557                 return False
558
559         status = nova_utils.get_server_status(self._nova, self.__vm)
560         if not status:
561             logger.warning('Cannot find instance with id - ' + self.__vm.id)
562             return False
563
564         if status == 'ERROR':
565             raise VmInstanceCreationError(
566                 'Instance had an error during deployment')
567         logger.debug(
568             'Instance status [%s] is - %s', self.instance_settings.name,
569             status)
570         return status == expected_status_code
571
572     def vm_ssh_active(self, user_override=None, password=None, block=False,
573                       timeout=None, poll_interval=POLL_INTERVAL):
574         """
575         Returns true when the VM can be accessed via SSH
576         :param user_override: overrides the user with which to create the
577                               connection
578         :param password: overrides the use of a password instead of a private
579                          key with which to create the connection
580         :param block: When true, thread will block until active or timeout
581                       value in seconds has been exceeded (False)
582         :param timeout: the number of seconds to retry obtaining the connection
583                         and overrides the ssh_connect_timeout member of the
584                         self.instance_settings object
585         :param poll_interval: The polling interval
586         :return: T/F
587         """
588         # sleep and wait for VM status change
589         logger.info('Checking if VM is active')
590
591         if not timeout:
592             timeout = self.instance_settings.ssh_connect_timeout
593
594         if self.vm_active(block=True):
595             if block:
596                 start = time.time()
597             else:
598                 start = time.time() - timeout
599
600             while timeout > time.time() - start:
601                 status = self.__ssh_active(
602                     user_override=user_override, password=password)
603                 if status:
604                     logger.info('SSH is active for VM instance')
605                     return True
606
607                 logger.debug('Retry SSH connection in ' + str(
608                     poll_interval) + ' seconds')
609                 time.sleep(poll_interval)
610                 logger.debug('SSH connection timeout in ' + str(
611                     timeout - (time.time() - start)))
612
613         logger.error('Timeout attempting to connect with VM via SSH')
614         return False
615
616     def __ssh_active(self, user_override=None, password=None):
617         """
618         Returns True when can create a SSH session else False
619         :return: T/F
620         """
621         if len(self.__floating_ip_dict) > 0:
622             ssh = self.ssh_client(
623                 user_override=user_override, password=password)
624             if ssh:
625                 ssh.close()
626                 return True
627         return False
628
629     def cloud_init_complete(self, block=False, poll_interval=POLL_INTERVAL):
630         """
631         Returns true when the VM's cloud-init routine has completed.
632         Note: this is currently done via SSH, therefore, if this instance does
633               not have a Floating IP or a running SSH server, this routine
634               will always return False or raise an Exception
635         :param block: When true, thread will block until active or timeout
636                       value in seconds has been exceeded (False)
637         :param poll_interval: The polling interval
638         :return: T/F
639         """
640         # sleep and wait for VM status change
641         logger.info('Checking if cloud-init has completed')
642
643         timeout = self.instance_settings.cloud_init_timeout
644
645         if self.vm_active(block=True) and self.vm_ssh_active(block=True):
646             if block:
647                 start = time.time()
648             else:
649                 start = time.time() - timeout
650
651             while timeout > time.time() - start:
652                 status = self.__cloud_init_complete()
653                 if status:
654                     logger.info('cloud-init complete for VM instance')
655                     return True
656
657                 logger.debug('Retry cloud-init query in ' + str(
658                     poll_interval) + ' seconds')
659                 time.sleep(poll_interval)
660                 logger.debug('cloud-init complete timeout in ' + str(
661                     timeout - (time.time() - start)))
662
663         logger.error('Timeout waiting for cloud-init to complete')
664         return False
665
666     def __cloud_init_complete(self):
667         """
668         Returns True when can create a SSH session else False
669         :return: T/F
670         """
671         if len(self.__floating_ip_dict) > 0:
672             ssh = self.ssh_client()
673             if ssh:
674                 stdin1, stdout1, sterr1 = ssh.exec_command(
675                     'ls -l /var/lib/cloud/instance/boot-finished')
676                 return stdout1.channel.recv_exit_status() == 0
677         return False
678
679     def get_floating_ip(self, fip_name=None):
680         """
681         Returns the floating IP object byt name if found, else the first known,
682         else None
683         :param fip_name: the name of the floating IP to return
684         :return: the SSH client or None
685         """
686         if fip_name and self.__floating_ip_dict.get(fip_name):
687             return self.__floating_ip_dict.get(fip_name)
688         else:
689             return self.__get_first_provisioning_floating_ip()
690
691     def ssh_client(self, fip_name=None, user_override=None, password=None):
692         """
693         Returns an SSH client using the name or the first known floating IP if
694         exists, else None
695         :param fip_name: the name of the floating IP to return
696         :param user_override: the username to use instead of the default
697         :param password: the password to use instead of the private key
698         :return: the SSH client or None
699         """
700         fip = self.get_floating_ip(fip_name)
701
702         ansible_user = self.get_image_user()
703         if user_override:
704             ansible_user = user_override
705
706         if password:
707             private_key = None
708         else:
709             private_key = self.keypair_settings.private_filepath
710
711         if fip:
712             return ansible_utils.ssh_client(
713                 self.__get_first_provisioning_floating_ip().ip,
714                 ansible_user,
715                 private_key_filepath=private_key,
716                 password=password,
717                 proxy_settings=self._os_creds.proxy_settings)
718         else:
719             FloatingIPAllocationError(
720                 'Cannot return an SSH client. No Floating IP configured')
721
722     def add_security_group(self, security_group):
723         """
724         Adds a security group to this VM. Call will block until VM is active.
725         :param security_group: the SNAPS SecurityGroup domain object
726         :return True if successful else False
727         """
728         self.vm_active(block=True)
729
730         if not security_group:
731             logger.warning('Security group object is None, cannot add')
732             return False
733
734         try:
735             nova_utils.add_security_group(self._nova, self.get_vm_inst(),
736                                           security_group.name)
737             return True
738         except NotFound as e:
739             logger.warning('Security group not added - ' + str(e))
740             return False
741
742     def remove_security_group(self, security_group):
743         """
744         Removes a security group to this VM. Call will block until VM is active
745         :param security_group: the OpenStack security group object
746         :return True if successful else False
747         """
748         self.vm_active(block=True)
749
750         if not security_group:
751             logger.warning('Security group object is None, cannot remove')
752             return False
753
754         try:
755             nova_utils.remove_security_group(self._nova, self.get_vm_inst(),
756                                              security_group)
757             return True
758         except NotFound as e:
759             logger.warning('Security group not removed - ' + str(e))
760             return False
761
762     def reboot(self, reboot_type=RebootType.soft):
763         """
764         Issues a reboot call
765         :param reboot_type: instance of
766                             snaps.openstack.utils.nova_utils.RebootType
767                             enumeration
768         :return:
769         """
770         nova_utils.reboot_server(
771             self._nova, self.__vm, reboot_type=reboot_type)
772
773
774 def generate_creator(os_creds, vm_inst, image_config, keypair_config=None):
775     """
776     Initializes an OpenStackVmInstance object
777     :param os_creds: the OpenStack credentials
778     :param vm_inst: the SNAPS-OO VmInst domain object
779     :param image_config: the associated ImageConfig object
780     :param keypair_config: the associated KeypairConfig object (optional)
781     :return: an initialized OpenStackVmInstance object
782     """
783     nova = nova_utils.nova_client(os_creds)
784     neutron = neutron_utils.neutron_client(os_creds)
785     derived_inst_config = settings_utils.create_vm_inst_config(
786         nova, neutron, vm_inst)
787
788     derived_inst_creator = OpenStackVmInstance(
789         os_creds, derived_inst_config, image_config, keypair_config)
790     derived_inst_creator.initialize()
791     return derived_inst_creator
792
793
794 class VmInstanceSettings(VmInstanceConfig):
795     """
796     Deprecated, use snaps.config.vm_inst.VmInstanceConfig instead
797     """
798     def __init__(self, **kwargs):
799         from warnings import warn
800         warn('Use snaps.config.vm_inst.VmInstanceConfig instead',
801              DeprecationWarning)
802         super(self.__class__, self).__init__(**kwargs)
803
804
805 class FloatingIpSettings(FloatingIpConfig):
806     """
807     Deprecated, use snaps.config.vm_inst.FloatingIpConfig instead
808     """
809     def __init__(self, **kwargs):
810         from warnings import warn
811         warn('Use snaps.config.vm_inst.FloatingIpConfig instead',
812              DeprecationWarning)
813         super(self.__class__, self).__init__(**kwargs)
814
815
816 class VmInstanceCreationError(Exception):
817     """
818     Exception to be thrown when an VM instance cannot be created
819     """
820
821
822 class FloatingIPAllocationError(Exception):
823     """
824     Exception to be thrown when an VM instance cannot allocate a floating IP
825     """