diff --git a/connector_dns/README.rst b/connector_dns/README.rst new file mode 100644 index 0000000..abd4657 --- /dev/null +++ b/connector_dns/README.rst @@ -0,0 +1,9 @@ + .. image:: https://img.shields.io/badge/licence-LGPL-3-blue.svg + :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html + :alt: License: LGPL + +============= +DNS Connector +============= + +Odoo modules related to the management of Domain Name Server services within network infrastructure. diff --git a/connector_dns/__init__.py b/connector_dns/__init__.py new file mode 100644 index 0000000..f24d3e2 --- /dev/null +++ b/connector_dns/__init__.py @@ -0,0 +1,2 @@ +from . import components +from . import models diff --git a/connector_dns/__manifest__.py b/connector_dns/__manifest__.py new file mode 100644 index 0000000..b8569ba --- /dev/null +++ b/connector_dns/__manifest__.py @@ -0,0 +1,16 @@ +# © 2015-2019 Elico Corp (https://www.elico-corp.com). +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). +{ + 'name': 'DNS Base', + 'category': 'connector', + 'version': '12.0.1.0.0', + 'license': 'LGPL-3', + 'author': 'Elico Corp, Odoo Community Association (OCA)', + 'website': 'https://github.com/OCA/infrastructure-dns', + 'depends': [ + 'connector', + ], + 'data': [ + 'views/dns_view.xml', + ], +} diff --git a/connector_dns/components/__init__.py b/connector_dns/components/__init__.py new file mode 100644 index 0000000..83856da --- /dev/null +++ b/connector_dns/components/__init__.py @@ -0,0 +1,7 @@ +from . import core +from . import backend_adapter +from . import binder +from . import importer +from . import exporter +from . import deleter +from . import mapper diff --git a/connector_dns/components/backend_adapter.py b/connector_dns/components/backend_adapter.py new file mode 100644 index 0000000..226b5e1 --- /dev/null +++ b/connector_dns/components/backend_adapter.py @@ -0,0 +1,24 @@ +# © 2015-2019 Elico Corp (https://www.elico-corp.com). +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). +from odoo.addons.component.core import AbstractComponent + + +class DNSAbstractAdapter(AbstractComponent): + _name = 'dns.abstract.adapter' + _inherit = ['base.backend.adapter', 'base.dns.connector'] + _usage = 'backend.adapter' + + def search_all(self, domain_id): + raise NotImplementedError + + def search(self, external_id): + raise NotImplementedError + + def create(self): + raise NotImplementedError + + def write(self, external_id): + raise NotImplementedError + + def delete(self, external_id): + raise NotImplementedError diff --git a/connector_dns/components/binder.py b/connector_dns/components/binder.py new file mode 100644 index 0000000..698d796 --- /dev/null +++ b/connector_dns/components/binder.py @@ -0,0 +1,9 @@ +# © 2015-2019 Elico Corp (https://www.elico-corp.com). +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). +from odoo.addons.component.core import Component + + +class DNSBinder(Component): + _name = 'dns.binder' + _inherit = ['base.binder', 'base.dns.connector'] + _apply_on = ['dns.domain'] diff --git a/connector_dns/components/core.py b/connector_dns/components/core.py new file mode 100644 index 0000000..d934b74 --- /dev/null +++ b/connector_dns/components/core.py @@ -0,0 +1,12 @@ +# © 2015-2019 Elico Corp (https://www.elico-corp.com). +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). +from odoo.addons.component.core import AbstractComponent + + +class BaseDNSConnectorComponent(AbstractComponent): + """ Base DNS Connector Component + All components of this connector should inherit from it. + """ + _name = 'base.dns.connector' + _inherit = 'base.connector' + _collection = 'dns.backend' diff --git a/connector_dns/components/deleter.py b/connector_dns/components/deleter.py new file mode 100644 index 0000000..6b6a6fd --- /dev/null +++ b/connector_dns/components/deleter.py @@ -0,0 +1,20 @@ +# © 2015-2019 Elico Corp (https://www.elico-corp.com). +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). +from odoo.addons.component.core import AbstractComponent + + +class DNSAbstractDeleter(AbstractComponent): + _name = 'dns.abstract.deleter' + _inherit = 'base.deleter' + _usage = 'dns.deleter' + + def run(self, binding): + self._before_delete(binding) + self.backend_adapter.delete(binding) + self._after_delete() + + def _before_delete(self, binding): + return + + def _after_delete(self): + return diff --git a/connector_dns/components/exporter.py b/connector_dns/components/exporter.py new file mode 100644 index 0000000..ea1949e --- /dev/null +++ b/connector_dns/components/exporter.py @@ -0,0 +1,35 @@ +# © 2015-2019 Elico Corp (https://www.elico-corp.com). +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). +from odoo.addons.component.core import AbstractComponent + + +class DNSAbstractExporter(AbstractComponent): + _name = 'dns.abstract.exporter' + _inherit = ['base.exporter', 'base.dns.connector'] + _usage = 'dns.exporter' + + def __init__(self, work_context): + super(DNSAbstractExporter, self).__init__(work_context) + self.binding = None + self.external_id = None + + def run(self, binding): + self.binding = binding + self.external_id = self.binder.to_external(self.binding) + self._before_export() + if self.external_id: + self.backend_adapter.write(self.binding) + else: + response = self.backend_adapter.create(self.binding) + self.external_id = self._get_external_id(response) + self.binder.bind(self.external_id, self.binding) + self._after_export() + + def _before_export(self): + return + + def _after_export(self): + return + + def _get_external_id(self, response): + raise NotImplementedError diff --git a/connector_dns/components/importer.py b/connector_dns/components/importer.py new file mode 100644 index 0000000..6dabfee --- /dev/null +++ b/connector_dns/components/importer.py @@ -0,0 +1,76 @@ +# © 2015-2019 Elico Corp (https://www.elico-corp.com). +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). +import logging + +from odoo.addons.component.core import AbstractComponent + +_logger = logging.getLogger(__name__) + + +class DNSAbstractImporter(AbstractComponent): + _name = 'dns.abstract.importer' + _inherit = ['base.importer', 'base.dns.connector'] + _usage = 'dns.importer' + + def __init__(self, work_context): + super(DNSAbstractImporter, self).__init__(work_context) + self.domain_id = None + self.external_id = None + self.dns_record = None + + def _before_import(self): + return + + def _after_import(self): + return + + def _get_records(self): + return self.backend_adapter.search(self.external_id) + + def _validate_data(self, data): + return + + def _map_data(self): + return self.mapper.map_record(self.dns_record) + + def _get_binding(self): + return self.binder.to_internal(self.external_id) + + def _create_data(self, map_record, **kwargs): + return map_record.values(for_create=True, **kwargs) + + def _create(self, data): + """ Create the Odoo record """ + # special check on data before import + self._validate_data(data) + model = self.model.with_context(connector_no_export=True) + binding = model.create(data) + return binding + + def _update_data(self, map_record, **kwargs): + return map_record.values(**kwargs) + + def _update(self, binding, data): + self._validate_data(data) + binding.with_context(no_connector_export=True).write(data) + + def run(self, domain_id): + self.domain_id = domain_id + for _id in self.backend_adapter.search_all(domain_id): + self._run(_id) + + def _run(self, external_id): + self.external_id = external_id + self.dns_record = self._get_records() + + binding = self._get_binding() + self._before_import() + map_record = self._map_data() + if binding: + record = self._update_data(map_record) + self._update(binding, record) + else: + record = self._create_data(map_record) + binding = self._create(record) + self.binder.bind(self.external_id, binding) + self._after_import() diff --git a/connector_dns/components/mapper.py b/connector_dns/components/mapper.py new file mode 100644 index 0000000..7e8f2df --- /dev/null +++ b/connector_dns/components/mapper.py @@ -0,0 +1,9 @@ +# © 2015-2019 Elico Corp (https://www.elico-corp.com). +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). +from odoo.addons.component.core import AbstractComponent + + +class DNSAbstractImportMapper(AbstractComponent): + _name = 'dns.abstract.mapper' + _inherit = ['base.dns.connector', 'base.import.mapper'] + _usage = 'import.mapper' diff --git a/connector_dns/models/__init__.py b/connector_dns/models/__init__.py new file mode 100644 index 0000000..53a2863 --- /dev/null +++ b/connector_dns/models/__init__.py @@ -0,0 +1,4 @@ +from . import dns_backend +from . import dns_binding +from . import dns_domain +from . import dns_record diff --git a/connector_dns/models/dns_backend.py b/connector_dns/models/dns_backend.py new file mode 100644 index 0000000..3c06a7e --- /dev/null +++ b/connector_dns/models/dns_backend.py @@ -0,0 +1,16 @@ +# © 2015-2019 Elico Corp (https://www.elico-corp.com). +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). +from odoo import models, fields + + +class DNSBackend(models.Model): + _name = 'dns.backend' + _inherit = 'connector.backend' + _description = 'DNS Connector Backend' + + name = fields.Char('Name', required=True) + company_id = fields.Many2one( + 'res.company', + default=lambda self: self.env.user.company_id, + string='Company') + is_default = fields.Boolean('Is Default Backend') diff --git a/connector_dns/models/dns_binding.py b/connector_dns/models/dns_binding.py new file mode 100644 index 0000000..10d6bc5 --- /dev/null +++ b/connector_dns/models/dns_binding.py @@ -0,0 +1,47 @@ +# © 2015-2019 Elico Corp (https://www.elico-corp.com). +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). +from odoo import api, fields, models +from odoo.addons.queue_job.job import job + + +class DNSBingingAbstract(models.AbstractModel): + _name = 'dns.binding' + _inherit = 'external.binding' + _description = 'DNS binding (abstract)' + + # odoo_id = odoo-side id must be declared in concrete model + backend_id = fields.Many2one( + 'dns.backend', + string='DNS Backend', + required=True, + ondelete='restrict') + external_id = fields.Char('ID on DNS Provider', readonly=True) + sync_date = fields.Datetime('Sync Date') + + @job(default_channel='root') + @api.model + def import_dns_domains(self, backend_id, binding): + with backend_id.work_on(self._name) as work: + importer = work.component(usage='dns.importer') + return importer.run(binding) + + @job(default_channel='root') + @api.model + def import_dns_records(self, backend_id, binding): + with backend_id.work_on(self._name) as work: + importer = work.component(usage='dns.importer') + return importer.run(binding) + + @job(default_channel='root') + @api.model + def export_dns_records(self, backend_id, binding): + with backend_id.work_on(self._name) as work: + exporter = work.component(usage='dns.exporter') + return exporter.run(binding) + + @job(default_channel='root') + @api.model + def delete_dns_records(self, backend_id, binding): + with backend_id.work_on(self._name) as work: + deleter = work.component(usage='dns.deleter') + return deleter.run(binding) diff --git a/connector_dns/models/dns_domain.py b/connector_dns/models/dns_domain.py new file mode 100644 index 0000000..fc52b94 --- /dev/null +++ b/connector_dns/models/dns_domain.py @@ -0,0 +1,32 @@ +# © 2015-2019 Elico Corp (https://www.elico-corp.com). +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). +from odoo import models, fields + + +class DNSDomain(models.Model): + _name = 'dns.domain' + _inherit = 'dns.binding' + _description = 'DNS Domain' + + name = fields.Char( + string='Domain', + required=True, + help='Domain name without "www", such as "dnspod.cn"' + ) + state = fields.Selection([ + ('draft', 'Draft'), + ('done', 'Done'), + ('exception', 'Exception')], + string='State', + default='draft', + help='Done when succeed otherwise Exception') + record_ids = fields.One2many( + comodel_name='dns.record', + inverse_name='domain_id', + string='Subdomains' + ) + + _sql_constraints = [ + ('dns_domain_uniq', 'unique(external_id)', + "An external_id already exists with the same record."), + ] diff --git a/connector_dns/models/dns_record.py b/connector_dns/models/dns_record.py new file mode 100644 index 0000000..58c0264 --- /dev/null +++ b/connector_dns/models/dns_record.py @@ -0,0 +1,34 @@ +# © 2015-2019 Elico Corp (https://www.elico-corp.com). +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). +from odoo import models, fields + + +class DNSRecord(models.Model): + _name = 'dns.record' + _description = 'DNS records' + + name = fields.Char( + 'Subdomain', + help='Host record, such as "www"', + required=True) + domain_id = fields.Many2one( + comodel_name='dns.domain', + domain="[('state', '=', 'done')]", + string='Domain', + ondelete='cascade') + value = fields.Text( + string='Value', + help="such as IP:200.200.200.200", + required=True + ) + mx_priority = fields.Integer( + string='MX priority', + help="scope: 1-20", + default=1 + ) + ttl = fields.Integer( + string='TTL', + default=600, + help="scope: 1-604800", + required=True + ) diff --git a/connector_dns/views/dns_view.xml b/connector_dns/views/dns_view.xml new file mode 100644 index 0000000..9405259 --- /dev/null +++ b/connector_dns/views/dns_view.xml @@ -0,0 +1,91 @@ + + + + + dns.backend.form + dns.backend + +
+ + +
+
+
+
+
+
+
+ + + dns.record.form + dns.record + +
+ + + + + +
+
+
+ + + dns.record.tree + dns.record + + + + + + + + + + + + dns.backend.tree + dns.backend + + + + + + + + + DNS Backends + dns.backend + form + tree,form + + + + + DNS Records + dns.record + form + tree,form + + + + + + + +