Merge "Optimize MiniDNS for fewer syscalls"
This commit is contained in:
commit
b926a1d4df
|
@ -13,25 +13,28 @@
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import shutil
|
|
||||||
import tempfile
|
|
||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
import six
|
||||||
|
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.i18n import _
|
from nova.i18n import _
|
||||||
from nova.network import dns_driver
|
from nova.network import dns_driver
|
||||||
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class MiniDNS(dns_driver.DNSDriver):
|
class MiniDNS(dns_driver.DNSDriver):
|
||||||
"""Trivial DNS driver. This will read/write to a local, flat file
|
"""Trivial DNS driver. This will read/write to either a local,
|
||||||
and have no effect on your actual DNS system. This class is
|
flat file or an in memory StringIO and have no effect on your actual
|
||||||
strictly for testing purposes, and should keep you out of dependency
|
DNS system. This class is strictly for testing purposes, and should
|
||||||
hell.
|
keep you out of dependency hell.
|
||||||
|
|
||||||
|
A file is used when CONF.log_dir is set. This is relevant for when
|
||||||
|
two different DNS driver instances share the same data file.
|
||||||
|
|
||||||
Note that there is almost certainly a race condition here that
|
Note that there is almost certainly a race condition here that
|
||||||
will manifest anytime instances are rapidly created and deleted.
|
will manifest anytime instances are rapidly created and deleted.
|
||||||
|
@ -39,25 +42,23 @@ class MiniDNS(dns_driver.DNSDriver):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
filename = None
|
||||||
if CONF.log_dir:
|
if CONF.log_dir:
|
||||||
self.filename = os.path.join(CONF.log_dir, "dnstest.txt")
|
filename = os.path.join(CONF.log_dir, "dnstest.txt")
|
||||||
self.tempdir = None
|
self.file = open(filename, 'w+')
|
||||||
else:
|
else:
|
||||||
self.tempdir = tempfile.mkdtemp()
|
self.file = six.StringIO()
|
||||||
self.filename = os.path.join(self.tempdir, "dnstest.txt")
|
if not filename or not os.path.exists(filename):
|
||||||
LOG.debug('minidns file is |%s|', self.filename)
|
self.file.write("# minidns\n\n\n")
|
||||||
|
self.file.flush()
|
||||||
if not os.path.exists(self.filename):
|
|
||||||
with open(self.filename, "w+") as f:
|
|
||||||
f.write("# minidns\n\n\n")
|
|
||||||
|
|
||||||
def get_domains(self):
|
def get_domains(self):
|
||||||
entries = []
|
entries = []
|
||||||
with open(self.filename, 'r') as infile:
|
self.file.seek(0)
|
||||||
for line in infile:
|
for line in self.file:
|
||||||
entry = self.parse_line(line)
|
entry = self.parse_line(line)
|
||||||
if entry and entry['address'] == 'domain':
|
if entry and entry['address'] == 'domain':
|
||||||
entries.append(entry['name'])
|
entries.append(entry['name'])
|
||||||
return entries
|
return entries
|
||||||
|
|
||||||
def qualify(self, name, domain):
|
def qualify(self, name, domain):
|
||||||
|
@ -79,9 +80,10 @@ class MiniDNS(dns_driver.DNSDriver):
|
||||||
if self.get_entries_by_name(name, domain):
|
if self.get_entries_by_name(name, domain):
|
||||||
raise exception.FloatingIpDNSExists(name=name, domain=domain)
|
raise exception.FloatingIpDNSExists(name=name, domain=domain)
|
||||||
|
|
||||||
with open(self.filename, 'a+') as outfile:
|
self.file.seek(0, os.SEEK_END)
|
||||||
outfile.write("%s %s %s\n" %
|
self.file.write("%s %s %s\n" %
|
||||||
(address, self.qualify(name, domain), type))
|
(address, self.qualify(name, domain), type))
|
||||||
|
self.file.flush()
|
||||||
|
|
||||||
def parse_line(self, line):
|
def parse_line(self, line):
|
||||||
vals = line.split()
|
vals = line.split()
|
||||||
|
@ -103,17 +105,19 @@ class MiniDNS(dns_driver.DNSDriver):
|
||||||
raise exception.InvalidInput(_("Invalid name"))
|
raise exception.InvalidInput(_("Invalid name"))
|
||||||
|
|
||||||
deleted = False
|
deleted = False
|
||||||
outfile = tempfile.NamedTemporaryFile('w', delete=False)
|
keeps = []
|
||||||
with open(self.filename, 'r') as infile:
|
self.file.seek(0)
|
||||||
for line in infile:
|
for line in self.file:
|
||||||
entry = self.parse_line(line)
|
entry = self.parse_line(line)
|
||||||
if (not entry or
|
if (not entry or
|
||||||
entry['name'] != self.qualify(name, domain)):
|
entry['name'] != self.qualify(name, domain)):
|
||||||
outfile.write(line)
|
keeps.append(line)
|
||||||
else:
|
else:
|
||||||
deleted = True
|
deleted = True
|
||||||
outfile.close()
|
self.file.truncate(0)
|
||||||
shutil.move(outfile.name, self.filename)
|
self.file.seek(0)
|
||||||
|
self.file.write(''.join(keeps))
|
||||||
|
self.file.flush()
|
||||||
if not deleted:
|
if not deleted:
|
||||||
LOG.warning('Cannot delete entry |%s|', self.qualify(name, domain))
|
LOG.warning('Cannot delete entry |%s|', self.qualify(name, domain))
|
||||||
raise exception.NotFound
|
raise exception.NotFound
|
||||||
|
@ -123,76 +127,80 @@ class MiniDNS(dns_driver.DNSDriver):
|
||||||
if not self.get_entries_by_name(name, domain):
|
if not self.get_entries_by_name(name, domain):
|
||||||
raise exception.NotFound
|
raise exception.NotFound
|
||||||
|
|
||||||
outfile = tempfile.NamedTemporaryFile('w', delete=False)
|
lines = []
|
||||||
with open(self.filename, 'r') as infile:
|
self.file.seek(0)
|
||||||
for line in infile:
|
for line in self.file:
|
||||||
entry = self.parse_line(line)
|
entry = self.parse_line(line)
|
||||||
if (entry and
|
if (entry and
|
||||||
entry['name'] == self.qualify(name, domain)):
|
entry['name'] == self.qualify(name, domain)):
|
||||||
outfile.write("%s %s %s\n" %
|
lines.append("%s %s %s\n" %
|
||||||
(address, self.qualify(name, domain), entry['type']))
|
(address, self.qualify(name, domain), entry['type']))
|
||||||
else:
|
else:
|
||||||
outfile.write(line)
|
lines.append(line)
|
||||||
outfile.close()
|
self.file.truncate(0)
|
||||||
shutil.move(outfile.name, self.filename)
|
self.file.seek(0)
|
||||||
|
self.file.write(''.join(lines))
|
||||||
|
self.file.flush()
|
||||||
|
|
||||||
def get_entries_by_address(self, address, domain):
|
def get_entries_by_address(self, address, domain):
|
||||||
entries = []
|
entries = []
|
||||||
with open(self.filename, 'r') as infile:
|
self.file.seek(0)
|
||||||
for line in infile:
|
for line in self.file:
|
||||||
entry = self.parse_line(line)
|
entry = self.parse_line(line)
|
||||||
if entry and entry['address'] == address.lower():
|
if entry and entry['address'] == address.lower():
|
||||||
if entry['name'].endswith(domain.lower()):
|
if entry['name'].endswith(domain.lower()):
|
||||||
name = entry['name'].split(".")[0]
|
name = entry['name'].split(".")[0]
|
||||||
if name not in entries:
|
if name not in entries:
|
||||||
entries.append(name)
|
entries.append(name)
|
||||||
|
|
||||||
return entries
|
return entries
|
||||||
|
|
||||||
def get_entries_by_name(self, name, domain):
|
def get_entries_by_name(self, name, domain):
|
||||||
entries = []
|
entries = []
|
||||||
with open(self.filename, 'r') as infile:
|
self.file.seek(0)
|
||||||
for line in infile:
|
for line in self.file:
|
||||||
entry = self.parse_line(line)
|
entry = self.parse_line(line)
|
||||||
if (entry and
|
if (entry and
|
||||||
entry['name'] == self.qualify(name, domain)):
|
entry['name'] == self.qualify(name, domain)):
|
||||||
entries.append(entry['address'])
|
entries.append(entry['address'])
|
||||||
return entries
|
return entries
|
||||||
|
|
||||||
def delete_dns_file(self):
|
def delete_dns_file(self):
|
||||||
if os.path.exists(self.filename):
|
self.file.close()
|
||||||
try:
|
try:
|
||||||
os.remove(self.filename)
|
if os.path.exists(self.file.name):
|
||||||
except OSError:
|
try:
|
||||||
pass
|
os.remove(self.file.name)
|
||||||
if self.tempdir and os.path.exists(self.tempdir):
|
except OSError:
|
||||||
try:
|
pass
|
||||||
shutil.rmtree(self.tempdir)
|
except AttributeError:
|
||||||
except OSError:
|
# This was a BytesIO, which has no name.
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def create_domain(self, fqdomain):
|
def create_domain(self, fqdomain):
|
||||||
if self.get_entries_by_name(fqdomain, ''):
|
if self.get_entries_by_name(fqdomain, ''):
|
||||||
raise exception.FloatingIpDNSExists(name=fqdomain, domain='')
|
raise exception.FloatingIpDNSExists(name=fqdomain, domain='')
|
||||||
|
|
||||||
with open(self.filename, 'a+') as outfile:
|
self.file.seek(0, os.SEEK_END)
|
||||||
outfile.write("%s %s %s\n" %
|
self.file.write("%s %s %s\n" % ('domain', fqdomain, 'domain'))
|
||||||
('domain', fqdomain, 'domain'))
|
self.file.flush()
|
||||||
|
|
||||||
def delete_domain(self, fqdomain):
|
def delete_domain(self, fqdomain):
|
||||||
deleted = False
|
deleted = False
|
||||||
outfile = tempfile.NamedTemporaryFile('w', delete=False)
|
keeps = []
|
||||||
with open(self.filename, 'r') as infile:
|
self.file.seek(0)
|
||||||
for line in infile:
|
for line in self.file:
|
||||||
entry = self.parse_line(line)
|
entry = self.parse_line(line)
|
||||||
if (not entry or
|
if (not entry or
|
||||||
entry['domain'] != fqdomain.lower()):
|
entry['domain'] != fqdomain.lower()):
|
||||||
outfile.write(line)
|
keeps.append(line)
|
||||||
else:
|
else:
|
||||||
LOG.info("deleted %s", entry)
|
LOG.info("deleted %s", entry)
|
||||||
deleted = True
|
deleted = True
|
||||||
outfile.close()
|
self.file.truncate(0)
|
||||||
shutil.move(outfile.name, self.filename)
|
self.file.seek(0)
|
||||||
|
self.file.write(''.join(keeps))
|
||||||
|
self.file.flush()
|
||||||
if not deleted:
|
if not deleted:
|
||||||
LOG.warning('Cannot delete domain |%s|', fqdomain)
|
LOG.warning('Cannot delete domain |%s|', fqdomain)
|
||||||
raise exception.NotFound
|
raise exception.NotFound
|
||||||
|
|
Loading…
Reference in New Issue