def form_valid(self, form): try: self.object = form.save(commit=False) self.object.service = self.service self.object.save() except Exception: form.add_error(None, "A Unix Group already exists with that name") return self.form_invalid(form) unix_users = list( set(validate_crsid_list(self.request.POST.getlist('unix_users')))) if not all( user in self.object.service.site.list_of_all_type_of_users() for user in unix_users): form.add_error( None, "You have added users to this group that are not in the authorisation user list." ) return self.form_invalid(form) self.object.users.add(*unix_users) launch_ansible(self.service) # to apply these changes to the vm return super(UnixGroupCreate, self).form_valid(form)
def accept_it(self): self.status = 'accepted' self.save() if self.vhost.main_domain is None or \ self.vhost.main_domain.name == self.vhost.service.network_configuration.name: self.vhost.main_domain = self self.vhost.save() from apimws.ipreg import set_cname try: set_cname(self.name, self.vhost.service.network_configuration.name) except DomainNameDelegatedException: return self.reject_it("Domain delegated") from apimws.ansible import launch_ansible launch_ansible(self.vhost.service) now = datetime.now() # Check if the set_cname was executed before the DNS refresh of the current hour. # DNS refreshes happen at 53 minutes of each hour if now.minute > 55: # If it was executed after the DNS refresh of the current hour, send the email to the user when # the next refresh happens eta = now.replace(minute=54) + timedelta(hours=1) else: # If it was executed before the DNS refresh of the current hour, send the email to the user when # the refresh happens eta = now.replace(minute=54) from apimws.utils import domain_confirmation_user domain_confirmation_user.apply_async(args=[ self, ], eta=eta)
def quarantine(request, service_id): service = get_object_or_404(Service, pk=service_id) site = privileges_check(service.site.id, request.user) if site is None: return HttpResponseForbidden() if not service or not service.active or service.is_busy: return redirect(site) breadcrumbs = { 0: dict(name='Managed Web Service server: ' + str(site.name), url=site.get_absolute_url()), 1: dict(name='Server settings' if service.primary else 'Test server settings', url=reverse(service_settings, kwargs={'service_id': service.id})), 2: dict(name='Quarantine', url=reverse(quarantine, kwargs={'service_id': service.id})), } parameters = { 'breadcrumbs': breadcrumbs, 'service': service, 'site': site, 'sidebar_messages': warning_messages(site), } if request.method == 'POST' and not site.is_admin_suspended(): if request.POST['quarantine'] == "Quarantine": service.quarantined = True else: service.quarantined = False service.save() launch_ansible(service) return redirect(site) return render(request, 'mws/quarantine.html', parameters)
def expire_domains(): ''' Periodically send messages to domain administrators about deleted domains and delete them after grace_days ''' grace_days = settings.MWS_DOMAIN_NAME_GRACE_DAYS notify_days = list(range(1, grace_days, grace_days // 3 + 1)) support_email = settings.EMAIL_MWS3_SUPPORT for domainname in DomainName.objects.filter(status='deleted').annotate( expired=(now() - F('updated_at'))): vhost = domainname.vhost service = domainname.vhost.service site = domainname.vhost.service.site if domainname.expired.days >= grace_days: EmailMessage( subject="MWS hostname %s deleted " % (domainname.name), body="This message is to inform you that the hostname\n%s\n" "associated with the %s website on the %s MWS server " "has failed validation for %d days, therefore it has been removed from the website.\n" "If you did not want this to happen you will need to make sure the hostname exists " "before contacting us at %s" % (domainname.name, vhost.name, site.name, domainname.expired.days, support_email), from_email="Managed Web Service Support <%s>" % getattr(settings, 'EMAIL_MWS3_SUPPORT', '*****@*****.**'), to=[site.email], headers={ 'Return-Path': getattr(settings, 'EMAIL_MWS3_SUPPORT', '*****@*****.**') }).send() LOGGER.warning('deleting DomainName {}'.format(domainname.name)) domainname.delete() launch_ansible(service) elif domainname.expired.days in notify_days: on = domainname.updated_at + timedelta(days=grace_days) LOGGER.info('sending warning email for {} to {}'.format( domainname.name, site.email)) EmailMessage( subject="MWS hostname %s scheduled for deletion " % (domainname.name), body="This message is to inform you that the hostname\n%s\n" "associated with the %s website on the %s MWS server " "has failed validation and is now scheduled for removal on %s.\n" "If you do not want this to happen you will need to ensure the hostname exists " "and is any one of\n - a CNAME pointing to the host %s;\n - an A record with address %s;\n" " - an AAAA record with address %s.\n" % (domainname.name, vhost.name, site.name, on.date().isoformat(), service.hostname, service.ipv4, service.ipv6), from_email="Managed Web Service Support <%s>" % getattr(settings, 'EMAIL_MWS3_SUPPORT', '*****@*****.**'), to=[site.email], headers={ 'Return-Path': getattr(settings, 'EMAIL_MWS3_SUPPORT', '*****@*****.**') }).send()
def delete(self, request, *args, **kwargs): if self.domain.name != self.domain.vhost.service.network_configuration.name: if self.domain != self.domain.vhost.main_domain or self.domain.vhost.domain_names.count( ) == 1: self.domain.delete() launch_ansible(self.service) return HttpResponse() return HttpResponseForbidden()
def form_valid(self, form): try: self.object = form.save(commit=False) self.object.service = self.service self.object.save() launch_ansible( self.service) # to create a new vhost configuration file return HttpResponseRedirect(self.get_success_url()) except IntegrityError: messages.error(self.request, 'This website name already exists') return redirect(self.get_success_url())
def php_libs(request, service_id): service = get_object_or_404(Service, pk=service_id) site = privileges_check(service.site.id, request.user) if site is None: return HttpResponseForbidden() if not service or not service.active or service.is_busy: return redirect(site) breadcrumbs = { 0: dict(name='Managed Web Service server: ' + str(site.name), url=site.get_absolute_url()), 1: dict(name='Server settings' if service.primary else 'Test server settings', url=reverse(service_settings, kwargs={'service_id': service.id})), 2: dict(name='PHP Libraries', url=reverse(php_libs, kwargs={'service_id': service.id})), } from apimws.forms import PHPLibForm parameters = { 'breadcrumbs': breadcrumbs, 'service': service, 'site': site, 'sidebar_messages': warning_messages(site), 'form': PHPLibForm(initial={ 'php_libs': list(service.php_libs.values_list('name', flat=True)) }), } if request.method == 'POST': f = PHPLibForm(request.POST) if f.is_valid(): service.php_libs.set(PHPLib.objects.filter( name__in=f.cleaned_data['php_libs']).all(), clear=True) service.save() launch_ansible(service) return render(request, 'mws/phplibs.html', parameters)
def delete(self, request, *args, **kwargs): vhost_name = self.vhost.name webapp = self.vhost.webapp service = self.vhost.service if vhost_name != "default": delete_vhost_ansible.delay(service, vhost_name, webapp) super(VhostDelete, self).delete(request, *args, **kwargs) launch_ansible(service) return HttpResponse( "The website/vhost '%s' has been deleted successfully" % vhost_name) else: return HttpResponseForbidden( "The default website/vhost cannot be deleted")
def generate_csr(request, vhost_id): vhost = get_object_or_404(Vhost, pk=vhost_id) site = privileges_check(vhost.service.site.id, request.user) if request.method == 'POST' and vhost.tls_key_hash != 'requested' and vhost.tls_key_hash != 'renewal': if vhost.main_domain is None: breadcrumbs = { 0: dict(name='Managed Web Service server: ' + str(site.name), url=site.get_absolute_url()), 1: dict(name='Server settings' if vhost.service.primary else 'Test server settings', url=reverse('sitesmanagement.views.service_settings', kwargs={'service_id': vhost.service.id})), 2: dict(name='Vhosts Management: %s' % vhost.name, url=reverse('listvhost', kwargs={'service_id': vhost.service.id})), 3: dict(name='TLS/SSL Certificates', url=reverse(certificates, kwargs={'vhost_id': vhost.id})), } return render( request, 'mws/certificates.html', { 'breadcrumbs': breadcrumbs, 'vhost': vhost, 'service': vhost.service, 'site': site, 'error_main_domain': True }) if vhost.tls_enabled: vhost.tls_key_hash = 'renewal' vhost.csr = None vhost.save() else: vhost.tls_key_hash = 'requested' vhost.certificate = None vhost.csr = None vhost.tls_enabled = False vhost.save() launch_ansible(vhost.service) return redirect(reverse(certificates, kwargs={'vhost_id': vhost.id}))
def switch_services(self): prod_service = self.production_service test_service = self.test_service netconf_prod = prod_service.network_configuration netconf_test = test_service.network_configuration vhost_prod = prod_service.vhosts.all() ug_prod = prod_service.unix_groups.all() with transaction.atomic(): # Switch vhosts, test service has no vhosts for vhost in vhost_prod: vhost.service = test_service vhost.save() # Switch unix groups, test service has no UG for ug in ug_prod: ug.service = test_service ug.save() # Switch network configuration test_service.network_configuration = NetworkConfig.get_free_test_service_config( ) test_service.type = "production" test_service.site = None test_service.save() prod_service.network_configuration = netconf_test prod_service.type = "test" prod_service.save() test_service.site = self test_service.network_configuration = netconf_prod test_service.save() from apimws.models import AnsibleConfiguration AnsibleConfiguration.objects.update_or_create( service=test_service, key="backup_first_date", value=date.today().isoformat()) from apimws.ansible import launch_ansible launch_ansible(prod_service) launch_ansible(test_service) return True
def form_valid(self, form): self.object = form.save() unix_users = list( set(validate_crsid_list(self.request.POST.getlist('unix_users')))) if not all( user in self.object.service.site.list_of_all_type_of_users() for user in unix_users): form.add_error( None, "You have added users to this group that are not in the authorisation user list." ) return self.form_invalid(form) self.object.users.clear() self.object.users.add(*unix_users) launch_ansible(self.service) # to apply these changes to the vm return super(UnixGroupUpdate, self).form_valid(form)
def set_dn_as_main(request, domain_id): """View(Controller) to set the domain name selected as the main domain of the vhost""" domain = get_object_or_404(DomainName, pk=domain_id) vhost = domain.vhost site = privileges_check(vhost.service.site.id, request.user) service = vhost.service if site is None: return HttpResponseForbidden() if not service or not service.active or service.is_busy: return redirect(site) if request.method == 'POST': vhost.main_domain = domain vhost.save() launch_ansible( vhost.service ) # to update the vhost main domain name in the apache configuration return HttpResponseRedirect( reverse('listdomains', kwargs={'vhost_id': vhost.id}))
def certificates(request, vhost_id): vhost = get_object_or_404(Vhost, pk=vhost_id) site = privileges_check(vhost.service.site.id, request.user) service = vhost.service if site is None: return HttpResponseForbidden() if not service or not service.active or service.is_busy: return redirect(site) if not vhost.domain_names.all(): return redirect( reverse('listvhost', kwargs={'service_id': vhost.service.id})) breadcrumbs = { 0: dict(name='Managed Web Service server: ' + str(site.name), url=site.get_absolute_url()), 1: dict(name='Server settings' if vhost.service.primary else 'Test server settings', url=reverse('sitesmanagement.views.service_settings', kwargs={'service_id': vhost.service.id})), 2: dict(name='Web sites management: %s' % vhost.name, url=reverse('listvhost', kwargs={'service_id': vhost.service.id})), 3: dict(name='TLS/SSL Certificate', url=reverse(certificates, kwargs={'vhost_id': vhost.id})), } error_message = None if request.method == 'POST': try: if 'cert' in request.FILES: try: certificates_str = request.FILES['cert'].file.read() cert = crypto.load_certificate(crypto.FILETYPE_PEM, certificates_str) except Exception as e: raise ValidationError("The certificate file is invalid") if vhost.csr is None: raise ValidationError("CSR does not exists") if not csr_match_crt(vhost.csr, certificates_str): raise ValidationError( "The certificate doesn't match the CSR") issuer = None for component in cert.get_issuer().get_components(): if component[0] == 'CN': issuer = component[1] if not issuer: raise ValidationError( "The certificate doesn't have any issuer") if issuer not in CERTIFICATE_CHAINS: raise ValidationError("Certificate issuer unknown by MWS") vhost.certificate = certificates_str vhost.certificate_chain = CERTIFICATE_CHAINS[issuer] vhost.tls_enabled = True if vhost.tls_key_hash == 'renewal_waiting_cert': vhost.tls_key_hash = 'renewal_cert' vhost.save() launch_ansible(service) else: raise ValidationError("No Certificate uploaded") except ValidationError as e: error_message = e.message return render( request, 'mws/certificates.html', { 'breadcrumbs': breadcrumbs, 'vhost': vhost, 'service': vhost.service, 'site': site, 'sidebar_messages': warning_messages(site), 'error_message': error_message })
def delete(self, request, *args, **kwargs): self.object = self.get_object() self.object.to_be_deleted = True self.object.save() launch_ansible(self.service) return HttpResponse()
def execute_ansible(modeladmin, request, queryset): from apimws.ansible import launch_ansible for service in queryset: launch_ansible(service)
def clone_vm_api_call(site): service = site.test_service host_network_configuration = NetworkConfig.get_free_host_config() parameters = {} parameters["site-id"] = "mwssite-%d" % service.site.id parameters["os"] = getattr(settings, 'OS_VERSION', "stretch") if host_network_configuration: netconf = {} if host_network_configuration.IPv4: netconf["IPv4"] = host_network_configuration.IPv4 if host_network_configuration.IPv6: netconf["IPv6"] = host_network_configuration.IPv6 if host_network_configuration.name: netconf["hostname"] = host_network_configuration.name vm = VirtualMachine.objects.create( service=service, token=uuid.uuid4(), network_configuration=host_network_configuration, cluster=which_cluster()) else: raise AttributeError("No host network configuration") parameters["netconf"] = netconf parameters["callback"] = { "endpoint": "%s%s" % (settings.MAIN_DOMAIN, reverse(post_installation)), "vm_id": vm.id, "secret": str(vm.token), } parameters["features"] = { "cpu": service.site.type.numcpu, "ram": service.site.type.sizeram * 1024, "disk": service.site.type.sizedisk, } service.status = 'installing' service.save() response = vm_api_request(command='create', parameters=parameters, vm=vm) try: jresponse = json.loads(response) except ValueError as e: LOGGER.error("VM API response is not properly formated: %s", response) vm.name = vm.network_configuration.name vm.save() raise e try: if 'vmid' in jresponse: vm.name = jresponse['vmid'] else: vm.name = vm.network_configuration.name except Exception as e: vm.name = vm.network_configuration.name vm.save() from apimws.models import AnsibleConfiguration AnsibleConfiguration.objects.update_or_create( service=service, key='os', defaults={'value': getattr(settings, "OS_VERSION", "stretch")}) secrets_prealocation_vm(vm) # PHPLibs for phplib in site.production_service.php_libs.all(): phplib.services.add(site.test_service) # Call Ansible to update the state of the machine launch_ansible(site.production_service) launch_ansible(site.test_service) return True
def add_domain(request, vhost_id, socket_error=None): """View(Controller) to add a domain name to the vhost selected. """ vhost = get_object_or_404(Vhost, pk=vhost_id) site = privileges_check(vhost.service.site.id, request.user) service = vhost.service if site is None: return HttpResponseForbidden() if not service or not service.active or service.is_busy: return redirect(site) if request.method == 'POST': domain_form = DomainNameFormNew(request.POST) if domain_form.is_valid(): domain_requested = domain_form.save(commit=False) if domain_requested.name != '': if is_camacuk(domain_requested.name): if domain_requested.name.endswith( ".usertest.mws3.csx.cam.ac.uk"): new_domain = DomainName.objects.create( name=domain_requested.name, status='accepted', vhost=vhost, requested_by=request.user) new_domain.accept_it() elif domain_requested.name.endswith(".mws3.csx.cam.ac.uk"): new_domain = DomainName.objects.create( name=domain_requested.name, status='denied', vhost=vhost, requested_by=request.user) elif 'special_case' in domain_form.data: new_domain = DomainName.objects.create( name=domain_requested.name, status='special', vhost=vhost, requested_by=request.user, reject_reason="User marked as special") else: new_domain = DomainName.objects.create( name=domain_requested.name, status='requested', vhost=vhost, requested_by=request.user) ip_register_api_request.delay(new_domain) else: new_domain = DomainName.objects.create( name=domain_requested.name, status='external', vhost=vhost, requested_by=request.user) if vhost.main_domain is None or \ vhost.main_domain.name == vhost.service.network_configuration.name: vhost.main_domain = new_domain vhost.save() launch_ansible( vhost.service ) # to add the new domain name to the vhost apache configuration else: breadcrumbs = { 0: dict(name='Managed Web Service server: ' + str(site.name), url=site.get_absolute_url()), 1: dict(name='Server settings' if vhost.service.primary else 'Test server settings', url=reverse('sitesmanagement.views.service_settings', kwargs={'service_id': vhost.service.id})), 2: dict(name='Web sites management: %s' % vhost.name, url=reverse('listvhost', kwargs={'service_id': vhost.service.id})), 3: dict(name='Domain Names management', url=reverse('listdomains', kwargs={'vhost_id': vhost.id})) } return render( request, 'mws/domains.html', { 'breadcrumbs': breadcrumbs, 'vhost': vhost, 'site': site, 'service': vhost.service, 'domain_form': domain_form, 'error': True }) return redirect(reverse('listdomains', kwargs={'vhost_id': vhost.id}))