def _add_or_update_dns_records(self, task_id, ip_address): hostname = self.node.hostname admin_net = ip_address.subnet record_cls = RecordView.Record if admin_net.dns_domain: logger.info('Adding or updating forward A DNS record for node %s', hostname) RecordView.add_or_update_record(self.request, record_cls.A, admin_net.dns_domain, hostname, task_id=task_id, related_obj=self.node) if admin_net.ptr_domain: logger.info( 'Adding or updating reverse PTR DNS record for node %s', hostname) RecordView.add_or_update_record(self.request, record_cls.PTR, admin_net.ptr_domain, record_cls.get_reverse( ip_address.ip), task_id=task_id, related_obj=self.node)
def _delete_dns_records(request, task_id, node, hostname, ip_address): if ip_address: admin_net = ip_address.subnet record_cls = RecordView.Record if admin_net.dns_domain: logger.info('Deleting forward A DNS record for node %s', hostname) RecordView.delete_record(request, record_cls.A, admin_net.dns_domain, hostname, task_id=task_id, related_obj=node) if admin_net.ptr_domain: logger.info('Deleting reverse PTR DNS record for node %s', hostname) RecordView.delete_record(request, record_cls.PTR, admin_net.ptr_domain, record_cls.get_reverse(ip_address.ip), task_id=task_id, related_obj=node)
def ptr_form(request, hostname, nic_id): """ Ajax page for PTR form validation. """ vm = get_vm(request, hostname) try: nic_ip = vm.json_get_nics()[int(nic_id) - 1]['ip'] ptr = RecordView.Record.get_record_PTR(nic_ip) if not ptr: raise Exception('PTR Record not found') except Exception: raise Http404 ptrform = PTRForm(request.POST, prefix='ptr') if ptrform.is_valid(): if ptrform.cleaned_data['content'] == ptr.content: return HttpResponse(None, status=204) else: res = RecordView.internal_response(request, 'PUT', ptr, ptrform.cleaned_data, related_obj=vm) if res.status_code in (200, 201): return HttpResponse(None, status=201) else: ptrform.set_api_errors(res.data) return render(request, 'gui/vm/ptr_form.html', {'ptrform': ptrform})
def _save_node_ip_address(task_id, node): """Helper function for saving IP address and creating DNS records of a new compute node""" assert node.address try: ip_address = node.create_ip_address() except IPAddress.DoesNotExist as exc: logger.warning( 'Could not save node %s IP address "%s" into admin network (%s)', node, node.address, exc) return logger.info('Saving node %s IP address "%s" into admin network', node, node.ip_address) ip_address.save() admin_net = node.ip_address.subnet # The network was updated by init_mgmt() # Reload Subnet object because it is cached inside node instance admin_net = admin_net.__class__.objects.get(pk=admin_net.pk) # We need a request object request = get_dummy_request(DefaultDc(), 'POST', system_user=True) record_cls = RecordView.Record if admin_net.dns_domain and admin_net.dns_domain == node.domain_name: logger.info('Creating forward A DNS record for node %s', node) # This will fail silently RecordView.add_or_update_record(request, record_cls.A, admin_net.dns_domain, node.hostname, node.address, task_id=task_id, related_obj=node) if admin_net.ptr_domain: logger.info('Creating reverse PTR DNS record for node %s', node) # This will fail silently RecordView.add_or_update_record(request, record_cls.PTR, admin_net.ptr_domain, record_cls.get_reverse(node.address), node.hostname, task_id=task_id, related_obj=node)
def dns_record_list(request, name, data=None): """ List (:http:get:`GET </dns/domain/(name)/record>`) all DNS records which belong to a DNS domain. Delete (:http:delete:`DELETE </dns/domain/(name)/record>`) specified DNS records that belong to a DNS domain. .. http:get:: /dns/domain/(name)/record :DC-bound?: * |dc-yes| - ``domain.dc_bound=true`` * |dc-no| - ``domain.dc_bound=false`` :Permissions: * |DnsAdmin| or |DomainOwner| :Asynchronous?: * |async-no| :arg name: **required** - Domain name :type name: string :arg data.full: Return list of objects with all record details (default: false) :type data.full: boolean :arg data.order_by: :ref:`Available fields for sorting <order_by>`: ``id``, ``name``, ``type``, ``ttl``, \ ``disabled``, ``changed`` (default: ``id``) :type data.order_by: string :status 200: SUCCESS :status 403: Forbidden .. http:delete:: /dns/domain/(name)/record :DC-bound?: * |dc-yes| - ``domain.dc_bound=true`` * |dc-no| - ``domain.dc_bound=false`` :Permissions: * |DnsAdmin| or |DomainOwner| :Asynchronous?: * |async-no| :arg name: **required** - Domain name :type name: string :arg data.records: **required** List of DNS record ids to be deleted :type data.records: array :status 200: SUCCESS :status 400: FAILURE :status 403: Forbidden :status 404: Domain not found / Record not found :status 412: Invalid records """ if request.method == 'POST': return dns_record(request, name, 0, data=data) return RecordView(request, name, None, data).response(many=True)
def dns_record_list(request, name, data=None): """ List (:http:get:`GET </dns/domain/(name)/record>`) all DNS records which belong to a DNS domain. .. http:get:: /dns/domain/(name)/record :DC-bound?: * |dc-yes| - ``domain.dc_bound=true`` * |dc-no| - ``domain.dc_bound=false`` :Permissions: * |DnsAdmin| or |DomainOwner| :Asynchronous?: * |async-no| :arg data.full: Return list of objects with all record details (default: false) :type data.full: boolean :arg data.order_by: :ref:`Available fields for sorting <order_by>`: ``id``, ``name``, ``type``, ``ttl``, \ ``disabled``, ``changed`` (default: ``id``) :type data.order_by: string :status 200: SUCCESS :status 403: Forbidden """ if request.method == 'POST': return dns_record(request, name, 0, data=data) return RecordView(request, name, None, data).response(many=True)
def node_sysinfo_cb(result, task_id, node_uuid=None): """ A callback function for updating Node.json (sysinfo). node_uuid will be set only if called via API or GUI """ # in case the callback is called by restarting erigonesd:fast service on compute node, the meta dict lacks # a lot of information; msg is required as part of exception logging inside callback decorator # therefore we set it explicitly result['meta']['msg'] = LOG_NODE_UPDATE if result['returncode'] != 0: logger.error( 'Found nonzero return code in result from esysinfo command on %s', node_uuid) raise TaskException(result, 'Got bad return code (%s)' % result['returncode']) stdout = result.pop('stdout', '') result.pop('stderr', None) node_new = False try: esysinfo = parse_esysinfo(stdout) img_sources = esysinfo.pop('img_sources') img_initial = esysinfo.pop('img_initial') except Exception as e: logger.error( 'Could not parse output from esysinfo command on %s. Error: %s', node_uuid, e) logger.exception(e) raise TaskException(result, 'Could not parse esysinfo output') else: uuid = esysinfo['sysinfo']['UUID'] try: node = Node.objects.get(uuid=uuid) except Node.DoesNotExist: # The head node must be in online state during the admin DC initialization and each compute node must be in # online state during ssh key exchange. node_new = True is_head = not Node.objects.exists() logger.warn('Creating NEW node from sysinfo output from %s', node_uuid) node = Node.create_from_sysinfo(uuid, esysinfo, status=Node.ONLINE, is_head=is_head) node_created.send(task_id, node=node) # Signal! result[ 'message'] = 'Successfully created new compute node %s' % node.hostname task_log_success(task_id, msg=LOG_NODE_CREATE, obj=node, task_result=result, update_user_tasks=True) sshkey_changed = bool(node.sshkey) if node.is_head: logger.warn( 'New node %s is the first node ever created - assuming head node status. ' 'Initializing mgmt system and creating admin DC', node) from api.system.init import init_mgmt try: init_mgmt(node, images=img_initial) except Exception as e: logger.exception(e) result[ 'message'] = 'Error while initializing admin datacenter (%s)' % e task_log_error(task_id, msg=LOG_NODE_CREATE, obj=node, task_result=result, update_user_tasks=True) logger.info('Saving node %s IP address "%s" into admin network', node, node.ip_address) try: # We should proceed even if the IP address is not registered node.ip_address.save() except Exception as e: logger.exception(e) else: admin_net = node.ip_address.subnet # The network was updated by init_mgmt() # Reload Subnet object because it is cached inside node instance admin_net = admin_net.__class__.objects.get(pk=admin_net.pk) # We need a request object request = get_dummy_request(DefaultDc(), 'POST', system_user=True) record_cls = RecordView.Record if admin_net.dns_domain and admin_net.dns_domain == node.domain_name: logger.info('Creating forward A DNS record for node %s', node) # This will fail silently RecordView.add_or_update_record(request, record_cls.A, admin_net.dns_domain, node.hostname, node.address, task_id=task_id, related_obj=node) if admin_net.ptr_domain: logger.info('Creating reverse PTR DNS record for node %s', node) # This will fail silently RecordView.add_or_update_record(request, record_cls.PTR, admin_net.ptr_domain, record_cls.get_reverse( node.address), node.hostname, task_id=task_id, related_obj=node) else: sshkey_changed = node.sshkey_changed(esysinfo) if node.sysinfo_changed(esysinfo) or sshkey_changed: logger.warn('Updating node %s json with sysinfo output from %s', node, node_uuid) node.update_from_sysinfo(esysinfo) # Will save public SSH key too node_json_changed.send(task_id, node=node) # Signal! result[ 'message'] = 'Successfully updated compute node %s' % node.hostname task_log_success(task_id, msg=LOG_NODE_UPDATE, obj=node, task_result=result, update_user_tasks=True) else: result[ 'message'] = 'No changes detected on compute node %s' % node.hostname task_log_success(task_id, msg=LOG_NODE_UPDATE, obj=node, task_result=result, update_user_tasks=True) if sshkey_changed: logger.warn( 'SSH key has changed on node %s - creating authorized_keys synchronization tasks', node) try: run_node_authorized_keys_sync() except Exception as e: logger.exception(e) try: run_node_img_sources_sync(node, node_img_sources=img_sources) except Exception as e: logger.exception(e) if node_new: node.del_initializing() # Used by esdc-ee to change node status to unlicensed node_status = getattr(settings, 'VMS_NODE_STATUS_DEFAULT', None) if node_status: node.save_status( node_status) # Set node status (most probably to unlicensed) else: # Always run vm_status_all for an old compute node vm_status_all(task_id, node) # Sync snapshots and backup for every node storage try: NodeVmSnapshotList.sync(node) except Exception as e: logger.exception(e) return result
def dns_record(request, name, record_id, data=None): """ Show (:http:get:`GET </dns/domain/(name)/record/(record_id)>`), create (:http:post:`POST </dns/domain/(name)/record>`, update (:http:put:`PUT </dns/domain/(name)/record/(record_id)>`) or delete (:http:delete:`DELETE </dns/domain/(name)/record/(record_id)>`) a DNS record. .. http:get:: /dns/domain/(name)/record/(record_id) :DC-bound?: * |dc-yes| - ``domain.dc_bound=true`` * |dc-no| - ``domain.dc_bound=false`` :Permissions: * |DnsAdmin| or |DomainOwner| :Asynchronous?: * |async-no| :arg name: **required** - Domain name :type name: string :arg record_id: **required** - DNS record ID :type record_id: integer :status 200: SUCCESS :status 403: Forbidden :status 404: Domain not found / Record not found .. http:post:: /dns/domain/(name)/record :DC-bound?: * |dc-yes| - ``domain.dc_bound=true`` * |dc-no| - ``domain.dc_bound=false`` :Permissions: * |DnsAdmin| or |DomainOwner| :Asynchronous?: * |async-no| :arg name: **required** - Domain name :type name: string :arg data.name: **required** - The name of the DNS record - the full URI the DNS server should pick up on :type data.name: string :arg data.type: **required** - DNS record type (one of: A, AAAA, CERT, CNAME, HINFO, KEY, LOC, MX, NAPTR, \ NS, PTR, RP, SOA, SPF, SSHFP, SRV, TLSA, TXT) :type data.type: string :arg data.content: **required** - DNS record content - the answer of the DNS query :type data.content: string :arg data.ttl: How long (seconds) the DNS client is allowed to remember this record (default: 3600) :type data.ttl: integer :arg data.prio: Priority used by some record types (default: 0) :type data.prio: integer :arg data.disabled: If set to true, this record is hidden from DNS clients (default: false) :type data.disabled: boolean :status 201: SUCCESS :status 400: FAILURE :status 403: Forbidden :status 404: Domain not found :status 406: Record already exists .. http:put:: /dns/domain/(name)/record/(record_id) :DC-bound?: * |dc-yes| - ``domain.dc_bound=true`` * |dc-no| - ``domain.dc_bound=false`` :Permissions: * |DnsAdmin| or |DomainOwner| :Asynchronous?: * |async-no| :arg name: **required** - Domain name :type name: string :arg record_id: **required** - DNS record ID :type record_id: integer :arg data.name: The name of the DNS record - the full URI the DNS server should pick up on :type data.name: string :arg data.type: DNS record type (one of: A, AAAA, CERT, CNAME, HINFO, KEY, LOC, MX, NAPTR, NS, PTR, RP, \ SOA, SPF, SSHFP, SRV, TLSA, TXT) :type data.type: string :arg data.content: DNS record content - the answer of the DNS query :type data.content: string :arg data.ttl: How long (seconds) the DNS client is allowed to remember this record :type data.ttl: integer :arg data.prio: Priority used by some record types :type data.prio: integer :arg data.disabled: If set to true, this record is hidden from DNS clients :type data.disabled: boolean :status 200: SUCCESS :status 400: FAILURE :status 403: Forbidden :status 404: Domain not found / Record not found .. http:delete:: /dns/domain/(name)/record/(record_id) :DC-bound?: * |dc-yes| - ``domain.dc_bound=true`` * |dc-no| - ``domain.dc_bound=false`` :Permissions: * |DnsAdmin| or |DomainOwner| :Asynchronous?: * |async-no| :arg name: **required** - Domain name :type name: string :arg record_id: **required** - DNS record ID :type record_id: integer :status 200: SUCCESS :status 400: FAILURE :status 403: Forbidden :status 404: Domain not found / Record not found """ return RecordView(request, name, record_id, data).response()