def ready(self): # structure from .backend import ZabbixBackend SupportedServices.register_backend(ZabbixBackend) # templates from nodeconductor.template import TemplateRegistry from nodeconductor_zabbix.template import HostProvisionTemplateForm TemplateRegistry.register(HostProvisionTemplateForm) from . import handlers for index, resource_model in enumerate( structure_models.Resource.get_all_models()): signals.post_save.connect( handlers.update_hosts_visible_name_on_scope_name_change, sender=resource_model, dispatch_uid= 'nodeconductor_zabbix.handlers.update_hosts_visible_name_on_scope_name_change_%s_%s' % (index, resource_model.__name__)) signals.pre_delete.connect( handlers.delete_hosts_on_scope_deletion, sender=resource_model, dispatch_uid= 'nodeconductor_zabbix.handlers.delete_hosts_on_scope_deletion_%s_%s' % (index, resource_model.__name__))
def ready(self): # structure from .backend import SugarCRMBackend SupportedServices.register_backend(SugarCRMBackend) # cost tracking from .cost_tracking import SugarCRMCostTrackingBackend CostTrackingRegister.register(self.label, SugarCRMCostTrackingBackend) # template from .template import CRMProvisionTemplateForm TemplateRegistry.register(CRMProvisionTemplateForm) from . import handlers CRM = self.get_model('CRM') signals.post_save.connect( handlers.update_user_limit_count_quota_on_crm_quota_change, sender=Quota, dispatch_uid='nodeconductor_sugarcrm.handlers.update_user_limit_count_quota_on_crm_quota_change', ) signals.pre_delete.connect( handlers.update_user_limit_count_quota_on_crm_deletion, sender=CRM, dispatch_uid='nodeconductor_sugarcrm.handlers.update_user_limit_count_quota_on_crm_deletion' )
def __new__(mcs, name, bases, args): service_filter = super(ServiceFilterMetaclass, mcs).__new__(mcs, name, bases, args) model = args['Meta'].model if not model._meta.abstract: SupportedServices.register_service_filter(args['Meta'].model, service_filter) return service_filter
def get_field_type(field): """ Returns field type/possible values. """ if isinstance(field, core_filters.MappedMultipleChoiceFilter): return ' | '.join(['"%s"' % f for f in sorted(field.mapped_to_model)]) if isinstance(field, OrderingFilter) or isinstance(field, ChoiceFilter): return ' | '.join(['"%s"' % f[0] for f in field.extra['choices']]) if isinstance(field, ChoiceField): return ' | '.join(['"%s"' % f for f in sorted(field.choices)]) if isinstance(field, HyperlinkedRelatedField): if field.view_name.endswith('detail'): return 'link to %s' % reverse(field.view_name, kwargs={ '%s' % field.lookup_field: "'%s'" % field.lookup_field }) return reverse(field.view_name) if isinstance(field, structure_filters.ServiceTypeFilter): return ' | '.join([ '"%s"' % f for f in SupportedServices.get_filter_mapping().keys() ]) if isinstance(field, ResourceTypeFilter): return ' | '.join([ '"%s"' % f for f in SupportedServices.get_resource_models().keys() ]) if isinstance(field, core_serializers.GenericRelatedField): links = [] for model in field.related_models: detail_view_name = core_utils.get_detail_view_name(model) for f in field.lookup_fields: try: link = reverse(detail_view_name, kwargs={'%s' % f: "'%s'" % f}) except NoReverseMatch: pass else: links.append(link) break path = ', '.join(links) if path: return 'link to any: %s' % path if isinstance(field, core_filters.ContentTypeFilter): return "string in form 'app_label'.'model_name'" if isinstance(field, ModelMultipleChoiceFilter): return get_field_type(field.field) if isinstance(field, ListSerializer): return 'list of [%s]' % get_field_type(field.child) if isinstance(field, ManyRelatedField): return 'list of [%s]' % get_field_type(field.child_relation) if isinstance(field, ModelField): return get_field_type(field.model_field) name = field.__class__.__name__ for w in ('Filter', 'Field', 'Serializer'): name = name.replace(w, '') return FIELDS.get(name, name)
def ready(self): OpenStackService = self.get_model('OpenStackService') OpenStackServiceProjectLink = self.get_model('OpenStackServiceProjectLink') Instance = self.get_model('Instance') FloatingIP = self.get_model('FloatingIP') # structure from nodeconductor.openstack.backend import OpenStackBackend SupportedServices.register_backend(OpenStackService, OpenStackBackend) # cost tracking from nodeconductor.openstack.cost_tracking import OpenStackCostTrackingBackend CostTrackingRegister.register(self.label, OpenStackCostTrackingBackend) # template from nodeconductor.template import TemplateRegistry from nodeconductor.openstack.template import InstanceProvisionTemplateForm TemplateRegistry.register(InstanceProvisionTemplateForm) signals.post_save.connect( handlers.create_initial_security_groups, sender=OpenStackServiceProjectLink, dispatch_uid='nodeconductor.openstack.handlers.create_initial_security_groups', ) signals.post_save.connect( quotas_handlers.add_quotas_to_scope, sender=OpenStackServiceProjectLink, dispatch_uid='nodeconductor.openstack.handlers.add_quotas_to_service_project_link', ) signals.pre_save.connect( handlers.set_spl_default_availability_zone, sender=OpenStackServiceProjectLink, dispatch_uid='nodeconductor.openstack.handlers.set_spl_default_availability_zone', ) signals.post_save.connect( handlers.increase_quotas_usage_on_instance_creation, sender=Instance, dispatch_uid='nodeconductor.openstack.handlers.increase_quotas_usage_on_instance_creation', ) signals.post_delete.connect( handlers.decrease_quotas_usage_on_instances_deletion, sender=Instance, dispatch_uid='nodeconductor.openstack.handlers.decrease_quotas_usage_on_instances_deletion', ) signals.post_save.connect( handlers.change_floating_ip_quota_on_status_change, sender=FloatingIP, dispatch_uid='nodeconductor.openstack.handlers.change_floating_ip_quota_on_status_change', )
def service_settings_description(): services = [] for cls in BaseServiceSerializer.__subclasses__(): if cls.Meta.model is NotImplemented: continue if not SupportedServices._is_active_model(cls.Meta.model): continue name = SupportedServices.get_name_for_model(cls.Meta.model) fields, extra_fields = get_fields(cls) services.append((name, { 'fields': fields, 'extra_fields': extra_fields })) return {'services': sorted(services)}
def ready(self): from .backend import SaltStackBackend from .models import SaltStackProperty import handlers SupportedServices.register_backend(SaltStackBackend) from nodeconductor.structure.models import ServiceSettings from nodeconductor.quotas.fields import QuotaField, CounterQuotaField from ..exchange.models import ExchangeTenant ServiceSettings.add_quota_field( name='sharepoint_storage', quota_field=QuotaField( creation_condition=lambda service_settings: service_settings. type == SaltStackConfig.service_name, ), ) ServiceSettings.add_quota_field( name='exchange_storage', quota_field=QuotaField( creation_condition=lambda service_settings: service_settings. type == SaltStackConfig.service_name, ), ) ServiceSettings.add_quota_field( name='exchange_tenant_count', quota_field=CounterQuotaField( creation_condition=lambda service_settings: service_settings. type == SaltStackConfig.service_name, target_models=[ExchangeTenant], path_to_scope='service_project_link.service.settings', )) for index, model in enumerate(SaltStackProperty.get_all_models()): signals.post_save.connect( handlers.log_saltstack_property_created, sender=model, dispatch_uid= 'nodeconductor_saltstack.saltstack.handlers.log_saltstack_property_created{}_{}' .format(model.__name__, index), ) signals.post_delete.connect( handlers.log_saltstack_property_deleted, sender=model, dispatch_uid= 'nodeconductor_saltstack.saltstack.handlers.log_saltstack_property_deleted{}_{}' .format(model.__name__, index), )
def count(self, request): """ Count resources by type. Example output: { "Amazon.Instance": 0, "GitLab.Project": 3, "Azure.VirtualMachine": 0, "IaaS.Instance": 10, "DigitalOcean.Droplet": 0, "OpenStack.Instance": 0, "GitLab.Group": 8 } """ types = request.query_params.getlist('resource_type', []) params = self.get_params(request) resources = SupportedServices.get_resources(request).items() result = {} for (type, url) in resources: if types != [] and type not in types: continue response = request_api(request, url, method='HEAD', params=params) if not response.success: raise APIException(response.data) result[type] = response.total return Response(result)
def get_urls(self, request): types = request.query_params.getlist('resource_type', []) resources = SupportedServices.get_resources(request).items() if types != []: return [url for (type, url) in resources if type in types] else: return [url for (type, url) in resources]
def begin_recovering_erred_service_settings(settings_uuid, transition_entity=None): settings = models.ServiceSettings.objects.get(uuid=settings_uuid) try: backend = settings.get_backend() is_active = backend.ping() except ServiceBackendNotImplemented: is_active = False if is_active: settings.set_in_sync() settings.error_message = '' settings.save() logger.info('Service settings %s successfully recovered.' % settings.name) try: spl_model = SupportedServices.get_service_models()[settings.type]['service_project_link'] erred_spls = spl_model.objects.filter(service__settings=settings, state=SynchronizationStates.ERRED) recover_erred_services.delay([spl.to_string() for spl in erred_spls]) except KeyError: logger.warning('Failed to recover service project links for settings %s', settings) else: settings.set_erred() settings.error_message = 'Failed to ping service settings %s' % settings.name settings.save() logger.info('Failed to recover service settings %s.' % settings.name)
def wait_for_provision(previous_task_data=None, template_uuid=None, token_key=None, template_group_result_uuid=None, success_state='Online', erred_state='Erred'): template_group_result = models.TemplateGroupResult.objects.get( uuid=template_group_result_uuid) template = models.Template.objects.get(uuid=template_uuid) url = previous_task_data['url'] resource_data = template.get_resource(url, token_key).json() resource_type = SupportedServices.get_name_for_model( template.resource_content_type.model_class()) state = resource_data['state'] if state == success_state: template_group_result.state_message = '%s has been successfully provisioned.' % resource_type template_group_result.provisioned_resources[resource_type] = url template_group_result.save() return resource_data elif state != erred_state: template_group_result.state_message = 'Waiting for %s provision (current state: %s). ' % ( resource_type, state) template_group_result.save() return False else: message = 'Failed to provision %s.' % resource_type details = 'Resource with URL %s come to state "%s".' % (url, state) raise models.TemplateActionException(message, details)
def _get_erred_resources_module(self): """ Returns a list of links to resources which are in ERRED state and linked to a shared service settings. """ result_module = modules.LinkList(title=_('Resources in erred state')) erred_state = structure_models.NewResource.States.ERRED children = [] resource_models = SupportedServices.get_resource_models() resources_in_erred_state_overall = 0 for resource_type, resource_model in resource_models.items(): queryset = resource_model.objects.filter( service_project_link__service__settings__shared=True) erred_amount = queryset.filter(state=erred_state).count() if erred_amount: resources_in_erred_state_overall = resources_in_erred_state_overall + erred_amount link = self._get_erred_resource_link(resource_model, erred_amount, erred_state) children.append(link) if resources_in_erred_state_overall: result_module.title = '%s (%s)' % ( result_module.title, resources_in_erred_state_overall) result_module.children = children else: result_module.pre_content = _('Nothing found.') return result_module
def clean(self): if not self.service: raise ValidationError(_('Service is not defined.')) if SupportedServices.is_public_service(self.service): raise ValidationError( _('Public service does not support price list items.')) resource = self.default_price_list_item.resource_content_type.model_class( ) valid_resources = SupportedServices.get_related_models( self.service)['resources'] if resource not in valid_resources: raise ValidationError( _('Service does not support required content type.'))
def debit_customers(): """ Fetch a list of shared services (services based on shared settings). Calculate the amount of consumed resources "yesterday" (make sure this task executed only once a day) Reduce customer's balance accordingly Stop online resource if needed """ date = datetime.now() - timedelta(days=1) start_date = date.replace(hour=0, minute=0, second=0, microsecond=0) end_date = start_date + timedelta(days=1, microseconds=-1) # XXX: it's just a placeholder, it doesn't work properly now nor implemented anyhow # perhaps it should merely use price estimates.. # TODO: remove once iaas has been deprecated from nodeconductor.iaas.models import Instance models = filter(lambda model: model != Instance, SupportedServices.get_resource_models().values()) for model in models: resources = model.objects.filter( service_project_link__service__settings__shared=True) for resource in resources: try: data = resource.get_cost(start_date, end_date) except NotImplementedError: continue else: resource.customer.debit_account(data['total_amount'])
def create(self, validated_data): # XXX: This behavior is wrong for services with several resources, find a better approach resource_class = SupportedServices.get_related_models( validated_data['service'])['resources'][0] validated_data[ 'resource_content_type'] = ContentType.objects.get_for_model( resource_class) return super(PriceListItemSerializer, self).create(validated_data)
def queryset(self, request, queryset): if self.value(): model = SupportedServices.get_resource_models().get( self.value(), None) if model: return queryset.filter(resource_content_type=ContentType. objects.get_for_model(model)) return queryset
def get_links(self): """ Get all service project links connected to current project """ return [ link for model in SupportedServices.get_service_models().values() for link in model['service_project_link'].objects.filter( project=self) ]
def filter(self, qs, value): if value in EMPTY_VALUES: return qs resource_models = SupportedServices.get_resource_models() try: model = resource_models[value] ct = ContentType.objects.get_for_model(model) return super(ResourceTypeFilter, self).filter(qs, ct) except (ContentType.DoesNotExist, KeyError): return qs.none()
def lookups(self, request, model_admin): resources = [ (model, name) for name, model in SupportedServices.get_resource_models().items() ] others = [(model, model.__name__) for model in models.PriceEstimate.get_estimated_models() if not issubclass(model, structure_models.ResourceMixin)] estimated_models = [(core_utils.serialize_class(model), name) for model, name in resources + others] return sorted(estimated_models, key=lambda x: x[1])
def _find_service(self): service_type = self.request.query_params.get('service_type') service_uuid = self.request.query_params.get('service_uuid') if not service_type or not service_uuid: return rows = SupportedServices.get_service_models() if service_type not in rows: return service_class = rows.get(service_type)['service'] try: return service_class.objects.get(uuid=service_uuid) except ObjectDoesNotExist: return None
def ready(self): from nodeconductor.quotas.fields import LimitAggregatorQuotaField from nodeconductor.structure import SupportedServices # structure from .backend import SugarCRMBackend SupportedServices.register_backend(SugarCRMBackend) from nodeconductor.structure.models import ServiceSettings from . import handlers, signals as sugarcrm_signals CRM = self.get_model('CRM') SugarCRMServiceProjectLink = self.get_model( 'SugarCRMServiceProjectLink') sugarcrm_signals.user_post_save.connect( handlers.log_user_post_save, sender=CRM, dispatch_uid='nodeconductor_sugarcrm.handlers.log_user_post_save') sugarcrm_signals.user_post_delete.connect( handlers.log_user_post_delete, sender=CRM, dispatch_uid='nodeconductor_sugarcrm.handlers.log_user_post_delete' ) ServiceSettings.add_quota_field( name='sugarcrm_user_count', quota_field=LimitAggregatorQuotaField( creation_condition=lambda service_settings: service_settings. type == SugarCRMConfig.service_name, get_children=lambda service_settings: SugarCRMServiceProjectLink.objects.filter(service__settings= service_settings), child_quota_name='user_limit_count', ), )
def connect_customer_to_shared_service_settings(sender, instance, created=False, **kwargs): if not created: return customer = instance for shared_settings in ServiceSettings.objects.filter( shared=True, state=SynchronizationStates.IN_SYNC): service_model = SupportedServices.get_service_models()[ shared_settings.type]['service'] service_model.objects.create(customer=customer, settings=shared_settings, name=shared_settings.name, available_for_all=True)
def get_backend(self, service): # project_uuid can be supplied in order to get a list of resources # available for import (link) based on project, depends on backend implementation project_uuid = self.request.query_params.get('project_uuid') if project_uuid: spl_class = SupportedServices.get_related_models( service)['service_project_link'] try: spl = spl_class.objects.get(project__uuid=project_uuid, service=service) except: raise NotFound("Can't find project %s" % project_uuid) else: return spl.get_backend() else: return service.get_backend()
def create_service_project_link(self, customer, project): service_type, models = SupportedServices.get_service_models().items( )[0] class ServiceFactory(factory.DjangoModelFactory): class Meta(object): model = models['service'] class ServiceProjectLinkFactory(factory.DjangoModelFactory): class Meta(object): model = models['service_project_link'] settings = structure_factories.ServiceSettingsFactory( customer=customer, type=service_type, shared=False) service = ServiceFactory(customer=customer, settings=settings) return ServiceProjectLinkFactory(service=service, project=project)
def _get_log_context(self, entity_name): context = super(ResourceMixin, self)._get_log_context(entity_name) # XXX: Add resource_full_name here, because event context does not support properties as fields context['resource_full_name'] = self.full_name # required for lookups in ElasticSearch by the client context['resource_type'] = SupportedServices.get_name_for_model(self) # XXX: a hack for IaaS / PaaS / SaaS tags # XXX: should be moved to itacloud assembly if self.tags.filter(name='IaaS').exists(): context['resource_delivery_model'] = 'IaaS' elif self.tags.filter(name='PaaS').exists(): context['resource_delivery_model'] = 'PaaS' elif self.tags.filter(name='SaaS').exists(): context['resource_delivery_model'] = 'SaaS' return context
def init_resource_count_quota(self): self.stdout.write('Drop current nc_resource_count quotas values ...') customer_ct = ContentType.objects.get_for_model(models.Customer) project_ct = ContentType.objects.get_for_model(models.Project) quotas_models.Quota.objects.filter( name='nc_resource_count', content_type__in=[project_ct, customer_ct]).update(usage=0) self.stdout.write('... Done') self.stdout.write( 'Calculating new nc_resource_count quotas values ...') resource_models = SupportedServices.get_resource_models().values() for model in resource_models: for resource in model.objects.all(): resource.service_project_link.project.add_quota_usage( 'nc_resource_count', 1) self.stdout.write('... Done')
def connect_customer_to_shared_service_settings(sender, instance, created=False, **kwargs): if not created: return customer = instance for shared_settings in ServiceSettings.objects.filter(shared=True): try: service_model = SupportedServices.get_service_models()[ shared_settings.type]['service'] service_model.objects.create(customer=customer, settings=shared_settings, available_for_all=True) except KeyError: logger.warning("Unregistered service of type %s" % shared_settings.type)
def provision_service(self, host): resource_type = SupportedServices.get_name_for_model(host.scope) description = self.service_triggers.get(resource_type) if not description: logger.warning( 'Zabbix IT service is not created because trigger ' 'description for resource with type %s is missing', resource_type) return trigger_id = self._get_trigger_id(host.backend_id, description) service_name = self._get_service_name(host.scope.backend_id) service_id, created = self.get_or_create_service( service_name, host.agreed_sla, trigger_id) host.service_id = service_id host.trigger_id = trigger_id host.save(update_fields=['service_id', 'trigger_id'])
def connect_shared_service_settings_to_customers(sender, instance, name, source, target, **kwargs): """ Connected service settings with all customers if they were created or become shared """ service_settings = instance if (target != SynchronizationStates.IN_SYNC or source not in (SynchronizationStates.ERRED, SynchronizationStates.CREATING) or not service_settings.shared): return service_model = SupportedServices.get_service_models()[ service_settings.type]['service'] for customer in Customer.objects.all(): if not service_model.objects.filter( customer=customer, settings=service_settings).exists(): service_model.objects.create(customer=customer, settings=service_settings, name=service_settings.name, available_for_all=True)
def schedule_provision(previous_task_data=None, url=None, template_uuid=None, token_key=None, additional_options=None, template_group_result_uuid=None): template = models.Template.objects.get(uuid=template_uuid) response_data = template.schedule_provision(url, token_key, additional_options, previous_task_data).json() # update templates group result if it is defined if template_group_result_uuid is not None: template_group_result = models.TemplateGroupResult.objects.get( uuid=template_group_result_uuid) resource_type = SupportedServices.get_name_for_model( template.resource_content_type.model_class()) template_group_result.state_message = '%s provision has been scheduled successfully.' % resource_type template_group_result.save() return response_data