Add DNS APIs

OVN Northbound DB has DNS table. Support CRUD operations for
this table. These APIs will be used by networking-ovn to use
OVN's native DNS feature.

Note: ovn-nbctl do not support these APIs as of now. But it is
good to have these in ovsdbapp and it will be useful.

Partial-bug: #1688172

Change-Id: I5528829eef0a7534d9266bb3abc2aec269e4ba35
This commit is contained in:
Numan Siddique 2017-08-24 13:00:10 +05:30
parent 558783eba2
commit d7e3313b2c
5 changed files with 323 additions and 0 deletions

View File

@ -64,6 +64,34 @@ class API(api.API):
:returns: :class:`Command` with RowView result
"""
@abc.abstractmethod
def ls_set_dns_records(self, switch_uuid, dns_uuids):
"""Sets 'dns_records' column on the switch with uuid 'switch_uuid'
:returns: :class:`Command` with RowView result
"""
@abc.abstractmethod
def ls_clear_dns_records(self, switch):
"""Clears 'dns_records' from the switch with uuid 'switch_uuid'
:returns: :class:`Command` with RowView result
"""
@abc.abstractmethod
def ls_add_dns_record(self, switch_uuid, dns_uuid):
"""Add the 'dns_record' to the switch's 'dns_records' list
:returns: :class:`Command` with RowView result
"""
@abc.abstractmethod
def ls_remove_dns_record(self, switch_uuid, dns_uuid):
"""Remove the 'dns_record' from the switch's 'dns_records' list
:returns: :class:`Command` with RowView result
"""
@abc.abstractmethod
def acl_add(self, switch, direction, priority, match, action, log=False):
"""Add an ACL to 'switch'
@ -657,3 +685,78 @@ class API(api.API):
:type uuid: string or uuid.UUID
:returns: :class:`Command` with dict result
"""
@abc.abstractmethod
def dns_add(self, **external_ids):
"""Create a DNS row with external_ids
:param external_ids: external_id field key/value mapping
:type external_ids: key: string, value: string
:returns: :class:`Command` with RowView result
"""
@abc.abstractmethod
def dns_del(self, uuid):
"""Delete DNS row with 'uuid'
:param uuid: The uuid of the DNS row to delete
:type uuid: string or uuid.UUID
:returns: :class:`Command` with no result
"""
@abc.abstractmethod
def dns_get(self, uuid):
"""Get DNS row with 'uuid'
:returns: :class:`Command` with RowView result
"""
@abc.abstractmethod
def dns_list(self):
"""Get all DNS rows
:returns: :class:`Command with RowView list result
"""
@abc.abstractmethod
def dns_set_records(self, uuid, **records):
"""Sets the 'records' field of the DNS row
:param uuid: The uuid of the DNS row to set the records with
:type uuid: string or uuid.UUID
:param records: keys and values for the DNS 'records' dict
:type records: key: string, value: string
:returns: :class:`Command` with no result
"""
@abc.abstractmethod
def dns_add_record(self, uuid, hostname, ips):
"""Add the record 'hostname: ips' into the records column of the DNS
:param uuid: The uuid of the DNS row to add the record
:type uuid: string or uuid.UUID
:param hostname: hostname as the key to the record dict
:type ips: IPs as the value to the hostname key in the 'records'
:returns: :class:`Command` with no result
"""
@abc.abstractmethod
def dns_remove_record(self, uuid, hostname):
"""Remove the 'hostname' from the 'records' field of the DNS row
:param uuid: The uuid of the DNS row to set the records with
:type uuid: string or uuid.UUID
:param hostname: hostname as the key to the record dict
:returns: :class:`Command` with no result
"""
@abc.abstractmethod
def dns_set_external_ids(self, uuid, **external_ids):
"""Sets the 'external_ids' field of the DNS row
:param uuid: The uuid of the DNS row to set the external_ids with
:type uuid: string or uuid.UUID
:param external_ids: keys and values for the DNS 'external_ids' dict
:type external_ids: key: string, value: string
:returns: :class:`Command` with no result
"""

View File

@ -990,3 +990,64 @@ class LsLbListCommand(cmd.BaseCommand):
def run_idl(self, txn):
ls = self.api.lookup('Logical_Switch', self.switch)
self.result = [rowview.RowView(r) for r in ls.load_balancer]
class DnsAddCommand(cmd.AddCommand):
table_name = 'DNS'
def __init__(self, api, **columns):
super(DnsAddCommand, self).__init__(api)
self.columns = columns
def run_idl(self, txn):
dns = txn.insert(self.api.tables[self.table_name])
# Transaction will not be commited if the row is not initialized with
# any columns.
dns.external_ids = {}
self.set_columns(dns, **self.columns)
self.result = dns.uuid
class DnsDelCommand(cmd.DbDestroyCommand):
def __init__(self, api, uuid):
super(DnsDelCommand, self).__init__(api, 'DNS', uuid)
class DnsGetCommand(cmd.BaseGetRowCommand):
table = 'DNS'
class DnsListCommand(cmd.BaseCommand):
def run_idl(self, txn):
table = self.api.tables['DNS']
self.result = [rowview.RowView(r) for r in table.rows.values()]
class DnsSetRecordsCommand(cmd.BaseCommand):
def __init__(self, api, row_uuid, **records):
super(DnsSetRecordsCommand, self).__init__(api)
self.row_uuid = row_uuid
self.records = records
def run_idl(self, txn):
try:
dns = self.api.lookup('DNS', self.row_uuid)
dns.records = self.records
except idlutils.RowNotFound:
msg = "DNS %s does not exist" % self.row_uuid
raise RuntimeError(msg)
class DnsSetExternalIdsCommand(cmd.BaseCommand):
def __init__(self, api, row_uuid, **external_ids):
super(DnsSetExternalIdsCommand, self).__init__(api)
self.row_uuid = row_uuid
self.external_ids = external_ids
def run_idl(self, txn):
try:
dns = self.api.lookup('DNS', self.row_uuid)
dns.external_ids = self.external_ids
except idlutils.RowNotFound:
msg = "DNS %s does not exist" % self.row_uuid
raise RuntimeError(msg)

View File

@ -15,6 +15,7 @@ from ovsdbapp.backend.ovs_idl import idlutils
from ovsdbapp import constants as const
from ovsdbapp.schema.ovn_northbound import api
from ovsdbapp.schema.ovn_northbound import commands as cmd
from ovsdbapp import utils
class OvnNbApiIdlImpl(ovs_idl.Backend, api.API):
@ -37,6 +38,21 @@ class OvnNbApiIdlImpl(ovs_idl.Backend, api.API):
def ls_get(self, switch):
return cmd.LsGetCommand(self, switch)
def ls_set_dns_records(self, switch_uuid, dns_uuids):
return self.db_set('Logical_Switch', switch_uuid,
('dns_records', dns_uuids))
def ls_clear_dns_records(self, switch_uuid):
return self.db_clear('Logical_Switch', switch_uuid, 'dns_records')
def ls_add_dns_record(self, switch_uuid, dns_uuid):
return self.db_add('Logical_Switch', switch_uuid, 'dns_records',
dns_uuid)
def ls_remove_dns_record(self, switch_uuid, dns_uuid):
return self.db_remove('Logical_Switch', switch_uuid, 'dns_records',
dns_uuid)
def acl_add(self, switch, direction, priority, match, action, log=False,
may_exist=False, **external_ids):
return cmd.AclAddCommand(self, switch, direction, priority,
@ -204,3 +220,29 @@ class OvnNbApiIdlImpl(ovs_idl.Backend, api.API):
def dhcp_options_get_options(self, dhcpopt_uuid):
return cmd.DhcpOptionsGetOptionsCommand(self, dhcpopt_uuid)
def dns_add(self, **columns):
return cmd.DnsAddCommand(self, **columns)
def dns_del(self, uuid):
return cmd.DnsDelCommand(self, uuid)
def dns_get(self, uuid):
return cmd.DnsGetCommand(self, uuid)
def dns_list(self):
return cmd.DnsListCommand(self)
def dns_set_records(self, uuid, **records):
return cmd.DnsSetRecordsCommand(self, uuid, **records)
def dns_add_record(self, uuid, hostname, ips):
if isinstance(ips, list):
ips = " ".join(utils.normalize_ip_port(ip) for ip in ips)
return self.db_add('DNS', uuid, 'records', {hostname: ips})
def dns_remove_record(self, uuid, hostname):
return self.db_remove('DNS', uuid, 'records', hostname)
def dns_set_external_ids(self, uuid, **external_ids):
return cmd.DnsSetExternalIdsCommand(self, uuid, **external_ids)

View File

@ -37,3 +37,10 @@ class LoadBalancerFixture(fixtures.ImplIdlFixture):
api = impl_idl.OvnNbApiIdlImpl
create = 'lb_add'
delete = 'lb_del'
class DnsFixture(fixtures.ImplIdlFixture):
api = impl_idl.OvnNbApiIdlImpl
create = 'dns_add'
delete = 'dns_del'
delete_args = {}

View File

@ -1087,3 +1087,113 @@ class TestCommonDbOps(OvnNorthboundTest):
# should be a NoOp, not fail
self.api.db_remove('Logical_Switch', self.switch.uuid, 'ports',
"badvalue").execute(check_error=True)
class TestDnsOps(OvnNorthboundTest):
def _dns_add(self, *args, **kwargs):
dns = self.useFixture(fixtures.DnsFixture(*args, **kwargs)).obj
return dns
def test_dns_get(self):
dns = self._dns_add()
found = self.api.dns_get(dns.uuid).execute(
check_error=True)
self.assertEqual(dns, found)
def test_dns_get_no_exist(self):
cmd = self.api.dns_get("noexist")
self.assertRaises(idlutils.RowNotFound, cmd.execute, check_error=True)
def test_dns_add(self):
self._dns_add()
def test_dns_add_ext_ids(self):
ext_ids = {'net-id': '1', 'other-id': '2'}
dns = self._dns_add(external_ids=ext_ids)
self.assertEqual(ext_ids, dns.external_ids)
def test_dns_list(self):
dnses = {self._dns_add() for d in range(3)}
dnses_set = set(
self.api.dns_list().execute(check_error=True))
self.assertTrue(dnses.issubset(dnses_set))
def test_dns_set_records(self):
dns = self._dns_add()
records = {'a': 'one', 'b': 'two'}
self.api.dns_set_records(
dns.uuid, **records).execute(check_error=True)
dns = self.api.dns_get(dns.uuid).execute(
check_error=True)
self.assertEqual(records, dns.records)
self.api.dns_set_records(
dns.uuid, **{}).execute(check_error=True)
self.assertEqual({}, dns.records)
def test_dns_set_external_ids(self):
dns = self._dns_add()
external_ids = {'a': 'one', 'b': 'two'}
self.api.dns_set_external_ids(
dns.uuid, **external_ids).execute(check_error=True)
dns = self.api.dns_get(dns.uuid).execute(
check_error=True)
self.assertEqual(external_ids, dns.external_ids)
self.api.dns_set_external_ids(
dns.uuid, **{}).execute(check_error=True)
self.assertEqual({}, dns.external_ids)
def test_dns_add_remove_records(self):
dns = self._dns_add()
self.api.dns_add_record(dns.uuid, 'a', 'one').execute()
self.api.dns_add_record(dns.uuid, 'b', 'two').execute()
dns = self.api.dns_get(dns.uuid).execute(
check_error=True)
records = {'a': 'one', 'b': 'two'}
self.assertEqual(records, dns.records)
self.api.dns_remove_record(dns.uuid, 'a').execute()
records.pop('a')
self.assertEqual(records, dns.records)
self.api.dns_remove_record(dns.uuid, 'b').execute()
self.assertEqual({}, dns.records)
class TestLsDnsOps(OvnNorthboundTest):
def _dns_add(self, *args, **kwargs):
dns = self.useFixture(fixtures.DnsFixture(*args, **kwargs)).obj
return dns
def _ls_add(self, *args, **kwargs):
fix = self.useFixture(fixtures.LogicalSwitchFixture(*args, **kwargs))
return fix.obj
def test_ls_dns_set_clear_records(self):
dns1 = self._dns_add()
dns2 = self._dns_add()
ls1 = self._ls_add('ls1')
self.api.ls_set_dns_records(ls1.uuid, [dns1.uuid, dns2.uuid]).execute()
self.assertItemsEqual([dns1.uuid, dns2.uuid],
[dns.uuid for dns in ls1.dns_records])
self.api.ls_clear_dns_records(ls1.uuid).execute()
self.assertEqual([], ls1.dns_records)
def test_ls_dns_add_remove_records(self):
dns1 = self._dns_add()
dns2 = self._dns_add()
ls1 = self._ls_add('ls1')
self.api.ls_add_dns_record(ls1.uuid, dns1.uuid).execute()
self.assertItemsEqual([dns1.uuid],
[dns.uuid for dns in ls1.dns_records])
self.api.ls_add_dns_record(ls1.uuid, dns2.uuid).execute()
self.assertItemsEqual([dns1.uuid, dns2.uuid],
[dns.uuid for dns in ls1.dns_records])
self.api.ls_remove_dns_record(ls1.uuid, dns2.uuid).execute()
self.assertItemsEqual([dns1.uuid],
[dns.uuid for dns in ls1.dns_records])
self.api.ls_remove_dns_record(ls1.uuid, dns1.uuid).execute()
self.assertEqual([], ls1.dns_records)