| 
									
										
										
										
											2017-09-09 18:19:09 +05:30
										 |  |  | from datetime import datetime | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | from celery.exceptions import MaxRetriesExceededError | 
					
						
							| 
									
										
										
										
											2017-08-06 18:42:27 +02:00
										 |  |  | from celery.utils.log import get_task_logger | 
					
						
							| 
									
										
										
										
											2017-09-15 13:09:46 +02:00
										 |  |  | from celery import current_task | 
					
						
							| 
									
										
										
										
											2017-08-06 18:42:27 +02:00
										 |  |  | from django.conf import settings | 
					
						
							| 
									
										
										
										
											2017-09-09 18:19:09 +05:30
										 |  |  | from django.core.mail import EmailMessage | 
					
						
							| 
									
										
										
										
											2017-09-16 20:55:37 +05:30
										 |  |  | from django.utils import translation | 
					
						
							| 
									
										
										
										
											2017-09-16 19:30:31 +05:30
										 |  |  | from django.utils.translation import ugettext_lazy as _ | 
					
						
							| 
									
										
										
										
											2017-09-09 18:19:09 +05:30
										 |  |  | 
 | 
					
						
							|  |  |  | from dynamicweb.celery import app | 
					
						
							| 
									
										
										
										
											2017-09-25 00:34:18 +05:30
										 |  |  | from hosting.models import HostingOrder, HostingBill | 
					
						
							| 
									
										
										
										
											2017-09-15 11:32:17 +02:00
										 |  |  | from membership.models import StripeCustomer, CustomUser | 
					
						
							| 
									
										
										
										
											2017-08-06 18:42:27 +02:00
										 |  |  | from opennebula_api.models import OpenNebulaManager | 
					
						
							|  |  |  | from opennebula_api.serializers import VirtualMachineSerializer | 
					
						
							| 
									
										
										
										
											2017-09-25 00:02:36 +05:30
										 |  |  | from utils.hosting_utils import get_all_public_keys, get_or_create_vm_detail | 
					
						
							| 
									
										
										
										
											2017-08-06 18:42:27 +02:00
										 |  |  | from utils.forms import UserBillingAddressForm | 
					
						
							| 
									
										
										
										
											2017-09-16 19:30:31 +05:30
										 |  |  | from utils.mailer import BaseEmail | 
					
						
							| 
									
										
										
										
											2017-08-06 18:42:27 +02:00
										 |  |  | from utils.models import BillingAddress | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | logger = get_task_logger(__name__) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def retry_task(task, exception=None): | 
					
						
							|  |  |  |     """Retries the specified task using a "backing off countdown",
 | 
					
						
							|  |  |  |     meaning that the interval between retries grows exponentially | 
					
						
							|  |  |  |     with every retry. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Arguments: | 
					
						
							|  |  |  |         task: | 
					
						
							|  |  |  |             The task to retry. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         exception: | 
					
						
							|  |  |  |             Optionally, the exception that caused the retry. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def backoff(attempts): | 
					
						
							|  |  |  |         return 2 ** attempts | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     kwargs = { | 
					
						
							|  |  |  |         'countdown': backoff(task.request.retries), | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if exception: | 
					
						
							|  |  |  |         kwargs['exc'] = exception | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-06 21:54:12 +02:00
										 |  |  |     raise task.retry(**kwargs) | 
					
						
							| 
									
										
										
										
											2017-08-06 18:42:27 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-06 21:54:12 +02:00
										 |  |  | @app.task(bind=True, max_retries=settings.CELERY_MAX_RETRIES) | 
					
						
							| 
									
										
										
										
											2017-08-23 20:12:50 +02:00
										 |  |  | def create_vm_task(self, vm_template_id, user, specs, template, | 
					
						
							|  |  |  |                    stripe_customer_id, billing_address_data, | 
					
						
							| 
									
										
										
										
											2017-08-06 18:42:27 +02:00
										 |  |  |                    billing_address_id, | 
					
						
							| 
									
										
										
										
											2017-08-24 11:49:41 +05:30
										 |  |  |                    charge, cc_details): | 
					
						
							| 
									
										
										
										
											2017-09-15 13:09:46 +02:00
										 |  |  |     logger.debug("Running create_vm_task on {}".format(current_task.request.hostname)) | 
					
						
							| 
									
										
										
										
											2017-08-06 18:52:23 +02:00
										 |  |  |     vm_id = None | 
					
						
							| 
									
										
										
										
											2017-08-06 18:42:27 +02:00
										 |  |  |     try: | 
					
						
							|  |  |  |         final_price = specs.get('price') | 
					
						
							| 
									
										
										
										
											2017-08-23 20:12:50 +02:00
										 |  |  |         billing_address = BillingAddress.objects.filter( | 
					
						
							|  |  |  |             id=billing_address_id).first() | 
					
						
							| 
									
										
										
										
											2017-08-06 18:42:27 +02:00
										 |  |  |         customer = StripeCustomer.objects.filter(id=stripe_customer_id).first() | 
					
						
							| 
									
										
										
										
											2017-09-10 15:26:29 +05:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-09 20:46:43 +05:30
										 |  |  |         if 'pass' in user: | 
					
						
							| 
									
										
										
										
											2017-09-10 15:26:29 +05:30
										 |  |  |             on_user = user.get('email') | 
					
						
							|  |  |  |             on_pass = user.get('pass') | 
					
						
							|  |  |  |             logger.debug("Using user {user} to create VM".format(user=on_user)) | 
					
						
							| 
									
										
										
										
											2017-09-10 14:52:06 +05:30
										 |  |  |             vm_name = None | 
					
						
							| 
									
										
										
										
											2017-09-09 20:46:43 +05:30
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2017-09-10 15:26:29 +05:30
										 |  |  |             on_user = settings.OPENNEBULA_USERNAME | 
					
						
							|  |  |  |             on_pass = settings.OPENNEBULA_PASSWORD | 
					
						
							| 
									
										
										
										
											2017-09-09 18:19:09 +05:30
										 |  |  |             logger.debug("Using OpenNebula admin user to create VM") | 
					
						
							| 
									
										
										
										
											2017-09-10 14:52:06 +05:30
										 |  |  |             vm_name = "{email}-{template_name}-{date}".format( | 
					
						
							|  |  |  |                 email=user.get('email'), | 
					
						
							|  |  |  |                 template_name=template.get('name'), | 
					
						
							|  |  |  |                 date=int(datetime.now().strftime("%s"))) | 
					
						
							| 
									
										
										
										
											2017-08-06 18:42:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-10 15:26:29 +05:30
										 |  |  |         # Create OpenNebulaManager | 
					
						
							|  |  |  |         manager = OpenNebulaManager(email=on_user, password=on_pass) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-06 18:42:27 +02:00
										 |  |  |         vm_id = manager.create_vm( | 
					
						
							|  |  |  |             template_id=vm_template_id, | 
					
						
							|  |  |  |             specs=specs, | 
					
						
							| 
									
										
										
										
											2017-08-19 18:02:55 +05:30
										 |  |  |             ssh_key=settings.ONEADMIN_USER_SSH_PUBLIC_KEY, | 
					
						
							| 
									
										
										
										
											2017-09-10 14:52:06 +05:30
										 |  |  |             vm_name=vm_name | 
					
						
							| 
									
										
										
										
											2017-08-06 18:42:27 +02:00
										 |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-06 18:52:23 +02:00
										 |  |  |         if vm_id is None: | 
					
						
							|  |  |  |             raise Exception("Could not create VM") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-06 18:42:27 +02:00
										 |  |  |         # Create a Hosting Order | 
					
						
							|  |  |  |         order = HostingOrder.create( | 
					
						
							|  |  |  |             price=final_price, | 
					
						
							|  |  |  |             vm_id=vm_id, | 
					
						
							|  |  |  |             customer=customer, | 
					
						
							|  |  |  |             billing_address=billing_address | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Create a Hosting Bill | 
					
						
							|  |  |  |         HostingBill.create( | 
					
						
							|  |  |  |             customer=customer, billing_address=billing_address) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Create Billing Address for User if he does not have one | 
					
						
							|  |  |  |         if not customer.user.billing_addresses.count(): | 
					
						
							|  |  |  |             billing_address_data.update({ | 
					
						
							|  |  |  |                 'user': customer.user.id | 
					
						
							|  |  |  |             }) | 
					
						
							|  |  |  |             billing_address_user_form = UserBillingAddressForm( | 
					
						
							|  |  |  |                 billing_address_data) | 
					
						
							|  |  |  |             billing_address_user_form.is_valid() | 
					
						
							|  |  |  |             billing_address_user_form.save() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-21 01:54:55 +05:30
										 |  |  |         # Associate an order with a stripe subscription | 
					
						
							| 
									
										
										
										
											2017-08-06 18:42:27 +02:00
										 |  |  |         charge_object = DictDotLookup(charge) | 
					
						
							| 
									
										
										
										
											2017-08-24 11:49:41 +05:30
										 |  |  |         order.set_subscription_id(charge_object, cc_details) | 
					
						
							| 
									
										
										
										
											2017-08-06 18:42:27 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # If the Stripe payment succeeds, set order status approved | 
					
						
							|  |  |  |         order.set_approved() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         vm = VirtualMachineSerializer(manager.get_vm(vm_id)).data | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         context = { | 
					
						
							|  |  |  |             'name': user.get('name'), | 
					
						
							|  |  |  |             'email': user.get('email'), | 
					
						
							|  |  |  |             'cores': specs.get('cpu'), | 
					
						
							|  |  |  |             'memory': specs.get('memory'), | 
					
						
							|  |  |  |             'storage': specs.get('disk_size'), | 
					
						
							|  |  |  |             'price': specs.get('price'), | 
					
						
							|  |  |  |             'template': template.get('name'), | 
					
						
							|  |  |  |             'vm.name': vm['name'], | 
					
						
							|  |  |  |             'vm.id': vm['vm_id'], | 
					
						
							|  |  |  |             'order.id': order.id | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         email_data = { | 
					
						
							|  |  |  |             'subject': settings.DCL_TEXT + " Order from %s" % context['email'], | 
					
						
							|  |  |  |             'from_email': settings.DCL_SUPPORT_FROM_ADDRESS, | 
					
						
							|  |  |  |             'to': ['info@ungleich.ch'], | 
					
						
							| 
									
										
										
										
											2017-08-23 20:12:50 +02:00
										 |  |  |             'body': "\n".join( | 
					
						
							|  |  |  |                 ["%s=%s" % (k, v) for (k, v) in context.items()]), | 
					
						
							| 
									
										
										
										
											2017-08-06 18:42:27 +02:00
										 |  |  |             'reply_to': [context['email']], | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         email = EmailMessage(**email_data) | 
					
						
							|  |  |  |         email.send() | 
					
						
							| 
									
										
										
										
											2017-09-15 11:32:17 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-15 12:43:48 +05:30
										 |  |  |         if 'pass' in user: | 
					
						
							| 
									
										
										
										
											2017-09-25 00:34:18 +05:30
										 |  |  |             lang = 'en-us' | 
					
						
							| 
									
										
										
										
											2017-09-16 20:55:37 +05:30
										 |  |  |             if user.get('language') is not None: | 
					
						
							|  |  |  |                 logger.debug("Language is set to {}".format(user.get('language'))) | 
					
						
							|  |  |  |                 lang = user.get('language') | 
					
						
							|  |  |  |             translation.activate(lang) | 
					
						
							| 
									
										
										
										
											2017-09-16 19:30:31 +05:30
										 |  |  |             # Send notification to the user as soon as VM has been booked | 
					
						
							|  |  |  |             context = { | 
					
						
							|  |  |  |                 'vm': vm, | 
					
						
							|  |  |  |                 'order': order, | 
					
						
							|  |  |  |                 'base_url': "{0}://{1}".format(user.get('request_scheme'), | 
					
						
							|  |  |  |                                                user.get('request_host')), | 
					
						
							|  |  |  |                 'page_header': _( | 
					
						
							|  |  |  |                     'Your New VM %(vm_name)s at Data Center Light') % { | 
					
						
							|  |  |  |                                    'vm_name': vm.get('name')} | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             email_data = { | 
					
						
							|  |  |  |                 'subject': context.get('page_header'), | 
					
						
							|  |  |  |                 'to': user.get('email'), | 
					
						
							|  |  |  |                 'context': context, | 
					
						
							|  |  |  |                 'template_name': 'new_booked_vm', | 
					
						
							|  |  |  |                 'template_path': 'hosting/emails/', | 
					
						
							|  |  |  |                 'from_address': settings.DCL_SUPPORT_FROM_ADDRESS, | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             email = BaseEmail(**email_data) | 
					
						
							|  |  |  |             email.send() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-15 12:43:48 +05:30
										 |  |  |             # try to see if we have the IP and that if the ssh keys can | 
					
						
							|  |  |  |             # be configured | 
					
						
							|  |  |  |             new_host = manager.get_primary_ipv4(vm_id) | 
					
						
							| 
									
										
										
										
											2017-09-15 11:32:17 +02:00
										 |  |  |             logger.debug("New VM ID is {vm_id}".format(vm_id=vm_id)) | 
					
						
							| 
									
										
										
										
											2017-09-15 12:43:48 +05:30
										 |  |  |             if new_host is not None: | 
					
						
							| 
									
										
										
										
											2017-09-15 11:32:17 +02:00
										 |  |  |                 custom_user = CustomUser.objects.get(email=user.get('email')) | 
					
						
							| 
									
										
										
										
											2017-09-25 00:02:36 +05:30
										 |  |  |                 get_or_create_vm_detail(custom_user, manager, vm_id) | 
					
						
							| 
									
										
										
										
											2017-09-15 11:32:17 +02:00
										 |  |  |                 if custom_user is not None: | 
					
						
							|  |  |  |                     public_keys = get_all_public_keys(custom_user) | 
					
						
							|  |  |  |                     keys = [{'value': key, 'state': True} for key in | 
					
						
							|  |  |  |                             public_keys] | 
					
						
							|  |  |  |                     if len(keys) > 0: | 
					
						
							|  |  |  |                         logger.debug( | 
					
						
							|  |  |  |                             "Calling configure on {host} for {num_keys} keys".format( | 
					
						
							|  |  |  |                                 host=new_host, num_keys=len(keys))) | 
					
						
							|  |  |  |                         # Let's delay the task by 75 seconds to be sure | 
					
						
							|  |  |  |                         # that we run the cdist configure after the host | 
					
						
							|  |  |  |                         # is up | 
					
						
							|  |  |  |                         manager.manage_public_key(keys, | 
					
						
							|  |  |  |                                                   hosts=[new_host], | 
					
						
							|  |  |  |                                                   countdown=75) | 
					
						
							| 
									
										
										
										
											2017-08-06 18:42:27 +02:00
										 |  |  |     except Exception as e: | 
					
						
							|  |  |  |         logger.error(str(e)) | 
					
						
							| 
									
										
										
										
											2017-08-06 21:54:12 +02:00
										 |  |  |         try: | 
					
						
							|  |  |  |             retry_task(self) | 
					
						
							|  |  |  |         except MaxRetriesExceededError: | 
					
						
							| 
									
										
										
										
											2017-08-23 20:12:50 +02:00
										 |  |  |             msg_text = 'Finished {} retries for create_vm_task'.format( | 
					
						
							|  |  |  |                 self.request.retries) | 
					
						
							| 
									
										
										
										
											2017-08-06 21:54:12 +02:00
										 |  |  |             logger.error(msg_text) | 
					
						
							|  |  |  |             # Try sending email and stop | 
					
						
							|  |  |  |             email_data = { | 
					
						
							| 
									
										
										
										
											2017-08-23 20:12:50 +02:00
										 |  |  |                 'subject': '{} CELERY TASK ERROR: {}'.format(settings.DCL_TEXT, | 
					
						
							|  |  |  |                                                              msg_text), | 
					
						
							| 
									
										
										
										
											2017-09-15 13:09:46 +02:00
										 |  |  |                 'from_email': current_task.request.hostname, | 
					
						
							|  |  |  |                 'to': settings.DCL_ERROR_EMAILS_TO_LIST, | 
					
						
							| 
									
										
										
										
											2017-08-06 22:19:27 +02:00
										 |  |  |                 'body': ',\n'.join(str(i) for i in self.request.args) | 
					
						
							| 
									
										
										
										
											2017-08-06 21:54:12 +02:00
										 |  |  |             } | 
					
						
							|  |  |  |             email = EmailMessage(**email_data) | 
					
						
							|  |  |  |             email.send() | 
					
						
							|  |  |  |             return | 
					
						
							| 
									
										
										
										
											2017-08-06 18:42:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-06 18:52:23 +02:00
										 |  |  |     return vm_id | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-06 18:42:27 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | class DictDotLookup(object): | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     Creates objects that behave much like a dictionaries, but allow nested | 
					
						
							|  |  |  |     key access using object '.' (dot) lookups. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, d): | 
					
						
							|  |  |  |         for k in d: | 
					
						
							|  |  |  |             if isinstance(d[k], dict): | 
					
						
							|  |  |  |                 self.__dict__[k] = DictDotLookup(d[k]) | 
					
						
							|  |  |  |             elif isinstance(d[k], (list, tuple)): | 
					
						
							|  |  |  |                 l = [] | 
					
						
							|  |  |  |                 for v in d[k]: | 
					
						
							|  |  |  |                     if isinstance(v, dict): | 
					
						
							|  |  |  |                         l.append(DictDotLookup(v)) | 
					
						
							|  |  |  |                     else: | 
					
						
							|  |  |  |                         l.append(v) | 
					
						
							|  |  |  |                 self.__dict__[k] = l | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 self.__dict__[k] = d[k] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __getitem__(self, name): | 
					
						
							|  |  |  |         if name in self.__dict__: | 
					
						
							|  |  |  |             return self.__dict__[name] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __iter__(self): | 
					
						
							|  |  |  |         return iter(self.__dict__.keys()) |