ovsdb_subordinate: Flip requires and provides

Across the OpenStack Engineering team charms we are using requires
and provides for subordinates in the opposite direction of what
is documented in Juju.

Given the end user experience is unaffected I'll bury this detail
for the sake of consistency across the charms.

Change-Id: Ia304cd25dbdd130338837f149a79efe59681f794
This commit is contained in:
Frode Nordahl 2020-02-05 17:37:31 +01:00
parent 41235cd678
commit 78d6c208b4
No known key found for this signature in database
GPG Key ID: 6A5D59A3BA48373F
4 changed files with 282 additions and 282 deletions

View File

@ -12,6 +12,8 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import subprocess
import charms.reactive as reactive import charms.reactive as reactive
# the reactive framework unfortunately does not grok `import as` in conjunction # the reactive framework unfortunately does not grok `import as` in conjunction
@ -27,107 +29,75 @@ from .ovsdb_subordinate_common import hash_hexdigest
class OVSDBSubordinateProvides(Endpoint): class OVSDBSubordinateProvides(Endpoint):
"""This interface is used on a principle charm to connect to subordinate """This interface is used on the subordinate side of the relation"""
"""
@property def _get_ovs_value(self, tbl, col, rec=None):
def chassis_name(self): """Get value of column in record in table
"""Retrieve chassis-name from relation data
:returns: Chassis name as provided on subordinate relation :param tbl: Name of table
:rtype: str :type tbl: str
:param col: Name of column
:type col: str
:param rec: Record ID
:type rec: Optional[str]
:raises: subprocess.CalledProcessError
""" """
return self.all_joined_units.received.get('chassis-name', '') cp = subprocess.run(('ovs-vsctl', 'get', tbl, rec or '.', col),
stdout=subprocess.PIPE,
check=True, universal_newlines=True)
return cp.stdout.rstrip().replace('"', '').replace("'", '')
@property def publish_chassis_name(self):
def ovn_configured(self): """Publish chassis name"""
"""Retrieve whether OVN is configured from relation data ovs_hostname = self._get_ovs_value('Open_vSwitch',
'external_ids:hostname')
:returns: True or False
:rtype: bool
"""
return self.all_joined_units.received.get('ovn-configured', False)
def _add_interface_request(self, bridge, ifname, ifdata):
"""Retrieve interface requests from relation and add/update requests
:param bridge: Name of bridge
:type bridge: str
:param ifname: Name of interface
:type ifname: str
:param ifdata: Data to be attached to interface in Open vSwitch
:type ifdata: Dict[str,Union[str,Dict[str,str]]]
"""
for relation in self.relations: for relation in self.relations:
relation_ifs = relation.to_publish.get('create-interfaces', {}) relation.to_publish['chassis-name'] = ovs_hostname
relation_ifs.update({bridge: {ifname: ifdata}})
relation.to_publish['create-interfaces'] = relation_ifs
def _interface_requests(self): def publish_ovn_configured(self):
"""Retrieve interface requests from relation """Publish whether OVN is configured in the local OVSDB"""
ovn_configured = False
try:
self._get_ovs_value('Open_vSwitch', 'external_ids:ovn-remote')
ovn_configured = True
except subprocess.CalledProcessError:
# No OVN
pass
for relation in self.relations:
relation.to_publish['ovn-configured'] = ovn_configured
@property
def interface_requests(self):
"""Retrieve current interface requests
:returns: Current interface requests :returns: Current interface requests
:rtype: Optional[Dict[str,Union[str,Dict[str,str]]]] :rtype: Dict[str,Union[str,Dict[str,str]]]
""" """
return self.all_joined_units.received.get('create-interfaces', {})
def interface_requests_handled(self):
"""Notify peer that interface requests has been dealt with
Sets a hash of request data back on relation to signal to the other end
it has been dealt with so it can proceed.
Note that we do not use the reactive request response pattern library
as we do not have use for per-unit granularity and we do not have
actual useful data to return.
"""
# The raw data is a json dump using sorted keys
ifreq_hexdigest = hash_hexdigest(
self.all_joined_units.received_raw['create-interfaces'])
for relation in self.relations: for relation in self.relations:
return relation.to_publish_raw.get('create-interfaces') relation.to_publish['interfaces-created'] = ifreq_hexdigest
def create_interface(self, bridge, ifname, ethaddr, ifid,
iftype=None, ifstatus=None):
"""Request system interface created and attach it to CMS
Calls to this function are additive so a principle charm can request to
have multiple interfaces created and maintained.
The flag {endpoint_name}.{interface_name}.created will be set when
ready.
:param bridge: Bridge the new interface should be created on
:type bridge: str
:param ifname: Interface name we want the new netdev to get
:type ifname: str
:param ethaddr: Ethernet address we want to attach to the netdev
:type ethaddr: str
:param ifid: Unique identifier for port from CMS
:type ifid: str
:param iftype: Interface type, defaults to 'internal'
:type iftype: Optional[str]
:param ifstatus: Interface status, defaults to 'active'
:type ifstatus: Optional[str]
"""
# The keys in the ifdata dictionary map directly to column names in the
# OpenvSwitch Interface table as defined in DB-SCHEMA [0] referenced in
# RFC 7047 [1]
#
# There are some established conventions for keys in the external-ids
# column of various tables, consult the OVS Integration Guide [2] for
# more details.
#
# NOTE(fnordahl): Technically the ``external-ids`` column is called
# ``external_ids`` (with an underscore) and we rely on ``ovs-vsctl``'s
# behaviour of transforming dashes to underscores for us [3] so we can
# have a more pleasant data structure.
#
# 0: http://www.openvswitch.org/ovs-vswitchd.conf.db.5.pdf
# 1: https://tools.ietf.org/html/rfc7047
# 2: http://docs.openvswitch.org/en/latest/topics/integration/
# 3: https://github.com/openvswitch/ovs/blob/
# 20dac08fdcce4b7fda1d07add3b346aa9751cfbc/
# lib/db-ctl-base.c#L189-L215
ifdata = {
'type': iftype or 'internal',
'external-ids': {
'iface-id': ifid,
'iface-status': ifstatus or 'active',
'attached-mac': ethaddr,
},
}
self._add_interface_request(bridge, ifname, ifdata)
reactive.clear_flag( reactive.clear_flag(
self.expand_name('{endpoint_name}.interfaces.created')) self.expand_name('{endpoint_name}.interfaces.new_requests'))
@when('endpoint.{endpoint_name}.joined') @when('endpoint.{endpoint_name}.joined')
def joined(self): def joined(self):
self.publish_chassis_name()
self.publish_ovn_configured()
reactive.set_flag(self.expand_name('{endpoint_name}.connected')) reactive.set_flag(self.expand_name('{endpoint_name}.connected'))
reactive.set_flag(self.expand_name('{endpoint_name}.available')) reactive.set_flag(self.expand_name('{endpoint_name}.available'))
@ -136,14 +106,10 @@ class OVSDBSubordinateProvides(Endpoint):
reactive.clear_flag(self.expand_name('{endpoint_name}.available')) reactive.clear_flag(self.expand_name('{endpoint_name}.available'))
reactive.clear_flag(self.expand_name('{endpoint_name}.connected')) reactive.clear_flag(self.expand_name('{endpoint_name}.connected'))
@when('endpoint.{endpoint_name}.changed.interfaces-created') @when('endpoint.{endpoint_name}.changed.create-interfaces')
def new_requests(self): def new_requests(self):
ifreq = self._interface_requests() reactive.set_flag(
self.expand_name('{endpoint_name}.interfaces.new_requests'))
if ifreq is not None and self.all_joined_units.received[ reactive.clear_flag(
'interfaces-created'] == hash_hexdigest(ifreq): self.expand_name(
reactive.set_flag( 'endpoint.{endpoint_name}.changed.create-interfaces'))
self.expand_name('{endpoint_name}.interfaces.created'))
reactive.clear_flag(
self.expand_name(
'endpoint.{endpoint_name}.changed.interfaces-created'))

View File

@ -12,8 +12,6 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import subprocess
import charms.reactive as reactive import charms.reactive as reactive
# the reactive framework unfortunately does not grok `import as` in conjunction # the reactive framework unfortunately does not grok `import as` in conjunction
@ -29,75 +27,107 @@ from .ovsdb_subordinate_common import hash_hexdigest
class OVSDBSubordinateRequires(Endpoint): class OVSDBSubordinateRequires(Endpoint):
"""This interface is used on the subordinate side of the relation""" """This interface is used on a principle charm to connect to subordinate
"""
def _get_ovs_value(self, tbl, col, rec=None):
"""Get value of column in record in table
:param tbl: Name of table
:type tbl: str
:param col: Name of column
:type col: str
:param rec: Record ID
:type rec: Optional[str]
:raises: subprocess.CalledProcessError
"""
cp = subprocess.run(('ovs-vsctl', 'get', tbl, rec or '.', col),
stdout=subprocess.PIPE,
check=True, universal_newlines=True)
return cp.stdout.rstrip().replace('"', '').replace("'", '')
def publish_chassis_name(self):
"""Publish chassis name"""
ovs_hostname = self._get_ovs_value('Open_vSwitch',
'external_ids:hostname')
for relation in self.relations:
relation.to_publish['chassis-name'] = ovs_hostname
def publish_ovn_configured(self):
"""Publish whether OVN is configured in the local OVSDB"""
ovn_configured = False
try:
self._get_ovs_value('Open_vSwitch', 'external_ids:ovn-remote')
ovn_configured = True
except subprocess.CalledProcessError:
# No OVN
pass
for relation in self.relations:
relation.to_publish['ovn-configured'] = ovn_configured
@property @property
def interface_requests(self): def chassis_name(self):
"""Retrieve current interface requests """Retrieve chassis-name from relation data
:returns: Chassis name as provided on subordinate relation
:rtype: str
"""
return self.all_joined_units.received.get('chassis-name', '')
@property
def ovn_configured(self):
"""Retrieve whether OVN is configured from relation data
:returns: True or False
:rtype: bool
"""
return self.all_joined_units.received.get('ovn-configured', False)
def _add_interface_request(self, bridge, ifname, ifdata):
"""Retrieve interface requests from relation and add/update requests
:param bridge: Name of bridge
:type bridge: str
:param ifname: Name of interface
:type ifname: str
:param ifdata: Data to be attached to interface in Open vSwitch
:type ifdata: Dict[str,Union[str,Dict[str,str]]]
"""
for relation in self.relations:
relation_ifs = relation.to_publish.get('create-interfaces', {})
relation_ifs.update({bridge: {ifname: ifdata}})
relation.to_publish['create-interfaces'] = relation_ifs
def _interface_requests(self):
"""Retrieve interface requests from relation
:returns: Current interface requests :returns: Current interface requests
:rtype: Dict[str,Union[str,Dict[str,str]]] :rtype: Optional[Dict[str,Union[str,Dict[str,str]]]]
""" """
return self.all_joined_units.received.get('create-interfaces', {})
def interface_requests_handled(self):
"""Notify peer that interface requests has been dealt with
Sets a hash of request data back on relation to signal to the other end
it has been dealt with so it can proceed.
Note that we do not use the reactive request response pattern library
as we do not have use for per-unit granularity and we do not have
actual useful data to return.
"""
# The raw data is a json dump using sorted keys
ifreq_hexdigest = hash_hexdigest(
self.all_joined_units.received_raw['create-interfaces'])
for relation in self.relations: for relation in self.relations:
relation.to_publish['interfaces-created'] = ifreq_hexdigest return relation.to_publish_raw.get('create-interfaces')
def create_interface(self, bridge, ifname, ethaddr, ifid,
iftype=None, ifstatus=None):
"""Request system interface created and attach it to CMS
Calls to this function are additive so a principle charm can request to
have multiple interfaces created and maintained.
The flag {endpoint_name}.{interface_name}.created will be set when
ready.
:param bridge: Bridge the new interface should be created on
:type bridge: str
:param ifname: Interface name we want the new netdev to get
:type ifname: str
:param ethaddr: Ethernet address we want to attach to the netdev
:type ethaddr: str
:param ifid: Unique identifier for port from CMS
:type ifid: str
:param iftype: Interface type, defaults to 'internal'
:type iftype: Optional[str]
:param ifstatus: Interface status, defaults to 'active'
:type ifstatus: Optional[str]
"""
# The keys in the ifdata dictionary map directly to column names in the
# OpenvSwitch Interface table as defined in DB-SCHEMA [0] referenced in
# RFC 7047 [1]
#
# There are some established conventions for keys in the external-ids
# column of various tables, consult the OVS Integration Guide [2] for
# more details.
#
# NOTE(fnordahl): Technically the ``external-ids`` column is called
# ``external_ids`` (with an underscore) and we rely on ``ovs-vsctl``'s
# behaviour of transforming dashes to underscores for us [3] so we can
# have a more pleasant data structure.
#
# 0: http://www.openvswitch.org/ovs-vswitchd.conf.db.5.pdf
# 1: https://tools.ietf.org/html/rfc7047
# 2: http://docs.openvswitch.org/en/latest/topics/integration/
# 3: https://github.com/openvswitch/ovs/blob/
# 20dac08fdcce4b7fda1d07add3b346aa9751cfbc/
# lib/db-ctl-base.c#L189-L215
ifdata = {
'type': iftype or 'internal',
'external-ids': {
'iface-id': ifid,
'iface-status': ifstatus or 'active',
'attached-mac': ethaddr,
},
}
self._add_interface_request(bridge, ifname, ifdata)
reactive.clear_flag( reactive.clear_flag(
self.expand_name('{endpoint_name}.interfaces.new_requests')) self.expand_name('{endpoint_name}.interfaces.created'))
@when('endpoint.{endpoint_name}.joined') @when('endpoint.{endpoint_name}.joined')
def joined(self): def joined(self):
self.publish_chassis_name()
self.publish_ovn_configured()
reactive.set_flag(self.expand_name('{endpoint_name}.connected')) reactive.set_flag(self.expand_name('{endpoint_name}.connected'))
reactive.set_flag(self.expand_name('{endpoint_name}.available')) reactive.set_flag(self.expand_name('{endpoint_name}.available'))
@ -106,10 +136,14 @@ class OVSDBSubordinateRequires(Endpoint):
reactive.clear_flag(self.expand_name('{endpoint_name}.available')) reactive.clear_flag(self.expand_name('{endpoint_name}.available'))
reactive.clear_flag(self.expand_name('{endpoint_name}.connected')) reactive.clear_flag(self.expand_name('{endpoint_name}.connected'))
@when('endpoint.{endpoint_name}.changed.create-interfaces') @when('endpoint.{endpoint_name}.changed.interfaces-created')
def new_requests(self): def new_requests(self):
reactive.set_flag( ifreq = self._interface_requests()
self.expand_name('{endpoint_name}.interfaces.new_requests'))
reactive.clear_flag( if ifreq is not None and self.all_joined_units.received[
self.expand_name( 'interfaces-created'] == hash_hexdigest(ifreq):
'endpoint.{endpoint_name}.changed.create-interfaces')) reactive.set_flag(
self.expand_name('{endpoint_name}.interfaces.created'))
reactive.clear_flag(
self.expand_name(
'endpoint.{endpoint_name}.changed.interfaces-created'))

View File

@ -54,64 +54,73 @@ class TestOVSDBSubordinateProvides(test_utils.PatchHelper):
self._relations.__iter__.return_value = [relation] self._relations.__iter__.return_value = [relation]
return relation.to_publish return relation.to_publish
def test_chassis_name(self): def test__get_ovs_value(self):
self.patch_target('_all_joined_units') self.patch_object(provides.subprocess, 'run')
self._all_joined_units.received.get.return_value = 'fakename' cp = mock.MagicMock()
self.assertEquals(self.target.chassis_name, 'fakename') cp.stdout = '"hostname-42"\n'
self._all_joined_units.received.get.assert_called_once_with( self.run.return_value = cp
'chassis-name', '') self.assertEquals(
self.target._get_ovs_value('tbl', 'col'),
'hostname-42')
self.run.assert_called_once_with(
('ovs-vsctl', 'get', 'tbl', '.', 'col'),
stdout=mock.ANY, check=True, universal_newlines=True)
self.run.reset_mock()
self.target._get_ovs_value('tbl', 'col', rec='rec')
self.run.assert_called_once_with(
('ovs-vsctl', 'get', 'tbl', 'rec', 'col'),
stdout=mock.ANY, check=True, universal_newlines=True)
def test_ovn_configured(self): def test_publish_chassis_name(self):
self.patch_target('_all_joined_units') self.patch_target('_get_ovs_value')
self._all_joined_units.received.get.return_value = True
self.assertEquals(self.target.ovn_configured, True)
self._all_joined_units.received.get.assert_called_once_with(
'ovn-configured', False)
def test__add_interface_request(self):
to_publish = self.patch_topublish() to_publish = self.patch_topublish()
to_publish.get.return_value = {} self._get_ovs_value.return_value = 'aHostname'
self.target._add_interface_request('br-ex', 'eth0', {'data': ''}) self.target.publish_chassis_name()
to_publish.__setitem__.assert_called_once_with( to_publish.__setitem__.assert_called_once_with(
'create-interfaces', {'br-ex': {'eth0': {'data': ''}}}) 'chassis-name', 'aHostname')
def test__interfrace_requests(self): def test_publish_ovn_configured(self):
self.patch_target('_relations') self.patch_object(provides, 'subprocess')
relation = mock.MagicMock() self.subprocess.CalledProcessError = Exception
self._relations.__iter__.return_value = [relation] self.patch_target('_get_ovs_value')
relation.to_publish_raw.get.return_value = 'aValue' to_publish = self.patch_topublish()
self.assertEquals(self.target._interface_requests(), 'aValue') self._get_ovs_value.side_effect = Exception
relation.to_publish_raw.get.assert_called_once_with( self.target.publish_ovn_configured()
'create-interfaces') to_publish.__setitem__.assert_called_once_with('ovn-configured', False)
self._get_ovs_value.assert_called_once_with(
'Open_vSwitch', 'external_ids:ovn-remote')
to_publish.__setitem__.reset_mock()
self._get_ovs_value.side_effect = None
self.target.publish_ovn_configured()
to_publish.__setitem__.assert_called_once_with('ovn-configured', True)
def test_create_interface(self): def test_interface_requests(self):
self.patch_target('_add_interface_request') self.patch_target('_all_joined_units')
self._all_joined_units.received.get.return_value = 'fakereq'
self.assertEquals(
self.target.interface_requests, 'fakereq')
def test_interface_requests_handled(self):
self.patch_object(provides, 'hash_hexdigest')
self.hash_hexdigest.return_value = 'fakehash'
self.patch_target('_all_joined_units')
self._all_joined_units.received_raw.__getitem__.return_value = 'ifreq'
to_publish = self.patch_topublish()
self.patch_object(provides.reactive, 'clear_flag') self.patch_object(provides.reactive, 'clear_flag')
ifdata = { self.target.interface_requests_handled()
'type': 'internal', self.hash_hexdigest.assert_called_once_with('ifreq')
'external-ids': { to_publish.__setitem__.assert_called_once_with(
'iface-id': 'fakeuuid', 'interfaces-created', 'fakehash')
'iface-status': 'active',
'attached-mac': 'fakemac',
},
}
self.target.create_interface('br-ex', 'eth0', 'fakemac', 'fakeuuid')
self._add_interface_request.assert_called_once_with(
'br-ex', 'eth0', ifdata)
self.clear_flag.assert_called_once_with( self.clear_flag.assert_called_once_with(
'some-relation.interfaces.created') 'some-relation.interfaces.new_requests')
self._add_interface_request.reset_mock()
ifdata['type'] = 'someothervalue'
ifdata['external-ids']['iface-status'] = 'inactive'
self.target.create_interface('br-ex', 'eth0', 'fakemac', 'fakeuuid',
iftype='someothervalue',
ifstatus='inactive')
self._add_interface_request.assert_called_once_with(
'br-ex', 'eth0', ifdata)
def test_joined(self): def test_joined(self):
self.patch_target('publish_chassis_name')
self.patch_target('publish_ovn_configured')
self.patch_object(provides.reactive, 'set_flag') self.patch_object(provides.reactive, 'set_flag')
self.target.joined() self.target.joined()
self.publish_chassis_name.assert_called_once_with()
self.publish_ovn_configured.assert_called_once_with()
self.set_flag.assert_has_calls([ self.set_flag.assert_has_calls([
mock.call('some-relation.connected'), mock.call('some-relation.connected'),
mock.call('some-relation.available'), mock.call('some-relation.available'),
@ -126,20 +135,10 @@ class TestOVSDBSubordinateProvides(test_utils.PatchHelper):
]) ])
def test_new_requests(self): def test_new_requests(self):
self.patch_target('_interface_requests')
self.patch_target('_all_joined_units')
self.patch_object(provides, 'hash_hexdigest')
self.hash_hexdigest.return_value = 'fakehash'
self._interface_requests.return_value = 'fakerequests'
self.patch_object(provides.reactive, 'set_flag') self.patch_object(provides.reactive, 'set_flag')
self.patch_object(provides.reactive, 'clear_flag') self.patch_object(provides.reactive, 'clear_flag')
self.target.new_requests() self.target.new_requests()
self.hash_hexdigest.assert_called_once_with('fakerequests')
self.assertFalse(self.set_flag.called)
self.assertFalse(self.clear_flag.called)
self._all_joined_units.received.__getitem__.return_value = 'fakehash'
self.target.new_requests()
self.set_flag.assert_called_once_with( self.set_flag.assert_called_once_with(
'some-relation.interfaces.created') 'some-relation.interfaces.new_requests')
self.clear_flag.assert_called_once_with( self.clear_flag.assert_called_once_with(
'endpoint.some-relation.changed.interfaces-created') 'endpoint.some-relation.changed.create-interfaces')

View File

@ -22,7 +22,7 @@ import charms_openstack.test_utils as test_utils
_hook_args = {} _hook_args = {}
class TestOVSDBSubordinateProvides(test_utils.PatchHelper): class TestOVSDBSubordinateRequires(test_utils.PatchHelper):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
@ -54,73 +54,64 @@ class TestOVSDBSubordinateProvides(test_utils.PatchHelper):
self._relations.__iter__.return_value = [relation] self._relations.__iter__.return_value = [relation]
return relation.to_publish return relation.to_publish
def test__get_ovs_value(self): def test_chassis_name(self):
self.patch_object(requires.subprocess, 'run') self.patch_target('_all_joined_units')
cp = mock.MagicMock() self._all_joined_units.received.get.return_value = 'fakename'
cp.stdout = '"hostname-42"\n' self.assertEquals(self.target.chassis_name, 'fakename')
self.run.return_value = cp self._all_joined_units.received.get.assert_called_once_with(
self.assertEquals( 'chassis-name', '')
self.target._get_ovs_value('tbl', 'col'),
'hostname-42')
self.run.assert_called_once_with(
('ovs-vsctl', 'get', 'tbl', '.', 'col'),
stdout=mock.ANY, check=True, universal_newlines=True)
self.run.reset_mock()
self.target._get_ovs_value('tbl', 'col', rec='rec')
self.run.assert_called_once_with(
('ovs-vsctl', 'get', 'tbl', 'rec', 'col'),
stdout=mock.ANY, check=True, universal_newlines=True)
def test_publish_chassis_name(self): def test_ovn_configured(self):
self.patch_target('_get_ovs_value') self.patch_target('_all_joined_units')
self._all_joined_units.received.get.return_value = True
self.assertEquals(self.target.ovn_configured, True)
self._all_joined_units.received.get.assert_called_once_with(
'ovn-configured', False)
def test__add_interface_request(self):
to_publish = self.patch_topublish() to_publish = self.patch_topublish()
self._get_ovs_value.return_value = 'aHostname' to_publish.get.return_value = {}
self.target.publish_chassis_name() self.target._add_interface_request('br-ex', 'eth0', {'data': ''})
to_publish.__setitem__.assert_called_once_with( to_publish.__setitem__.assert_called_once_with(
'chassis-name', 'aHostname') 'create-interfaces', {'br-ex': {'eth0': {'data': ''}}})
def test_publish_ovn_configured(self): def test__interfrace_requests(self):
self.patch_object(requires, 'subprocess') self.patch_target('_relations')
self.subprocess.CalledProcessError = Exception relation = mock.MagicMock()
self.patch_target('_get_ovs_value') self._relations.__iter__.return_value = [relation]
to_publish = self.patch_topublish() relation.to_publish_raw.get.return_value = 'aValue'
self._get_ovs_value.side_effect = Exception self.assertEquals(self.target._interface_requests(), 'aValue')
self.target.publish_ovn_configured() relation.to_publish_raw.get.assert_called_once_with(
to_publish.__setitem__.assert_called_once_with('ovn-configured', False) 'create-interfaces')
self._get_ovs_value.assert_called_once_with(
'Open_vSwitch', 'external_ids:ovn-remote')
to_publish.__setitem__.reset_mock()
self._get_ovs_value.side_effect = None
self.target.publish_ovn_configured()
to_publish.__setitem__.assert_called_once_with('ovn-configured', True)
def test_interface_requests(self): def test_create_interface(self):
self.patch_target('_all_joined_units') self.patch_target('_add_interface_request')
self._all_joined_units.received.get.return_value = 'fakereq'
self.assertEquals(
self.target.interface_requests, 'fakereq')
def test_interface_requests_handled(self):
self.patch_object(requires, 'hash_hexdigest')
self.hash_hexdigest.return_value = 'fakehash'
self.patch_target('_all_joined_units')
self._all_joined_units.received_raw.__getitem__.return_value = 'ifreq'
to_publish = self.patch_topublish()
self.patch_object(requires.reactive, 'clear_flag') self.patch_object(requires.reactive, 'clear_flag')
self.target.interface_requests_handled() ifdata = {
self.hash_hexdigest.assert_called_once_with('ifreq') 'type': 'internal',
to_publish.__setitem__.assert_called_once_with( 'external-ids': {
'interfaces-created', 'fakehash') 'iface-id': 'fakeuuid',
'iface-status': 'active',
'attached-mac': 'fakemac',
},
}
self.target.create_interface('br-ex', 'eth0', 'fakemac', 'fakeuuid')
self._add_interface_request.assert_called_once_with(
'br-ex', 'eth0', ifdata)
self.clear_flag.assert_called_once_with( self.clear_flag.assert_called_once_with(
'some-relation.interfaces.new_requests') 'some-relation.interfaces.created')
self._add_interface_request.reset_mock()
ifdata['type'] = 'someothervalue'
ifdata['external-ids']['iface-status'] = 'inactive'
self.target.create_interface('br-ex', 'eth0', 'fakemac', 'fakeuuid',
iftype='someothervalue',
ifstatus='inactive')
self._add_interface_request.assert_called_once_with(
'br-ex', 'eth0', ifdata)
def test_joined(self): def test_joined(self):
self.patch_target('publish_chassis_name')
self.patch_target('publish_ovn_configured')
self.patch_object(requires.reactive, 'set_flag') self.patch_object(requires.reactive, 'set_flag')
self.target.joined() self.target.joined()
self.publish_chassis_name.assert_called_once_with()
self.publish_ovn_configured.assert_called_once_with()
self.set_flag.assert_has_calls([ self.set_flag.assert_has_calls([
mock.call('some-relation.connected'), mock.call('some-relation.connected'),
mock.call('some-relation.available'), mock.call('some-relation.available'),
@ -135,10 +126,20 @@ class TestOVSDBSubordinateProvides(test_utils.PatchHelper):
]) ])
def test_new_requests(self): def test_new_requests(self):
self.patch_target('_interface_requests')
self.patch_target('_all_joined_units')
self.patch_object(requires, 'hash_hexdigest')
self.hash_hexdigest.return_value = 'fakehash'
self._interface_requests.return_value = 'fakerequests'
self.patch_object(requires.reactive, 'set_flag') self.patch_object(requires.reactive, 'set_flag')
self.patch_object(requires.reactive, 'clear_flag') self.patch_object(requires.reactive, 'clear_flag')
self.target.new_requests() self.target.new_requests()
self.hash_hexdigest.assert_called_once_with('fakerequests')
self.assertFalse(self.set_flag.called)
self.assertFalse(self.clear_flag.called)
self._all_joined_units.received.__getitem__.return_value = 'fakehash'
self.target.new_requests()
self.set_flag.assert_called_once_with( self.set_flag.assert_called_once_with(
'some-relation.interfaces.new_requests') 'some-relation.interfaces.created')
self.clear_flag.assert_called_once_with( self.clear_flag.assert_called_once_with(
'endpoint.some-relation.changed.create-interfaces') 'endpoint.some-relation.changed.interfaces-created')