Advertisement
Guest User

Untitled

a guest
Jul 8th, 2015
208
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 70.07 KB | None | 0 0
  1. # Copyright 2012 Managed I.T.
  2. # Copyright 2013 Hewlett-Packard Development Company, L.P.
  3. #
  4. # Author: Kiall Mac Innes <kiall@managedit.ie>
  5. #
  6. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  7. # not use this file except in compliance with the License. You may obtain
  8. # a copy of the License at
  9. #
  10. #      http://www.apache.org/licenses/LICENSE-2.0
  11. #
  12. # Unless required by applicable law or agreed to in writing, software
  13. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  14. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  15. # License for the specific language governing permissions and limitations
  16. # under the License.
  17. import re
  18. import contextlib
  19. import collections
  20. import functools
  21. import threading
  22.  
  23. from oslo.config import cfg
  24. from oslo import messaging
  25.  
  26. from designate.openstack.common import log as logging
  27. from designate.openstack.common import excutils
  28. import lockutils
  29. from designate.i18n import _LI
  30. from designate.i18n import _LC
  31. from designate import context as dcontext
  32. from designate import backend
  33. from designate import central
  34. from designate import exceptions
  35. from designate import network_api
  36. from designate import objects
  37. from designate import policy
  38. from designate import quota
  39. from designate import service
  40. from designate import utils
  41. from designate import storage
  42.  
  43.  
  44. LOG = logging.getLogger(__name__)
  45. DOMAIN_LOCKS = threading.local()
  46. NOTIFICATION_BUFFER = threading.local()
  47.  
  48. @contextlib.contextmanager
  49. def wrap_backend_call():
  50.     """
  51.    Wraps backend calls, ensuring any exception raised is a Backend exception.
  52.    """
  53.     try:
  54.         yield
  55.     except exceptions.Backend as exc:
  56.         raise
  57.     except Exception as exc:
  58.         raise exceptions.Backend('Unknown backend failure: %r' % exc)
  59.  
  60. def transaction(f):
  61.     # TODO(kiall): Get this a better home :)
  62.     @functools.wraps(f)
  63.     def wrapper(self, *args, **kwargs):
  64.         self.storage.begin()
  65.         try:
  66.             result = f(self, *args, **kwargs)
  67.         except Exception:
  68.             with excutils.save_and_reraise_exception():
  69.                 self.storage.rollback()
  70.         else:
  71.             self.storage.commit()
  72.             return result
  73.     return wrapper
  74.  
  75. def synchronized_domain(domain_arg=1, new_domain=False):
  76.     """Ensures only a single operation is in progress for each domain
  77.    A Decorator which ensures only a single operation can be happening
  78.    on a single domain at once, within the current designate-central instance
  79.    """
  80.     def outer(f):
  81.         @functools.wraps(f)
  82.         def wrapper(self, *args, **kwargs):
  83.             if not hasattr(DOMAIN_LOCKS, 'held'):
  84.                 # Create the held set if necessary
  85.                 DOMAIN_LOCKS.held = set()
  86.  
  87.             domain_id = None
  88.  
  89.             if 'domain_id' in kwargs:
  90.                 domain_id = kwargs['domain_id']
  91.  
  92.             elif 'domain' in kwargs:
  93.                 domain_id = kwargs['domain'].id
  94.  
  95.             elif 'recordset' in kwargs:
  96.                 domain_id = kwargs['recordset'].domain_id
  97.  
  98.             elif 'record' in kwargs:
  99.                 domain_id = kwargs['record'].domain_id
  100.  
  101.             # The various objects won't always have an ID set, we should
  102.             # attempt to locate an Object containing the ID.
  103.             if domain_id is None:
  104.                 for arg in itertools.chain(kwargs.values(), args):
  105.                     if isinstance(arg, objects.Domain):
  106.                         domain_id = arg.id
  107.                         if domain_id is not None:
  108.                             break
  109.  
  110.                     elif (isinstance(arg, objects.RecordSet) or
  111.                           isinstance(arg, objects.Record) or
  112.                           isinstance(arg, objects.ZoneTransferRequest) or
  113.                           isinstance(arg, objects.ZoneTransferAccept)):
  114.  
  115.                         domain_id = arg.domain_id
  116.                         if domain_id is not None:
  117.                             break
  118.  
  119.             # If we still don't have an ID, find the Nth argument as
  120.             # defined by the domain_arg decorator option.
  121.             if domain_id is None and len(args) > domain_arg:
  122.                 domain_id = args[domain_arg]
  123.  
  124.                 if isinstance(domain_id, objects.Domain):
  125.                     # If the value is a Domain object, extract it's ID.
  126.                     domain_id = domain_id.id
  127.  
  128.             if not new_domain and domain_id is None:
  129.                 raise Exception('Failed to determine domain id for '
  130.                                 'synchronized operation')
  131.  
  132.             if domain_id in DOMAIN_LOCKS.held:
  133.                 # Call the wrapped function
  134.                 return f(self, *args, **kwargs)
  135.             else:
  136.                 with lockutils.lock('domain-%s' % domain_id):
  137.                     DOMAIN_LOCKS.held.add(domain_id)
  138.  
  139.                     # Call the wrapped function
  140.                     result = f(self, *args, **kwargs)
  141.  
  142.                     DOMAIN_LOCKS.held.remove(domain_id)
  143.                     return result
  144.  
  145.         return wrapper
  146.     return outer
  147.  
  148. def notification(notification_type):
  149.     def outer(f):
  150.         @functools.wraps(f)
  151.         def wrapper(self, *args, **kwargs):
  152.             if not hasattr(NOTIFICATION_BUFFER, 'queue'):
  153.                 # Create the notifications queue if necessary
  154.                 NOTIFICATION_BUFFER.stack = 0
  155.                 NOTIFICATION_BUFFER.queue = collections.deque()
  156.  
  157.             NOTIFICATION_BUFFER.stack += 1
  158.  
  159.             try:
  160.                 # Find the context argument
  161.                 context = dcontext.DesignateContext.\
  162.                     get_context_from_function_and_args(f, args, kwargs)
  163.  
  164.                 # Call the wrapped function
  165.                 result = f(self, *args, **kwargs)
  166.  
  167.                 # Enqueue the notification
  168.                 LOG.debug('Queueing notification for %(type)s ',
  169.                           {'type': notification_type})
  170.                 NOTIFICATION_BUFFER.queue.appendleft(
  171.                     (context, notification_type, result,))
  172.  
  173.                 return result
  174.  
  175.             finally:
  176.                 NOTIFICATION_BUFFER.stack -= 1
  177.  
  178.                 if NOTIFICATION_BUFFER.stack == 0:
  179.                     LOG.debug('Emitting %(count)d notifications',
  180.                               {'count': len(NOTIFICATION_BUFFER.queue)})
  181.                     # Send the queued notifications, in order.
  182.                     for value in NOTIFICATION_BUFFER.queue:
  183.                         LOG.debug('Emitting %(type)s notification',
  184.                                   {'type': value[1]})
  185.                         self.notifier.info(value[0], value[1], value[2])
  186.  
  187.                     # Reset the queue
  188.                     NOTIFICATION_BUFFER.queue.clear()
  189.  
  190.         return wrapper
  191.     return outer
  192.  
  193.  
  194. class Service(service.RPCService):
  195.     RPC_API_VERSION = '4.0'
  196.  
  197.     target = messaging.Target(version=RPC_API_VERSION)
  198.  
  199.     def __init__(self, *args, **kwargs):
  200.         super(Service, self).__init__(*args, **kwargs)
  201.  
  202.         backend_driver = cfg.CONF['service:central'].backend_driver
  203.         self.backend = backend.get_backend(backend_driver, self)
  204.  
  205.         # Get a storage connection
  206.         storage_driver = cfg.CONF['service:central'].storage_driver
  207.         self.storage = storage.get_storage(storage_driver)
  208.  
  209.         # Get a quota manager instance
  210.         self.quota = quota.get_quota()
  211.  
  212.         self.network_api = network_api.get_network_api(cfg.CONF.network_api)
  213.  
  214.     def start(self):
  215.         # Check to see if there are any TLDs in the database
  216.         tlds = self.storage.find_tlds({})
  217.         if tlds:
  218.             self.check_for_tlds = True
  219.             LOG.info(_LI("Checking for TLDs"))
  220.         else:
  221.             self.check_for_tlds = False
  222.             LOG.info(_LI("NOT checking for TLDs"))
  223.  
  224.         self.backend.start()
  225.  
  226.         super(Service, self).start()
  227.  
  228.     def stop(self):
  229.         super(Service, self).stop()
  230.  
  231.         self.backend.stop()
  232.  
  233.     @property
  234.     def mdns_api(self):
  235.         return central.get_mdns_api()
  236.  
  237.     def _is_valid_domain_name(self, context, domain_name):
  238.         # Validate domain name length
  239.         if len(domain_name) > cfg.CONF['service:central'].max_domain_name_len:
  240.             raise exceptions.InvalidDomainName('Name too long')
  241.  
  242.         # Break the domain name up into its component labels
  243.         domain_labels = domain_name.strip('.').split('.')
  244.  
  245.         # We need more than 1 label.
  246.         if len(domain_labels) <= 1:
  247.             raise exceptions.InvalidDomainName('More than one label is '
  248.                                                'required')
  249.  
  250.         # Check the TLD for validity if there are entries in the database
  251.         if self.check_for_tlds:
  252.             try:
  253.                 self.storage.find_tld(context, {'name': domain_labels[-1]})
  254.             except exceptions.TldNotFound:
  255.                 raise exceptions.InvalidDomainName('Invalid TLD')
  256.  
  257.             # Now check that the domain name is not the same as a TLD
  258.             try:
  259.                 stripped_domain_name = domain_name.strip('.').lower()
  260.                 self.storage.find_tld(
  261.                     context,
  262.                     {'name': stripped_domain_name})
  263.             except exceptions.TldNotFound:
  264.                 pass
  265.             else:
  266.                 raise exceptions.InvalidDomainName(
  267.                     'Domain name cannot be the same as a TLD')
  268.  
  269.         # Check domain name blacklist
  270.         if self._is_blacklisted_domain_name(context, domain_name):
  271.             # Some users are allowed bypass the blacklist.. Is this one?
  272.             if not policy.check('use_blacklisted_domain', context,
  273.                                 do_raise=False):
  274.                 raise exceptions.InvalidDomainName('Blacklisted domain name')
  275.  
  276.         return True
  277.  
  278.     def _is_valid_recordset_name(self, context, domain, recordset_name):
  279.         if not recordset_name.endswith('.'):
  280.             raise ValueError('Please supply a FQDN')
  281.  
  282.         # Validate record name length
  283.         max_len = cfg.CONF['service:central'].max_recordset_name_len
  284.         if len(recordset_name) > max_len:
  285.             raise exceptions.InvalidRecordSetName('Name too long')
  286.  
  287.         # RecordSets must be contained in the parent zone
  288.         if not recordset_name.endswith(domain['name']):
  289.             raise exceptions.InvalidRecordSetLocation(
  290.                 'RecordSet is not contained within it\'s parent domain')
  291.  
  292.     def _is_valid_recordset_placement(self, context, domain, recordset_name,
  293.                                       recordset_type, recordset_id=None):
  294.         # CNAME's must not be created at the zone apex.
  295.         if recordset_type == 'CNAME' and recordset_name == domain.name:
  296.             raise exceptions.InvalidRecordSetLocation(
  297.                 'CNAME recordsets may not be created at the zone apex')
  298.  
  299.         # CNAME's must not share a name with other recordsets
  300.         criterion = {
  301.             'domain_id': domain.id,
  302.             'name': recordset_name,
  303.         }
  304.  
  305.         if recordset_type != 'CNAME':
  306.             criterion['type'] = 'CNAME'
  307.  
  308.         recordsets = self.storage.find_recordsets(context, criterion)
  309.  
  310.         if ((len(recordsets) == 1 and recordsets[0].id != recordset_id)
  311.                 or len(recordsets) > 1):
  312.             raise exceptions.InvalidRecordSetLocation(
  313.                 'CNAME recordsets may not share a name with any other records')
  314.  
  315.         return True
  316.  
  317.     def _is_valid_recordset_placement_subdomain(self, context, domain,
  318.                                                 recordset_name,
  319.                                                 criterion=None):
  320.         """
  321.        Check that the placement of the requested rrset belongs to any of the
  322.        domains subdomains..
  323.        """
  324.         LOG.debug("Checking if %s belongs in any of %s subdomains" %
  325.                   (recordset_name, domain.name))
  326.  
  327.         criterion = criterion or {}
  328.  
  329.         context = context.elevated()
  330.         context.all_tenants = True
  331.  
  332.         if domain.name == recordset_name:
  333.             return
  334.  
  335.         child_domains = self.storage.find_domains(
  336.             context, {"parent_domain_id": domain.id})
  337.         for child_domain in child_domains:
  338.             try:
  339.                 self._is_valid_recordset_name(
  340.                     context, child_domain, recordset_name)
  341.             except Exception:
  342.                 continue
  343.             else:
  344.                 msg = 'RecordSet belongs in a child zone: %s' % \
  345.                     child_domain['name']
  346.                 raise exceptions.InvalidRecordSetLocation(msg)
  347.  
  348.     def _is_blacklisted_domain_name(self, context, domain_name):
  349.         """
  350.        Ensures the provided domain_name is not blacklisted.
  351.        """
  352.  
  353.         blacklists = self.storage.find_blacklists(context)
  354.  
  355.         for blacklist in blacklists:
  356.             if bool(re.search(blacklist.pattern, domain_name)):
  357.                 return True
  358.  
  359.         return False
  360.  
  361.     def _is_subdomain(self, context, domain_name):
  362.         """
  363.        Ensures the provided domain_name is the subdomain
  364.        of an existing domain (checks across all tenants)
  365.        """
  366.         context = context.elevated()
  367.         context.all_tenants = True
  368.  
  369.         # Break the name up into it's component labels
  370.         labels = domain_name.split(".")
  371.  
  372.         i = 1
  373.  
  374.         # Starting with label #2, search for matching domain's in the database
  375.         while (i < len(labels)):
  376.             name = '.'.join(labels[i:])
  377.  
  378.             try:
  379.                 domain = self.storage.find_domain(context, {'name': name})
  380.             except exceptions.DomainNotFound:
  381.                 i += 1
  382.             else:
  383.                 return domain
  384.  
  385.         return False
  386.  
  387.     def _is_superdomain(self, context, domain_name):
  388.         """
  389.        Ensures the provided domain_name is the parent domain
  390.        of an existing subdomain (checks across all tenants)
  391.        """
  392.         context = context.elevated()
  393.         context.all_tenants = True
  394.  
  395.         # Create wildcard term to catch all subdomains
  396.         search_term = "*%s" % domain_name
  397.  
  398.         try:
  399.             criterion = {'name': search_term}
  400.             subdomains = self.storage.find_domains(context, criterion)
  401.         except exceptions.DomainNotFound:
  402.             return False
  403.  
  404.         return subdomains
  405.  
  406.     def _is_valid_ttl(self, context, ttl):
  407.         min_ttl = cfg.CONF['service:central'].min_ttl
  408.         if min_ttl != "None" and ttl < int(min_ttl):
  409.             try:
  410.                 policy.check('use_low_ttl', context)
  411.             except exceptions.Forbidden:
  412.                 raise exceptions.InvalidTTL('TTL is below the minimum: %s'
  413.                                             % min_ttl)
  414.  
  415.     def _increment_domain_serial(self, context, domain):
  416.  
  417.         # Increment the serial number
  418.         domain.serial = utils.increment_serial(domain.serial)
  419.         domain = self.storage.update_domain(context, domain)
  420.  
  421.         # Update SOA record
  422.         self._update_soa(context, domain)
  423.  
  424.         return domain
  425.  
  426.     # Methods to handle priority
  427.     def _get_priority(self, recordset):
  428.         if recordset.type != "MX" and recordset.type != "SRV":
  429.             return recordset
  430.         else:
  431.             if recordset.records is not None:
  432.                 for r in recordset.records:
  433.                     r.data = str(r.priority) + " " + r.data
  434.  
  435.         return recordset
  436.  
  437.     def _set_priority(self, recordset):
  438.         if recordset.type != "MX" and recordset.type != "SRV":
  439.             return recordset
  440.         else:
  441.             if recordset.records is not None:
  442.                 for r in recordset.records:
  443.                     head, sep, tail = r.data.partition(" ")
  444.                     if sep:
  445.                         r.priority = head
  446.                         r.data = tail
  447.  
  448.         return recordset
  449.  
  450.     # SOA Recordset Methods
  451.     def _build_soa_record(self, zone, servers):
  452.         return "%s %s. %d %d %d %d %d" % (servers[0]['name'],
  453.                                           zone['email'].replace("@", "."),
  454.                                           zone['serial'],
  455.                                           zone['refresh'],
  456.                                           zone['retry'],
  457.                                           zone['expire'],
  458.                                           zone['minimum'])
  459.  
  460.     def _create_soa(self, context, zone):
  461.         # Need elevated context to get the servers
  462.         elevated_context = context.elevated()
  463.         elevated_context.all_tenants = True
  464.         servers = self.find_servers(elevated_context)
  465.         soa_values = [self._build_soa_record(zone, servers)]
  466.         recordlist = objects.RecordList(objects=[
  467.             objects.Record(data=r, managed=True) for r in soa_values])
  468.         values = {
  469.             'name': zone['name'],
  470.             'type': "SOA",
  471.             'records': recordlist
  472.         }
  473.         soa = self._create_recordset_in_storage(
  474.             context, zone, objects.RecordSet(**values),
  475.             increment_serial=False)
  476.         return soa
  477.  
  478.  
  479.     def _update_soa(self, context, zone):
  480.  
  481.         servers = self.get_domain_servers(context, zone['id'])
  482.  
  483.         soa = self.find_recordset(context,
  484.                                   criterion={'domain_id': zone['id'],
  485.                                              'type': "SOA"})
  486.  
  487.         soa.records[0].data = self._build_soa_record(zone, servers)
  488.  
  489.         self._update_recordset_in_storage(context, zone, soa,
  490.                                           increment_serial=False)  
  491.  
  492.     # NS Recordset Methods
  493.     def _create_ns(self, context, zone, servers):
  494.         # Create an NS record for each server
  495.         ns_values = []
  496.         for s in servers:
  497.             ns_values.append(s.name)
  498.         recordlist = objects.RecordList(objects=[
  499.             objects.Record(data=r, managed=True) for r in ns_values])
  500.         values = {
  501.             'name': zone['name'],
  502.             'type': "NS",
  503.             'records': recordlist
  504.         }
  505.         ns = self._create_recordset_in_storage(
  506.             context, zone, objects.RecordSet(**values),
  507.             increment_serial=False)
  508.  
  509.         return ns
  510.  
  511.     def _update_ns(self, context, zone, orig_name, new_name):
  512.         # Get the zone's NS recordset
  513.         ns = self.find_recordset(context,
  514.                                  criterion={'domain_id': zone['id'],
  515.                                             'type': "NS"})
  516.  
  517.         for r in ns.records:
  518.             if r.data == orig_name:
  519.                 r.data = new_name
  520.                 self._update_recordset_in_storage(context, zone, ns)
  521.  
  522.     def _add_ns(self, context, zone, server):
  523.         # Get NS recordset
  524.         ns = self.find_recordset(context,
  525.                                  criterion={'domain_id': zone['id'],
  526.                                             'type': "NS"})
  527.         # Add new record to recordset
  528.         ns_record = objects.Record(data=server.name)
  529.         ns.records.append(ns_record)
  530.  
  531.         self._update_recordset_in_storage(context, zone, ns)
  532.  
  533.     def _delete_ns(self, context, zone, server):
  534.         ns = self.find_recordset(context,
  535.                                  criterion={'domain_id': zone['id'],
  536.                                             'type': "NS"})
  537.         records = ns.records
  538.  
  539.         for r in records:
  540.             if r.data == server.name:
  541.                 ns.records.remove(r)
  542.  
  543.         self._update_recordset_in_storage(context, zone, ns)
  544.  
  545.     # Quota Enforcement Methods
  546.     def _enforce_domain_quota(self, context, tenant_id):
  547.         criterion = {'tenant_id': tenant_id}
  548.         count = self.storage.count_domains(context, criterion)
  549.  
  550.         self.quota.limit_check(context, tenant_id, domains=count)
  551.  
  552.     def _enforce_recordset_quota(self, context, domain):
  553.         # TODO(kiall): Enforce RRSet Quotas
  554.         pass
  555.  
  556.     def _enforce_record_quota(self, context, domain, recordset):
  557.         # Ensure the records per domain quota is OK
  558.         criterion = {'domain_id': domain['id']}
  559.         count = self.storage.count_records(context, criterion)
  560.  
  561.         self.quota.limit_check(context, domain['tenant_id'],
  562.                                domain_records=count)
  563.  
  564.         # TODO(kiall): Enforce Records per RRSet Quotas
  565.  
  566.     # Misc Methods
  567.     def get_absolute_limits(self, context):
  568.         # NOTE(Kiall): Currently, we only have quota based limits..
  569.         return self.quota.get_quotas(context, context.tenant)
  570.  
  571.     # Quota Methods
  572.     def get_quotas(self, context, tenant_id):
  573.         target = {'tenant_id': tenant_id}
  574.         policy.check('get_quotas', context, target)
  575.  
  576.         # This allows admins to get quota information correctly for all tenants
  577.         context.all_tenants = True
  578.  
  579.         return self.quota.get_quotas(context, tenant_id)
  580.  
  581.     def get_quota(self, context, tenant_id, resource):
  582.         target = {'tenant_id': tenant_id, 'resource': resource}
  583.         policy.check('get_quota', context, target)
  584.  
  585.         return self.quota.get_quota(context, tenant_id, resource)
  586.  
  587.     @transaction
  588.     def set_quota(self, context, tenant_id, resource, hard_limit):
  589.         target = {
  590.             'tenant_id': tenant_id,
  591.             'resource': resource,
  592.             'hard_limit': hard_limit,
  593.         }
  594.  
  595.         policy.check('set_quota', context, target)
  596.  
  597.         return self.quota.set_quota(context, tenant_id, resource, hard_limit)
  598.  
  599.     @transaction
  600.     def reset_quotas(self, context, tenant_id):
  601.         target = {'tenant_id': tenant_id}
  602.         policy.check('reset_quotas', context, target)
  603.  
  604.         self.quota.reset_quotas(context, tenant_id)
  605.  
  606.     # Server Methods
  607.     @notification('dns.server.create')
  608.     def create_server(self, context, server):
  609.         policy.check('create_server', context)
  610.  
  611.         elevated_context = context.elevated()
  612.         elevated_context.all_tenants = True
  613.  
  614.         zones = self.find_domains(elevated_context)
  615.  
  616.         server = self._create_server_in_storage(context, server, zones)
  617.         return server
  618.  
  619.     @transaction
  620.     def _create_server_in_storage(self, context, server, zones):
  621.  
  622.         server = self.storage.create_server(context, server)
  623.  
  624.         elevated_context = context.elevated()
  625.         elevated_context.all_tenants = True
  626.  
  627.         # Add NS recordsets for all zones.
  628.         for zone in zones:
  629.             self._add_ns(elevated_context, zone, server)
  630.  
  631.         return server
  632.  
  633.     def find_servers(self, context, criterion=None, marker=None, limit=None,
  634.                      sort_key=None, sort_dir=None):
  635.         policy.check('find_servers', context)
  636.  
  637.         return self.storage.find_servers(context, criterion, marker, limit,
  638.                                          sort_key, sort_dir)
  639.  
  640.     def get_server(self, context, server_id):
  641.         policy.check('get_server', context, {'server_id': server_id})
  642.  
  643.         return self.storage.get_server(context, server_id)
  644.  
  645.  
  646.     @notification('dns.server.update')
  647.     def update_server(self, context, server):
  648.         target = {
  649.             'server_id': server.obj_get_original_value('id'),
  650.         }
  651.         policy.check('update_server', context, target)
  652.  
  653.         elevated_context = context.elevated()
  654.         elevated_context.all_tenants = True
  655.  
  656.         zones = self.find_domains(elevated_context)
  657.  
  658.         server = self._update_server_in_storage(context, server, zones)
  659.  
  660.         return server
  661.  
  662.     @transaction
  663.     def _update_server_in_storage(self, context, server, zones):
  664.  
  665.         orig_server_name = server.obj_get_original_value('name')
  666.         new_server_name = server.name
  667.  
  668.         server = self.storage.update_server(context, server)
  669.  
  670.         elevated_context = context.elevated()
  671.         elevated_context.all_tenants = True
  672.  
  673.         # Update NS recordsets for all zones.
  674.         for zone in zones:
  675.             self._update_ns(elevated_context, zone, orig_server_name,
  676.                             new_server_name)
  677.  
  678.         return server
  679.  
  680.     @notification('dns.server.delete')
  681.     def delete_server(self, context, server_id):
  682.         policy.check('delete_server', context, {'server_id': server_id})
  683.  
  684.         elevated_context = context.elevated()
  685.         elevated_context.all_tenants = True
  686.  
  687.         zones = self.find_domains(elevated_context)
  688.  
  689.         server = self._delete_server_in_storage(context, server_id, zones)
  690.  
  691.         for zone in zones:
  692.             self.pool_manager_api.update_domain(elevated_context, zone)
  693.  
  694.         # Update backend with the new server..
  695.         with wrap_backend_call():
  696.             self.backend.delete_server(context, server)
  697.  
  698.         return server
  699.  
  700.     @transaction
  701.     def _delete_server_in_storage(self, context, server_id, zones):
  702.  
  703.         # don't delete last of servers
  704.         servers = self.storage.find_servers(context)
  705.         if len(servers) == 1 and server_id == servers[0].id:
  706.             raise exceptions.LastServerDeleteNotAllowed(
  707.                 "Not allowed to delete last of servers")
  708.  
  709.         server = self.storage.delete_server(context, server_id)
  710.  
  711.         elevated_context = context.elevated()
  712.         elevated_context.all_tenants = True
  713.  
  714.         # Delete NS recordsets for all zones.
  715.         for zone in zones:
  716.             self._delete_ns(elevated_context, zone, server)
  717.  
  718.         return server
  719.  
  720.     # TLD Methods
  721.     @notification('dns.tld.create')
  722.     @transaction
  723.     def create_tld(self, context, tld):
  724.         policy.check('create_tld', context)
  725.  
  726.         # The TLD is only created on central's storage and not on the backend.
  727.         created_tld = self.storage.create_tld(context, tld)
  728.  
  729.         # Set check for tlds to be true
  730.         self.check_for_tlds = True
  731.         return created_tld
  732.  
  733.     def find_tlds(self, context, criterion=None, marker=None, limit=None,
  734.                   sort_key=None, sort_dir=None):
  735.         policy.check('find_tlds', context)
  736.  
  737.         return self.storage.find_tlds(context, criterion, marker, limit,
  738.                                       sort_key, sort_dir)
  739.  
  740.     def get_tld(self, context, tld_id):
  741.         policy.check('get_tld', context, {'tld_id': tld_id})
  742.  
  743.         return self.storage.get_tld(context, tld_id)
  744.    
  745.     @notification('dns.tld.update')
  746.     @transaction
  747.     def update_tld(self, context, tld):
  748.         target = {
  749.             'tld_id': tld.obj_get_original_value('id'),
  750.         }
  751.         policy.check('update_tld', context, target)
  752.  
  753.         tld = self.storage.update_tld(context, tld)
  754.  
  755.         return tld
  756.    
  757.     @notification('dns.tld.delete')
  758.     @transaction
  759.     def delete_tld(self, context, tld_id):
  760.         # Known issue - self.check_for_tld is not reset here.  So if the last
  761.         # TLD happens to be deleted, then we would incorrectly do the TLD
  762.         # validations.
  763.         # This decision was influenced by weighing the (ultra low) probability
  764.         # of hitting this issue vs doing the checks for every delete.
  765.         policy.check('delete_tld', context, {'tld_id': tld_id})
  766.  
  767.         tld = self.storage.delete_tld(context, tld_id)
  768.  
  769.     # TSIG Key Methods
  770.     @notification('dns.tsigkey.create')
  771.     @transaction
  772.     def create_tsigkey(self, context, tsigkey):
  773.         policy.check('create_tsigkey', context)
  774.  
  775.         created_tsigkey = self.storage.create_tsigkey(context, tsigkey)
  776.  
  777.         with wrap_backend_call():
  778.             self.backend.create_tsigkey(context, created_tsigkey)
  779.  
  780.         return created_tsigkey
  781.  
  782.     def find_tsigkeys(self, context, criterion=None, marker=None, limit=None,
  783.                       sort_key=None, sort_dir=None):
  784.         policy.check('find_tsigkeys', context)
  785.  
  786.         return self.storage.find_tsigkeys(context, criterion, marker,
  787.                                           limit, sort_key, sort_dir)
  788.  
  789.     def get_tsigkey(self, context, tsigkey_id):
  790.         policy.check('get_tsigkey', context, {'tsigkey_id': tsigkey_id})
  791.  
  792.         return self.storage.get_tsigkey(context, tsigkey_id)
  793.  
  794.     @notification('dns.tsigkey.update')
  795.     @transaction
  796.     def update_tsigkey(self, context, tsigkey):
  797.         target = {
  798.             'tsigkey_id': tsigkey.obj_get_original_value('id'),
  799.         }
  800.         policy.check('update_tsigkey', context, target)
  801.  
  802.         tsigkey = self.storage.update_tsigkey(context, tsigkey)
  803.  
  804.         with wrap_backend_call():
  805.             self.backend.update_tsigkey(context, tsigkey)
  806.  
  807.         return tsigkey
  808.  
  809.     @notification('dns.tsigkey.delete')
  810.     @transaction
  811.     def delete_tsigkey(self, context, tsigkey_id):
  812.         policy.check('delete_tsigkey', context, {'tsigkey_id': tsigkey_id})
  813.  
  814.         tsigkey = self.storage.delete_tsigkey(context, tsigkey_id)
  815.  
  816.         with wrap_backend_call():
  817.             self.backend.delete_tsigkey(context, tsigkey)
  818.  
  819.     # Tenant Methods
  820.     def find_tenants(self, context):
  821.         policy.check('find_tenants', context)
  822.         return self.storage.find_tenants(context)
  823.  
  824.     def get_tenant(self, context, tenant_id):
  825.         target = {
  826.             'tenant_id': tenant_id
  827.         }
  828.  
  829.         policy.check('get_tenant', context, target)
  830.  
  831.         return self.storage.get_tenant(context, tenant_id)
  832.  
  833.     def count_tenants(self, context):
  834.         policy.check('count_tenants', context)
  835.         return self.storage.count_tenants(context)
  836.  
  837.     # Domain Methods
  838.     @notification('dns.domain.create')
  839.     @synchronized_domain(new_domain=True)
  840.     def create_domain(self, context, domain):
  841.         # TODO(kiall): Refactor this method into *MUCH* smaller chunks.
  842.  
  843.         # Default to creating in the current users tenant
  844.         if domain.tenant_id is None:
  845.             domain.tenant_id = context.tenant
  846.  
  847.         target = {
  848.             'tenant_id': domain.tenant_id,
  849.             'domain_name': domain.name
  850.         }
  851.  
  852.         policy.check('create_domain', context, target)
  853.  
  854.         # Ensure the tenant has enough quota to continue
  855.         self._enforce_domain_quota(context, domain.tenant_id)
  856.  
  857.         # Ensure the domain name is valid
  858.         self._is_valid_domain_name(context, domain.name)
  859.  
  860.         # Ensure TTL is above the minimum
  861.         if domain.ttl is not None:
  862.             self._is_valid_ttl(context, domain.ttl)
  863.  
  864.         # Handle sub-domains appropriately
  865.         parent_domain = self._is_subdomain(context, domain.name)
  866.         if parent_domain:
  867.             if parent_domain.tenant_id == domain.tenant_id:
  868.                 # Record the Parent Domain ID
  869.                 domain.parent_domain_id = parent_domain.id
  870.             else:
  871.                 raise exceptions.Forbidden('Unable to create subdomain in '
  872.                                            'another tenants domain')
  873.  
  874.         # Handle super-domains appropriately
  875.         subdomains = self._is_superdomain(context, domain.name)
  876.         if subdomains:
  877.             LOG.debug("Domain '{0}' is a superdomain.".format(domain.name))
  878.             for subdomain in subdomains:
  879.                 if subdomain.tenant_id != domain.tenant_id:
  880.                     raise exceptions.Forbidden('Unable to create domain '
  881.                                                'because another tenant '
  882.                                                'owns a subdomain of '
  883.                                                'the domain')
  884.         # If this succeeds, subdomain parent IDs will be updated
  885.         # after domain is created
  886.  
  887.         # NOTE(kiall): Fetch the servers before creating the domain, this way
  888.         #              we can prevent domain creation if no servers are
  889.         #              configured.
  890.         servers = self.storage.find_servers(context)
  891.  
  892.         if len(servers) == 0:
  893.             LOG.critical(_LC('No servers configured. '
  894.                              'Please create at least one server'))
  895.             raise exceptions.NoServersConfigured()
  896.  
  897.        
  898.         domain = self._create_domain_in_storage(context, domain)
  899.  
  900.         with wrap_backend_call():
  901.             self.backend.create_domain(context, domain)
  902.  
  903.         # If domain is a superdomain, update subdomains
  904.         # with new parent IDs
  905.         for subdomain in subdomains:
  906.             LOG.debug("Updating subdomain '{0}' parent ID "
  907.                       "using superdomain ID '{1}'"
  908.                       .format(subdomain.name, domain.id))
  909.             subdomain.parent_domain_id = domain.id
  910.             self.update_domain(context, subdomain)
  911.  
  912.         return domain
  913.  
  914.     @transaction
  915.     def _create_domain_in_storage(self, context, domain):
  916.  
  917.         domain.action = 'CREATE'
  918.         domain.status = 'PENDING'
  919.  
  920.         # Set the serial number
  921.         domain.serial = utils.increment_serial()
  922.  
  923.         domain = self.storage.create_domain(context, domain)
  924.  
  925.         if domain.obj_attr_is_set('recordsets'):
  926.             for rrset in domain.recordsets:
  927.                 self._create_recordset_in_storage(
  928.                     context, domain, rrset, increment_serial=False)
  929.  
  930.         servers = self.storage.find_servers(context)
  931.  
  932.         # Create the SOA and NS recordsets for the new domain.  The SOA
  933.         # record will always be the first 'created_at' record for a domain.
  934.         self._create_soa(context, domain)
  935.         self._create_ns(context, domain, servers)
  936.  
  937.         return domain
  938.  
  939.     def get_domain(self, context, domain_id):
  940.         domain = self.storage.get_domain(context, domain_id)
  941.  
  942.         target = {
  943.             'domain_id': domain_id,
  944.             'domain_name': domain.name,
  945.             'tenant_id': domain.tenant_id
  946.         }
  947.         policy.check('get_domain', context, target)
  948.  
  949.         return domain
  950.  
  951.     def get_domain_servers(self, context, domain_id, criterion=None):
  952.         domain = self.storage.get_domain(context, domain_id)
  953.  
  954.         target = {
  955.             'domain_id': domain_id,
  956.             'domain_name': domain.name,
  957.             'tenant_id': domain.tenant_id
  958.         }
  959.  
  960.         policy.check('get_domain_servers', context, target)
  961.  
  962.         # TODO(kiall): Once we allow domains to be allocated on 1 of N server
  963.         #              pools, return the filtered list here.
  964.         return self.storage.find_servers(context, criterion)
  965.  
  966.     def find_domains(self, context, criterion=None, marker=None, limit=None,
  967.                      sort_key=None, sort_dir=None):
  968.         target = {'tenant_id': context.tenant}
  969.         policy.check('find_domains', context, target)
  970.  
  971.         return self.storage.find_domains(context, criterion, marker, limit,
  972.                                          sort_key, sort_dir)
  973.  
  974.     def find_domain(self, context, criterion=None):
  975.         target = {'tenant_id': context.tenant}
  976.         policy.check('find_domain', context, target)
  977.  
  978.         return self.storage.find_domain(context, criterion)
  979.  
  980.     @notification('dns.domain.update')
  981.     @synchronized_domain()
  982.     def update_domain(self, context, domain, increment_serial=True):
  983.         # TODO(kiall): Refactor this method into *MUCH* smaller chunks.
  984.         target = {
  985.             'domain_id': domain.obj_get_original_value('id'),
  986.             'domain_name': domain.obj_get_original_value('name'),
  987.             'tenant_id': domain.obj_get_original_value('tenant_id'),
  988.         }
  989.  
  990.         policy.check('update_domain', context, target)
  991.  
  992.         changes = domain.obj_get_changes()
  993.  
  994.         # Ensure immutable fields are not changed
  995.         if 'tenant_id' in changes:
  996.             # TODO(kiall): Moving between tenants should be allowed, but the
  997.             #              current code will not take into account that
  998.             #              RecordSets and Records must also be moved.
  999.             raise exceptions.BadRequest('Moving a domain between tenants is '
  1000.                                         'not allowed')
  1001.  
  1002.         if 'name' in changes:
  1003.             raise exceptions.BadRequest('Renaming a domain is not allowed')
  1004.  
  1005.         # Ensure TTL is above the minimum
  1006.         ttl = changes.get('ttl', None)
  1007.         if ttl is not None:
  1008.             self._is_valid_ttl(context, ttl)
  1009.  
  1010.         domain = self._update_domain_in_storage(
  1011.             context, domain, increment_serial=increment_serial)
  1012.  
  1013.         with wrap_backend_call():
  1014.             self.backend.update_domain(context, domain)
  1015.  
  1016.         return domain
  1017.  
  1018.     @transaction
  1019.     def _update_domain_in_storage(self, context, domain,
  1020.                                   increment_serial=True):
  1021.  
  1022.         domain.action = 'UPDATE'
  1023.         domain.status = 'PENDING'
  1024.  
  1025.         if increment_serial:
  1026.             domain = self._increment_domain_serial(context, domain)
  1027.  
  1028.         domain = self.storage.update_domain(context, domain)
  1029.  
  1030.         return domain
  1031.  
  1032.     @notification('dns.domain.delete')
  1033.     @synchronized_domain()
  1034.     def delete_domain(self, context, domain_id):
  1035.         domain = self.storage.get_domain(context, domain_id)
  1036.  
  1037.         target = {
  1038.             'domain_id': domain_id,
  1039.             'domain_name': domain.name,
  1040.             'tenant_id': domain.tenant_id
  1041.         }
  1042.  
  1043.         policy.check('delete_domain', context, target)
  1044.  
  1045.         # Prevent deletion of a zone which has child zones
  1046.         criterion = {'parent_domain_id': domain_id}
  1047.  
  1048.         if self.storage.count_domains(context, criterion) > 0:
  1049.             raise exceptions.DomainHasSubdomain('Please delete any subdomains '
  1050.                                                 'before deleting this domain')
  1051.  
  1052.         domain = self._delete_domain_in_storage(context, domain)
  1053.  
  1054.         with wrap_backend_call():
  1055.             self.backend.delete_domain(context, domain)
  1056.  
  1057.         return domain
  1058.  
  1059.     @transaction
  1060.     def _delete_domain_in_storage(self, context, domain):
  1061.  
  1062.         domain.action = 'DELETE'
  1063.         domain.status = 'PENDING'
  1064.  
  1065.         domain = self.storage.update_domain(context, domain)
  1066.  
  1067.         return domain
  1068.  
  1069.     def count_domains(self, context, criterion=None):
  1070.         if criterion is None:
  1071.             criterion = {}
  1072.  
  1073.         target = {
  1074.             'tenant_id': criterion.get('tenant_id', None)
  1075.         }
  1076.  
  1077.         policy.check('count_domains', context, target)
  1078.  
  1079.         return self.storage.count_domains(context, criterion)
  1080.  
  1081.     # Report combining all the count reports based on criterion
  1082.     def count_report(self, context, criterion=None):
  1083.         reports = []
  1084.  
  1085.         if criterion is None:
  1086.             # Get all the reports
  1087.             reports.append({'zones': self.count_domains(context),
  1088.                             'records': self.count_records(context),
  1089.                             'tenants': self.count_tenants(context)})
  1090.         elif criterion == 'zones':
  1091.             reports.append({'zones': self.count_domains(context)})
  1092.         elif criterion == 'records':
  1093.             reports.append({'records': self.count_records(context)})
  1094.         elif criterion == 'tenants':
  1095.             reports.append({'tenants': self.count_tenants(context)})
  1096.         else:
  1097.             raise exceptions.ReportNotFound()
  1098.  
  1099.         return reports
  1100.  
  1101.     @notification('dns.domain.touch')
  1102.     @synchronized_domain()
  1103.     def touch_domain(self, context, domain_id):
  1104.         domain = self.storage.get_domain(context, domain_id)
  1105.  
  1106.         target = {
  1107.             'domain_id': domain_id,
  1108.             'domain_name': domain.name,
  1109.             'tenant_id': domain.tenant_id
  1110.         }
  1111.  
  1112.         policy.check('touch_domain', context, target)
  1113.        
  1114.         self._touch_domain_in_storage(context, domain)
  1115.         #TODO - what about wrapper call ?
  1116.  
  1117.         return domain
  1118.  
  1119.     @transaction
  1120.     def _touch_domain_in_storage(self, context, domain):
  1121.  
  1122.         domain = self._increment_domain_serial(context, domain)
  1123.  
  1124.         return domain
  1125.  
  1126.     # RecordSet Methods
  1127.     @notification('dns.recordset.create')
  1128.     @synchronized_domain()
  1129.     def create_recordset(self, context, domain_id, recordset,
  1130.                          increment_serial=True):
  1131.         domain = self.storage.get_domain(context, domain_id)
  1132.  
  1133.         target = {
  1134.             'domain_id': domain_id,
  1135.             'domain_name': domain.name,
  1136.             'recordset_name': recordset.name,
  1137.             'tenant_id': domain.tenant_id,
  1138.         }
  1139.  
  1140.         policy.check('create_recordset', context, target)
  1141.  
  1142.         recordset = self._create_recordset_in_storage(
  1143.             context, domain, recordset, increment_serial=increment_serial)
  1144.  
  1145.         with wrap_backend_call():
  1146.             self.backend.create_recordset(context, domain, recordset)
  1147.  
  1148.         return self._get_priority(recordset)
  1149.  
  1150.     @transaction
  1151.     def _create_recordset_in_storage(self, context, domain, recordset,
  1152.                                      increment_serial=True):
  1153.  
  1154.         # Ensure the tenant has enough quota to continue
  1155.         self._enforce_recordset_quota(context, domain)
  1156.  
  1157.         # Ensure TTL is above the minimum
  1158.         ttl = getattr(recordset, 'ttl', None)
  1159.         if ttl is not None:
  1160.             self._is_valid_ttl(context, ttl)
  1161.  
  1162.         # Ensure the recordset name and placement is valid
  1163.         self._is_valid_recordset_name(context, domain, recordset.name)
  1164.         self._is_valid_recordset_placement(context, domain, recordset.name,
  1165.                                            recordset.type)
  1166.         self._is_valid_recordset_placement_subdomain(
  1167.             context, domain, recordset.name)
  1168.  
  1169.         if recordset.records and len(recordset.records) > 0:
  1170.             if increment_serial:
  1171.                 domain = self._increment_domain_serial(context, domain)
  1172.  
  1173.             for record in recordset.records:
  1174.                 record.action = 'CREATE'
  1175.                 record.status = 'PENDING'
  1176.                 record.serial = domain.serial
  1177.  
  1178.         recordset = self.storage.create_recordset(context, domain.id,
  1179.                                                   recordset)
  1180.  
  1181.         return recordset
  1182.  
  1183.     def get_recordset(self, context, domain_id, recordset_id):
  1184.         domain = self.storage.get_domain(context, domain_id)
  1185.         recordset = self.storage.get_recordset(context, recordset_id)
  1186.  
  1187.         # Ensure the domain_id matches the record's domain_id
  1188.         if domain.id != recordset.domain_id:
  1189.             raise exceptions.RecordSetNotFound()
  1190.  
  1191.         target = {
  1192.             'domain_id': domain_id,
  1193.             'domain_name': domain.name,
  1194.             'recordset_id': recordset.id,
  1195.             'tenant_id': domain.tenant_id,
  1196.         }
  1197.  
  1198.         policy.check('get_recordset', context, target)
  1199.  
  1200.         # Add the priority to the records
  1201.         recordset = self._get_priority(recordset)
  1202.  
  1203.         return recordset
  1204.  
  1205.     def find_recordsets(self, context, criterion=None, marker=None, limit=None,
  1206.                         sort_key=None, sort_dir=None):
  1207.         target = {'tenant_id': context.tenant}
  1208.         policy.check('find_recordsets', context, target)
  1209.  
  1210.         recordsets = self.storage.find_recordsets(context, criterion, marker,
  1211.                                                   limit, sort_key, sort_dir)
  1212.  
  1213.         # Set the priority for each record
  1214.         for rs in recordsets:
  1215.             rs = self._get_priority(rs)
  1216.  
  1217.         return recordsets
  1218.  
  1219.     def find_recordset(self, context, criterion=None):
  1220.         target = {'tenant_id': context.tenant}
  1221.         policy.check('find_recordset', context, target)
  1222.  
  1223.         recordset = self.storage.find_recordset(context, criterion)
  1224.  
  1225.         # Add the priority to the records
  1226.         recordset = self._get_priority(recordset)
  1227.  
  1228.         return recordset
  1229.  
  1230.     @notification('dns.recordset.update')
  1231.     @synchronized_domain()
  1232.     def update_recordset(self, context, recordset, increment_serial=True):
  1233.         domain_id = recordset.obj_get_original_value('domain_id')
  1234.         domain = self.storage.get_domain(context, domain_id)
  1235.  
  1236.         changes = recordset.obj_get_changes()
  1237.  
  1238.         # Ensure immutable fields are not changed
  1239.         if 'tenant_id' in changes:
  1240.             raise exceptions.BadRequest('Moving a recordset between tenants '
  1241.                                         'is not allowed')
  1242.  
  1243.         if 'domain_id' in changes:
  1244.             raise exceptions.BadRequest('Moving a recordset between domains '
  1245.                                         'is not allowed')
  1246.  
  1247.         if 'type' in changes:
  1248.             raise exceptions.BadRequest('Changing a recordsets type is not '
  1249.                                         'allowed')
  1250.  
  1251.         target = {
  1252.             'domain_id': recordset.obj_get_original_value('domain_id'),
  1253.             'recordset_id': recordset.obj_get_original_value('id'),
  1254.             'domain_name': domain.name,
  1255.             'tenant_id': domain.tenant_id
  1256.         }
  1257.  
  1258.         policy.check('update_recordset', context, target)
  1259.  
  1260.         recordset = self._update_recordset_in_storage(
  1261.             context, domain, recordset, increment_serial=increment_serial)
  1262.  
  1263.         with wrap_backend_call():
  1264.             self.backend.update_recordset(context, domain, recordset)
  1265.  
  1266.         return self._get_priority(recordset)
  1267.  
  1268.     @transaction
  1269.     def _update_recordset_in_storage(self, context, domain, recordset,
  1270.                                      increment_serial=True):
  1271.  
  1272.         changes = recordset.obj_get_changes()
  1273.  
  1274.         # Ensure the record name is valid
  1275.         self._is_valid_recordset_name(context, domain, recordset.name)
  1276.         self._is_valid_recordset_placement(context, domain, recordset.name,
  1277.                                            recordset.type, recordset.id)
  1278.         self._is_valid_recordset_placement_subdomain(
  1279.             context, domain, recordset.name)
  1280.  
  1281.         # Ensure TTL is above the minimum
  1282.         ttl = changes.get('ttl', None)
  1283.         if ttl is not None:
  1284.             self._is_valid_ttl(context, ttl)
  1285.  
  1286.         if increment_serial:
  1287.             domain = self._increment_domain_serial(context, domain)
  1288.  
  1289.         if recordset.records:
  1290.             for record in recordset.records:
  1291.                 #TODO(ghetman) check that shit
  1292.                 #print dir(record)
  1293.                 #if record.action != 'DELETE':
  1294.                 #    record.action = 'UPDATE'
  1295.                 #    record.status = 'PENDING'
  1296.                 #    record.serial = domain.serial
  1297.                 pass
  1298.  
  1299.         # Update the recordset
  1300.         recordset = self.storage.update_recordset(context, recordset)
  1301.  
  1302.         return recordset
  1303.  
  1304.     @notification('dns.recordset.delete')
  1305.     @synchronized_domain()
  1306.     def delete_recordset(self, context, domain_id, recordset_id,
  1307.                          increment_serial=True):
  1308.         domain = self.storage.get_domain(context, domain_id)
  1309.         recordset = self.storage.get_recordset(context, recordset_id)
  1310.  
  1311.         # Ensure the domain_id matches the recordset's domain_id
  1312.         if domain.id != recordset.domain_id:
  1313.             raise exceptions.RecordSetNotFound()
  1314.  
  1315.         target = {
  1316.             'domain_id': domain_id,
  1317.             'domain_name': domain.name,
  1318.             'recordset_id': recordset.id,
  1319.             'tenant_id': domain.tenant_id
  1320.         }
  1321.  
  1322.         policy.check('delete_recordset', context, target)
  1323.  
  1324.         recordset = self._delete_recordset_in_storage(
  1325.             context, domain, recordset, increment_serial=increment_serial)
  1326.  
  1327.         with wrap_backend_call():
  1328.             self.backend.delete_recordset(context, domain, recordset)
  1329.  
  1330.         return recordset
  1331.  
  1332.     @transaction
  1333.     def _delete_recordset_in_storage(self, context, domain, recordset,
  1334.                                      increment_serial=True):
  1335.  
  1336.         if increment_serial:
  1337.             domain = self._increment_domain_serial(context, domain)
  1338.  
  1339.         if recordset.records:
  1340.             for record in recordset.records:
  1341.                 record.action = 'DELETE'
  1342.                 record.status = 'PENDING'
  1343.                 record.serial = domain.serial
  1344.  
  1345.         recordset = self.storage.delete_recordset(context, recordset.id)
  1346.  
  1347.         return recordset
  1348.  
  1349.     def count_recordsets(self, context, criterion=None):
  1350.         if criterion is None:
  1351.             criterion = {}
  1352.  
  1353.         target = {
  1354.             'tenant_id': criterion.get('tenant_id', None)
  1355.         }
  1356.  
  1357.         policy.check('count_recordsets', context, target)
  1358.  
  1359.         return self.storage.count_recordsets(context, criterion)
  1360.  
  1361.     # Record Methods
  1362.     @notification('dns.record.create')
  1363.     @synchronized_domain()
  1364.     def create_record(self, context, domain_id, recordset_id, record,
  1365.                       increment_serial=True):
  1366.         domain = self.storage.get_domain(context, domain_id)
  1367.         recordset = self.storage.get_recordset(context, recordset_id)
  1368.  
  1369.         target = {
  1370.             'domain_id': domain_id,
  1371.             'domain_name': domain.name,
  1372.             'recordset_id': recordset_id,
  1373.             'recordset_name': recordset.name,
  1374.             'tenant_id': domain.tenant_id
  1375.         }
  1376.  
  1377.         policy.check('create_record', context, target)
  1378.  
  1379.         record = self._create_record_in_storage(
  1380.             context, domain, recordset, record,
  1381.             increment_serial=increment_serial)
  1382.  
  1383.         with wrap_backend_call():
  1384.             self.backend.create_record(context, domain, recordset, record)
  1385.  
  1386.         return record
  1387.  
  1388.     @transaction
  1389.     def _create_record_in_storage(self, context, domain, recordset, record,
  1390.                                   increment_serial=True):
  1391.  
  1392.         # Ensure the tenant has enough quota to continue
  1393.         self._enforce_record_quota(context, domain, recordset)
  1394.  
  1395.         if increment_serial:
  1396.             domain = self._increment_domain_serial(context, domain)
  1397.  
  1398.         record.action = 'CREATE'
  1399.         record.status = 'PENDING'
  1400.         record.serial = domain.serial
  1401.  
  1402.         record = self.storage.create_record(context, domain.id, recordset.id,
  1403.                                             record)
  1404.  
  1405.         return record
  1406.  
  1407.     def get_record(self, context, domain_id, recordset_id, record_id):
  1408.         domain = self.storage.get_domain(context, domain_id)
  1409.         recordset = self.storage.get_recordset(context, recordset_id)
  1410.         record = self.storage.get_record(context, record_id)
  1411.  
  1412.         # Ensure the domain_id matches the record's domain_id
  1413.         if domain.id != record.domain_id:
  1414.             raise exceptions.RecordNotFound()
  1415.  
  1416.         # Ensure the recordset_id matches the record's recordset_id
  1417.         if recordset.id != record.recordset_id:
  1418.             raise exceptions.RecordNotFound()
  1419.  
  1420.         target = {
  1421.             'domain_id': domain_id,
  1422.             'domain_name': domain.name,
  1423.             'recordset_id': recordset_id,
  1424.             'recordset_name': recordset.name,
  1425.             'record_id': record.id,
  1426.             'tenant_id': domain.tenant_id
  1427.         }
  1428.  
  1429.         policy.check('get_record', context, target)
  1430.  
  1431.         return record
  1432.  
  1433.     def find_records(self, context, criterion=None, marker=None, limit=None,
  1434.                      sort_key=None, sort_dir=None):
  1435.         target = {'tenant_id': context.tenant}
  1436.         policy.check('find_records', context, target)
  1437.  
  1438.         return self.storage.find_records(context, criterion, marker, limit,
  1439.                                          sort_key, sort_dir)
  1440.  
  1441.     def find_record(self, context, criterion=None):
  1442.         target = {'tenant_id': context.tenant}
  1443.         policy.check('find_record', context, target)
  1444.  
  1445.         return self.storage.find_record(context, criterion)
  1446.  
  1447.     @notification('dns.record.update')
  1448.     @synchronized_domain()
  1449.     def update_record(self, context, record, increment_serial=True):
  1450.         domain_id = record.obj_get_original_value('domain_id')
  1451.         domain = self.storage.get_domain(context, domain_id)
  1452.  
  1453.         recordset_id = record.obj_get_original_value('recordset_id')
  1454.         recordset = self.storage.get_recordset(context, recordset_id)
  1455.  
  1456.         changes = record.obj_get_changes()
  1457.  
  1458.         # Ensure immutable fields are not changed
  1459.         if 'tenant_id' in changes:
  1460.             raise exceptions.BadRequest('Moving a recordset between tenants '
  1461.                                         'is not allowed')
  1462.  
  1463.         if 'domain_id' in changes:
  1464.             raise exceptions.BadRequest('Moving a recordset between domains '
  1465.                                         'is not allowed')
  1466.  
  1467.         if 'recordset_id' in changes:
  1468.             raise exceptions.BadRequest('Moving a recordset between '
  1469.                                         'recordsets is not allowed')
  1470.  
  1471.         target = {
  1472.             'domain_id': record.obj_get_original_value('domain_id'),
  1473.             'domain_name': domain.name,
  1474.             'recordset_id': record.obj_get_original_value('recordset_id'),
  1475.             'recordset_name': recordset.name,
  1476.             'record_id': record.obj_get_original_value('id'),
  1477.             'tenant_id': domain.tenant_id
  1478.         }
  1479.  
  1480.         policy.check('update_record', context, target)
  1481.  
  1482.         record = self._update_record_in_storage(
  1483.             context, domain, record, increment_serial=increment_serial)
  1484.  
  1485.         with wrap_backend_call():
  1486.             self.backend.update_record(context, domain, recordset, record)
  1487.  
  1488.         return record
  1489.  
  1490.     @transaction
  1491.     def _update_record_in_storage(self, context, domain, record,
  1492.                                   increment_serial=True):
  1493.  
  1494.         if increment_serial:
  1495.             domain = self._increment_domain_serial(context, domain)
  1496.  
  1497.         record.action = 'UPDATE'
  1498.         record.status = 'PENDING'
  1499.         record.serial = domain.serial
  1500.  
  1501.         # Update the record
  1502.         record = self.storage.update_record(context, record)
  1503.  
  1504.         return record
  1505.  
  1506.     @notification('dns.record.delete')
  1507.     @synchronized_domain()
  1508.     def delete_record(self, context, domain_id, recordset_id, record_id,
  1509.                       increment_serial=True):
  1510.         domain = self.storage.get_domain(context, domain_id)
  1511.         recordset = self.storage.get_recordset(context, recordset_id)
  1512.         record = self.storage.get_record(context, record_id)
  1513.  
  1514.         # Ensure the domain_id matches the record's domain_id
  1515.         if domain.id != record.domain_id:
  1516.             raise exceptions.RecordNotFound()
  1517.  
  1518.         # Ensure the recordset_id matches the record's recordset_id
  1519.         if recordset.id != record.recordset_id:
  1520.             raise exceptions.RecordNotFound()
  1521.  
  1522.         target = {
  1523.             'domain_id': domain_id,
  1524.             'domain_name': domain.name,
  1525.             'recordset_id': recordset_id,
  1526.             'recordset_name': recordset.name,
  1527.             'record_id': record.id,
  1528.             'tenant_id': domain.tenant_id
  1529.         }
  1530.  
  1531.         policy.check('delete_record', context, target)
  1532.  
  1533.         record = self._delete_record_in_storage(
  1534.             context, domain, record, increment_serial=increment_serial)
  1535.  
  1536.         with wrap_backend_call():
  1537.             self.backend.delete_record(context, domain, recordset, record)
  1538.  
  1539.         return record
  1540.  
  1541.     @transaction
  1542.     def _delete_record_in_storage(self, context, domain, record,
  1543.                                   increment_serial=True):
  1544.  
  1545.         if increment_serial:
  1546.             domain = self._increment_domain_serial(context, domain)
  1547.  
  1548.         record.action = 'DELETE'
  1549.         record.status = 'PENDING'
  1550.         record.serial = domain.serial
  1551.  
  1552.         record = self.storage.update_record(context, record)
  1553.  
  1554.         return record
  1555.  
  1556.     def count_records(self, context, criterion=None):
  1557.         if criterion is None:
  1558.             criterion = {}
  1559.  
  1560.         target = {
  1561.             'tenant_id': criterion.get('tenant_id', None)
  1562.         }
  1563.  
  1564.         policy.check('count_records', context, target)
  1565.         return self.storage.count_records(context, criterion)
  1566.  
  1567.     # Diagnostics Methods
  1568.     def _sync_domain(self, context, domain):
  1569.         recordsets = self.storage.find_recordsets(
  1570.             context, criterion={'domain_id': domain['id']})
  1571.  
  1572.         # Since we now have records as well as recordsets we need to get the
  1573.         # records for it as well and pass that down since the backend wants it.
  1574.         rdata = []
  1575.         for recordset in recordsets:
  1576.             records = self.find_records(
  1577.                 context, {'recordset_id': recordset.id})
  1578.             rdata.append((recordset, records))
  1579.         with wrap_backend_call():
  1580.             return self.backend.sync_domain(context, domain, rdata)
  1581.  
  1582.     @transaction
  1583.     def sync_domains(self, context):
  1584.         policy.check('diagnostics_sync_domains', context)
  1585.  
  1586.         domains = self.storage.find_domains(context)
  1587.  
  1588.         results = {}
  1589.         for domain in domains:
  1590.             results[domain.id] = self._sync_domain(context, domain)
  1591.  
  1592.         return results
  1593.  
  1594.     @transaction
  1595.     def sync_domain(self, context, domain_id):
  1596.         domain = self.storage.get_domain(context, domain_id)
  1597.  
  1598.         target = {
  1599.             'domain_id': domain_id,
  1600.             'domain_name': domain.name,
  1601.             'tenant_id': domain.tenant_id
  1602.         }
  1603.  
  1604.         policy.check('diagnostics_sync_domain', context, target)
  1605.  
  1606.         return self._sync_domain(context, domain)
  1607.  
  1608.     @transaction
  1609.     def sync_record(self, context, domain_id, recordset_id, record_id):
  1610.         domain = self.storage.get_domain(context, domain_id)
  1611.         recordset = self.storage.get_recordset(context, recordset_id)
  1612.  
  1613.         target = {
  1614.             'domain_id': domain_id,
  1615.             'domain_name': domain.name,
  1616.             'recordset_id': recordset_id,
  1617.             'recordset_name': recordset.name,
  1618.             'record_id': record_id,
  1619.             'tenant_id': domain.tenant_id
  1620.         }
  1621.  
  1622.         policy.check('diagnostics_sync_record', context, target)
  1623.  
  1624.         record = self.storage.get_record(context, record_id)
  1625.  
  1626.         with wrap_backend_call():
  1627.             return self.backend.sync_record(context, domain, recordset, record)
  1628.  
  1629.     def ping(self, context):
  1630.         policy.check('diagnostics_ping', context)
  1631.  
  1632.         try:
  1633.             backend_status = self.backend.ping(context)
  1634.         except Exception as e:
  1635.             backend_status = {'status': False, 'message': str(e)}
  1636.  
  1637.         try:
  1638.             storage_status = self.storage.ping(context)
  1639.         except Exception as e:
  1640.             storage_status = {'status': False, 'message': str(e)}
  1641.  
  1642.         if backend_status and storage_status:
  1643.             status = True
  1644.         else:
  1645.             status = False
  1646.  
  1647.         return {
  1648.             'host': cfg.CONF.host,
  1649.             'status': status,
  1650.             'backend': backend_status,
  1651.             'storage': storage_status
  1652.         }
  1653.  
  1654.     def _determine_floatingips(self, context, fips, records=None,
  1655.                                tenant_id=None):
  1656.         """
  1657.        Given the context or tenant, records and fips it returns the valid
  1658.        floatingips either with a associated record or not. Deletes invalid
  1659.        records also.
  1660.  
  1661.        Returns a list of tuples with FloatingIPs and it's Record.
  1662.        """
  1663.         tenant_id = tenant_id or context.tenant
  1664.  
  1665.         elevated_context = context.elevated()
  1666.         elevated_context.all_tenants = True
  1667.  
  1668.         criterion = {
  1669.             'managed': True,
  1670.             'managed_resource_type': 'ptr:floatingip',
  1671.         }
  1672.  
  1673.         records = self.find_records(elevated_context, criterion)
  1674.         records = dict([(r['managed_extra'], r) for r in records])
  1675.  
  1676.         invalid = []
  1677.         data = {}
  1678.         # First populate the list of FIPS
  1679.         for fip_key, fip_values in fips.items():
  1680.             # Check if the FIP has a record
  1681.             record = records.get(fip_values['address'])
  1682.  
  1683.             # NOTE: Now check if it's owned by the tenant that actually has the
  1684.             # FIP in the external service and if not invalidate it (delete it)
  1685.             # thus not returning it with in the tuple with the FIP, but None..
  1686.  
  1687.             if record:
  1688.                 record_tenant = record['managed_tenant_id']
  1689.  
  1690.                 if record_tenant != tenant_id:
  1691.                     msg = "Invalid FloatingIP %s belongs to %s but record " \
  1692.                           "owner %s"
  1693.                     LOG.debug(msg, fip_key, tenant_id, record_tenant)
  1694.  
  1695.                     invalid.append(record)
  1696.                     record = None
  1697.             data[fip_key] = (fip_values, record)
  1698.  
  1699.         return data, invalid
  1700.  
  1701.     def _invalidate_floatingips(self, context, records):
  1702.         """
  1703.        Utility method to delete a list of records.
  1704.        """
  1705.         elevated_context = context.elevated()
  1706.         elevated_context.all_tenants = True
  1707.  
  1708.         if records > 0:
  1709.             for r in records:
  1710.                 msg = 'Deleting record %s for FIP %s'
  1711.                 LOG.debug(msg, r['id'], r['managed_resource_id'])
  1712.                 self.delete_record(elevated_context, r['domain_id'],
  1713.                                    r['recordset_id'], r['id'])
  1714.  
  1715.     def _format_floatingips(self, context, data, recordsets=None):
  1716.         """
  1717.        Given a list of FloatingIP and Record tuples we look through creating
  1718.        a new dict of FloatingIPs
  1719.        """
  1720.         elevated_context = context.elevated()
  1721.         elevated_context.all_tenants = True
  1722.  
  1723.         fips = {}
  1724.         for key, value in data.items():
  1725.             fip_ptr = {
  1726.                 'address': value[0]['address'],
  1727.                 'id': value[0]['id'],
  1728.                 'region': value[0]['region'],
  1729.                 'ptrdname': None,
  1730.                 'ttl': None,
  1731.                 'description': None
  1732.             }
  1733.  
  1734.             # TTL population requires a present record in order to find the
  1735.             # RS or Zone
  1736.             if value[1]:
  1737.                 # We can have a recordset dict passed in
  1738.                 if (recordsets is not None and
  1739.                         value[1]['recordset_id'] in recordsets):
  1740.                     recordset = recordsets[value[1]['recordset_id']]
  1741.                 else:
  1742.                     recordset = self.storage.get_recordset(
  1743.                         elevated_context, value[1]['recordset_id'])
  1744.  
  1745.                 if recordset['ttl'] is not None:
  1746.                     fip_ptr['ttl'] = recordset['ttl']
  1747.                 else:
  1748.                     zone = self.get_domain(
  1749.                         elevated_context, value[1]['domain_id'])
  1750.                     fip_ptr['ttl'] = zone['ttl']
  1751.  
  1752.                 fip_ptr['ptrdname'] = value[1]['data']
  1753.             else:
  1754.                 LOG.debug("No record information found for %s" %
  1755.                           value[0]['id'])
  1756.  
  1757.             # Store the "fip_record" with the region and it's id as key
  1758.             fips[key] = fip_ptr
  1759.         return fips
  1760.  
  1761.     def _list_floatingips(self, context, region=None):
  1762.         data = self.network_api.list_floatingips(context, region=region)
  1763.         return self._list_to_dict(data, keys=['region', 'id'])
  1764.  
  1765.     def _list_to_dict(self, data, keys=['id']):
  1766.         new = {}
  1767.         for i in data:
  1768.             key = tuple([i[key] for key in keys])
  1769.             new[key] = i
  1770.         return new
  1771.  
  1772.     def _get_floatingip(self, context, region, floatingip_id, fips):
  1773.         if (region, floatingip_id) not in fips:
  1774.             msg = 'FloatingIP %s in %s is not associated for tenant "%s"' % \
  1775.                 (floatingip_id, region, context.tenant)
  1776.             raise exceptions.NotFound(msg)
  1777.         return fips[region, floatingip_id]
  1778.  
  1779.     # PTR ops
  1780.     def list_floatingips(self, context):
  1781.         """
  1782.        List Floating IPs PTR
  1783.  
  1784.        A) We have service_catalog in the context and do a lookup using the
  1785.               token pr Neutron in the SC
  1786.        B) We lookup FIPs using the configured values for this deployment.
  1787.        """
  1788.         elevated_context = context.elevated()
  1789.         elevated_context.all_tenants = True
  1790.  
  1791.         tenant_fips = self._list_floatingips(context)
  1792.  
  1793.         valid, invalid = self._determine_floatingips(
  1794.             elevated_context, tenant_fips)
  1795.  
  1796.         self._invalidate_floatingips(context, invalid)
  1797.  
  1798.         return self._format_floatingips(context, valid).values()
  1799.  
  1800.     def get_floatingip(self, context, region, floatingip_id):
  1801.         """
  1802.        Get Floating IP PTR
  1803.        """
  1804.         elevated_context = context.elevated()
  1805.         elevated_context.all_tenants = True
  1806.  
  1807.         tenant_fips = self._list_floatingips(context, region=region)
  1808.  
  1809.         self._get_floatingip(context, region, floatingip_id, tenant_fips)
  1810.  
  1811.         valid, invalid = self._determine_floatingips(
  1812.             elevated_context, tenant_fips)
  1813.  
  1814.         self._invalidate_floatingips(context, invalid)
  1815.  
  1816.         mangled = self._format_floatingips(context, valid)
  1817.         return mangled[region, floatingip_id]
  1818.  
  1819.     def _set_floatingip_reverse(self, context, region, floatingip_id, values):
  1820.         """
  1821.        Set the FloatingIP's PTR record based on values.
  1822.        """
  1823.         values.setdefault('description', None)
  1824.  
  1825.         elevated_context = context.elevated()
  1826.         elevated_context.all_tenants = True
  1827.  
  1828.         tenant_fips = self._list_floatingips(context, region=region)
  1829.  
  1830.         fip = self._get_floatingip(context, region, floatingip_id, tenant_fips)
  1831.  
  1832.         zone_name = self.network_api.address_zone(fip['address'])
  1833.  
  1834.         # NOTE: Find existing zone or create it..
  1835.         try:
  1836.             zone = self.storage.find_domain(
  1837.                 elevated_context, {'name': zone_name})
  1838.         except exceptions.DomainNotFound:
  1839.             msg = _LI('Creating zone for %(fip_id)s:%(region)s - '
  1840.                       '%(fip_addr)s zone %(zonename)s') % \
  1841.                     {'fip_id': floatingip_id, 'region': region,
  1842.                     'fip_addr': fip['address'], 'zonename': zone_name}
  1843.             LOG.info(msg)
  1844.  
  1845.             email = cfg.CONF['service:central'].managed_resource_email
  1846.             tenant_id = cfg.CONF['service:central'].managed_resource_tenant_id
  1847.  
  1848.             zone_values = {
  1849.                 'name': zone_name,
  1850.                 'email': email,
  1851.                 'tenant_id': tenant_id
  1852.             }
  1853.  
  1854.             zone = self.create_domain(
  1855.                 elevated_context, objects.Domain(**zone_values))
  1856.  
  1857.         record_name = self.network_api.address_name(fip['address'])
  1858.  
  1859.         try:
  1860.             # NOTE: Delete the current recormdset if any (also purges records)
  1861.             LOG.debug("Removing old RRset / Record")
  1862.             rset = self.find_recordset(
  1863.                 elevated_context, {'name': record_name, 'type': 'PTR'})
  1864.  
  1865.             records = self.find_records(
  1866.                 elevated_context, {'recordset_id': rset['id']})
  1867.  
  1868.             for record in records:
  1869.                 self.delete_record(
  1870.                     elevated_context,
  1871.                     rset['domain_id'],
  1872.                     rset['id'],
  1873.                     record['id'])
  1874.             self.delete_recordset(elevated_context, zone['id'], rset['id'])
  1875.         except exceptions.RecordSetNotFound:
  1876.             pass
  1877.  
  1878.         recordset_values = {
  1879.             'name': record_name,
  1880.             'type': 'PTR',
  1881.             'ttl': values.get('ttl', None),
  1882.         }
  1883.  
  1884.         recordset = self.create_recordset(
  1885.             elevated_context,
  1886.             zone['id'],
  1887.             objects.RecordSet(**recordset_values))
  1888.  
  1889.         record_values = {
  1890.             'data': values['ptrdname'],
  1891.             'description': values['description'],
  1892.             'managed': True,
  1893.             'managed_extra': fip['address'],
  1894.             'managed_resource_id': floatingip_id,
  1895.             'managed_resource_region': region,
  1896.             'managed_resource_type': 'ptr:floatingip',
  1897.             'managed_tenant_id': context.tenant
  1898.         }
  1899.  
  1900.         record = self.create_record(
  1901.             elevated_context,
  1902.             zone['id'],
  1903.             recordset['id'],
  1904.             objects.Record(**record_values))
  1905.  
  1906.         mangled = self._format_floatingips(
  1907.             context, {(region, floatingip_id): (fip, record)},
  1908.             {recordset['id']: recordset})
  1909.  
  1910.         return mangled[region, floatingip_id]
  1911.  
  1912.     def _unset_floatingip_reverse(self, context, region, floatingip_id):
  1913.         """
  1914.        Unset the FloatingIP PTR record based on the
  1915.  
  1916.        Service's FloatingIP ID > managed_resource_id
  1917.        Tenant ID > managed_tenant_id
  1918.  
  1919.        We find the record based on the criteria and delete it or raise.
  1920.        """
  1921.         elevated_context = context.elevated()
  1922.         elevated_context.all_tenants = True
  1923.  
  1924.         criterion = {
  1925.             'managed_resource_id': floatingip_id,
  1926.             'managed_tenant_id': context.tenant
  1927.         }
  1928.  
  1929.         try:
  1930.             record = self.storage.find_record(
  1931.                 elevated_context, criterion=criterion)
  1932.         except exceptions.RecordNotFound:
  1933.             msg = 'No such FloatingIP %s:%s' % (region, floatingip_id)
  1934.             raise exceptions.NotFound(msg)
  1935.  
  1936.         self.delete_record(
  1937.             elevated_context,
  1938.             record['domain_id'],
  1939.             record['recordset_id'],
  1940.             record['id'])
  1941.  
  1942.     @transaction
  1943.     def update_floatingip(self, context, region, floatingip_id, values):
  1944.         """
  1945.        We strictly see if values['ptrdname'] is str or None and set / unset
  1946.        the requested FloatingIP's PTR record based on that.
  1947.        """
  1948.         if values['ptrdname'] is None:
  1949.             self._unset_floatingip_reverse(context, region, floatingip_id)
  1950.         elif isinstance(values['ptrdname'], basestring):
  1951.             return self._set_floatingip_reverse(
  1952.                 context, region, floatingip_id, values)
  1953.  
  1954.     # Blacklisted Domains
  1955.     @notification('dns.blacklist.create')
  1956.     @transaction
  1957.     def create_blacklist(self, context, blacklist):
  1958.         policy.check('create_blacklist', context)
  1959.  
  1960.         created_blacklist = self.storage.create_blacklist(context, blacklist)
  1961.  
  1962.         return created_blacklist
  1963.  
  1964.     def get_blacklist(self, context, blacklist_id):
  1965.         policy.check('get_blacklist', context)
  1966.  
  1967.         blacklist = self.storage.get_blacklist(context, blacklist_id)
  1968.  
  1969.         return blacklist
  1970.  
  1971.     def find_blacklists(self, context, criterion=None, marker=None,
  1972.                         limit=None, sort_key=None, sort_dir=None):
  1973.         policy.check('find_blacklists', context)
  1974.  
  1975.         blacklists = self.storage.find_blacklists(context, criterion,
  1976.                                                   marker, limit,
  1977.                                                   sort_key, sort_dir)
  1978.  
  1979.         return blacklists
  1980.  
  1981.     def find_blacklist(self, context, criterion):
  1982.         policy.check('find_blacklist', context)
  1983.  
  1984.         blacklist = self.storage.find_blacklist(context, criterion)
  1985.  
  1986.         return blacklist
  1987.  
  1988.     @notification('dns.blacklist.update')
  1989.     @transaction
  1990.     def update_blacklist(self, context, blacklist):
  1991.         target = {
  1992.             'blacklist_id': blacklist.id,
  1993.         }
  1994.         policy.check('update_blacklist', context, target)
  1995.  
  1996.         blacklist = self.storage.update_blacklist(context, blacklist)
  1997.  
  1998.         return blacklist
  1999.  
  2000.     @notification('dns.blacklist.delete')
  2001.     @transaction
  2002.     def delete_blacklist(self, context, blacklist_id):
  2003.         policy.check('delete_blacklist', context)
  2004.  
  2005.         blacklist = self.storage.delete_blacklist(context, blacklist_id)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement