Merge lp:~thedac/charm-helpers/access-network into lp:charm-helpers

Proposed by David Ames
Status: Merged
Merged at revision: 736
Proposed branch: lp:~thedac/charm-helpers/access-network
Merge into: lp:charm-helpers
Diff against target: 144 lines (+50/-55)
2 files modified
charmhelpers/contrib/network/ip.py (+23/-16)
tests/contrib/network/test_ip.py (+27/-39)
To merge this branch: bzr merge lp:~thedac/charm-helpers/access-network
Reviewer Review Type Date Requested Status
Alex Kavanagh Approve
charmers Pending
Review via email: mp+322707@code.launchpad.net

Description of the change

Pass an access-network directly to get_relation_ip

get_relation_ip currently has a config parameter config_override.
However, there are times like with share-db when the network override
is not a config option. Allow the option to set an access-network directly.

To post a comment you must log in.
Revision history for this message
Alex Kavanagh (ajkavanagh) wrote :

Is it correct that prefer-ipv6 overrides the access-space? This is more just a comment, to check that I understand the precedence for the various config options. Sorry if a daft question.

Revision history for this message
Alex Kavanagh (ajkavanagh) wrote :

Sorry, an additional comment. How are:

get_relation_id(interface, config_override='access-network')

and

get_relation_id(interface, access_network=config('access-network'))

different? I must be missing something in the intent, or how charms are configured.

Revision history for this message
David Ames (thedac) wrote :

> Is it correct that prefer-ipv6 overrides the access-space? This is more just
> a comment, to check that I understand the precedence for the various config
> options. Sorry if a daft question.

With the current implementation when prefer-ipv6 is set it overrides everything. Or at least that is how it has been implemented in the past.

Going forward we ultimately want IPv6 to just be another space. As soon as we have no Juju 1.x we can just rely on spaces for IPv6 or IPv4.

Revision history for this message
David Ames (thedac) wrote :

> Sorry, an additional comment. How are:
>
> get_relation_id(interface, config_override='access-network')
>
> and
>
> get_relation_id(interface, access_network=config('access-network'))
>
> different? I must be missing something in the intent, or how charms are
> configured.

Good question, and this is what I feel is clunky. In both cases we are selecting an address given a CIDR network.

config_override is from a configuration value like rabbitmq's "access-network" https://github.com/openstack/charm-rabbitmq-server/blob/master/config.yaml#L172

access_network is not a configuration value. In the primary shared-db case this is received over a relation and then passed to get_relation_ip.

Revision history for this message
Alex Kavanagh (ajkavanagh) wrote :

Ah, yes, of course. I see what you mean about 'clunky'. The reason it's clunky is that (from a technical perspective) we are 'mixing concerns'. The get_relation_id() function is already dependent on the implementation of config(...) rather than the override just being the CIDR string.

The proper 'fix' is to take out the config(config_override) call from the function and just pass in the CIDR as a string. The calling function knows that it's in config and so should do:

get_relation_id(interface, override_network=config('override-config-key'))

rather than:

get_relation_id(interface, override='override-config-key')

That's where the clunkiness is coming from. I'm not sure if it is fixable because it's a shared library and charms may be depending on the functionality. A solution is to provide another function that does it properly and then just proxy the get_relation_id to that function:

def get_relation_id(interface, override):
    return proper_func(interface, config(override) if override)

Thoughts?

Revision history for this message
David Ames (thedac) wrote :

That makes sense.

So the function is new enough that is only in a handful of places. As long as we update all of them we should be ok. I'll make the change and get the list of charms that use it that need to be updated.

735. By David Ames

Simplify to only cidr_network

Revision history for this message
David Ames (thedac) wrote :

Simplified and updated. Much less clunky. And I thought harder about the fallback address which had me rearrange things a bit.

The good news is that only nova-compute uses get_relation_ip and it does not pass an override. So no changes required. The bad news is rabbitmq-server uses a bastardized version of this and should probably be updated.

Regardless this CH MP and the nova-cloud-controller PR are ready to go if you give the all clear.

Revision history for this message
Alex Kavanagh (ajkavanagh) wrote :

David, this is great! Nicely handles the fallback for Juju 1.25.x, deals with the ipv6 issue (until it becomes a space) and also handles the override with a cidr in a non-dependency way. I need this function for my nova-cc changes too! This is a function that should go on the mailing list.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'charmhelpers/contrib/network/ip.py'
2--- charmhelpers/contrib/network/ip.py 2017-04-19 12:42:07 +0000
3+++ charmhelpers/contrib/network/ip.py 2017-04-19 21:29:37 +0000
4@@ -554,31 +554,38 @@
5 "versions less than Trusty 14.04")
6
7
8-def get_relation_ip(interface, config_override=None):
9- """Return this unit's IP for the given relation.
10+def get_relation_ip(interface, cidr_network=None):
11+ """Return this unit's IP for the given interface.
12
13 Allow for an arbitrary interface to use with network-get to select an IP.
14- Handle all address selection options including configuration parameter
15- override and IPv6.
16+ Handle all address selection options including passed cidr network and
17+ IPv6.
18
19- Usage: get_relation_ip('amqp', config_override='access-network')
20+ Usage: get_relation_ip('amqp', cidr_network='10.0.0.0/8')
21
22 @param interface: string name of the relation.
23- @param config_override: string name of the config option for network
24- override. Supports legacy network override configuration parameters.
25+ @param cidr_network: string CIDR Network to select an address from.
26 @raises Exception if prefer-ipv6 is configured but IPv6 unsupported.
27 @returns IPv6 or IPv4 address
28 """
29+ # Select the interface address first
30+ # For possible use as a fallback bellow with get_address_in_network
31+ try:
32+ # Get the interface specific IP
33+ address = network_get_primary_address(interface)
34+ except NotImplementedError:
35+ # If network-get is not available
36+ address = get_host_ip(unit_get('private-address'))
37
38- fallback = get_host_ip(unit_get('private-address'))
39 if config('prefer-ipv6'):
40+ # Currently IPv6 has priority, eventually we want IPv6 to just be
41+ # another network space.
42 assert_charm_supports_ipv6()
43 return get_ipv6_addr()[0]
44- elif config_override and config(config_override):
45- return get_address_in_network(config(config_override),
46- fallback)
47- else:
48- try:
49- return network_get_primary_address(interface)
50- except NotImplementedError:
51- return fallback
52+ elif cidr_network:
53+ # If a specific CIDR network is passed get the address from that
54+ # network.
55+ return get_address_in_network(cidr_network, address)
56+
57+ # Return the interface address
58+ return address
59
60=== modified file 'tests/contrib/network/test_ip.py'
61--- tests/contrib/network/test_ip.py 2017-04-19 12:42:07 +0000
62+++ tests/contrib/network/test_ip.py 2017-04-19 21:29:37 +0000
63@@ -803,54 +803,42 @@
64 def test_get_relation_ip(self, assert_charm_supports_ipv6, get_ipv6_addr,
65 unit_get, get_address_in_network,
66 network_get_primary_address, config):
67+ ACCESS_IP = '10.50.1.1'
68+ ACCESS_NETWORK = '10.50.1.0/24'
69 AMQP_IP = '10.200.1.1'
70- OVERRIDE_AMQP_IP = '10.250.1.1'
71- CLUSTER_IP = '10.100.1.1'
72- OVERRIDE_CLUSTER_IP = '10.150.1.1'
73 IPV6_IP = '2001:DB8::1'
74 DEFAULT_IP = '172.16.1.1'
75 assert_charm_supports_ipv6.return_value = True
76 get_ipv6_addr.return_value = [IPV6_IP]
77 unit_get.return_value = DEFAULT_IP
78 get_address_in_network.return_value = DEFAULT_IP
79+ network_get_primary_address.return_value = AMQP_IP
80+
81+ # Network-get calls
82+ _config = {'prefer-ipv6': False}
83+ config.side_effect = lambda key: _config.get(key)
84+
85+ network_get_primary_address.side_effect = NotImplementedError
86+ self.assertEqual(DEFAULT_IP, net_ip.get_relation_ip('amqp'))
87+
88+ network_get_primary_address.side_effect = None
89+ self.assertEqual(AMQP_IP, net_ip.get_relation_ip('amqp'))
90+
91+ self.assertFalse(get_address_in_network.called)
92+
93+ # Specific CIDR network
94+ get_address_in_network.return_value = ACCESS_IP
95 network_get_primary_address.return_value = DEFAULT_IP
96+ self.assertEqual(
97+ ACCESS_IP,
98+ net_ip.get_relation_ip('shared-db',
99+ cidr_network=ACCESS_NETWORK))
100+ get_address_in_network.assert_called_with(ACCESS_NETWORK, DEFAULT_IP)
101+
102+ self.assertFalse(assert_charm_supports_ipv6.called)
103
104 # IPv6
105- _config = {'prefer-ipv6': True,
106- 'cluster-network': '10.100.1.0/24',
107- 'access-network': '10.200.1.0/24'}
108+ _config = {'prefer-ipv6': True}
109 config.side_effect = lambda key: _config.get(key)
110 self.assertEqual(IPV6_IP, net_ip.get_relation_ip('amqp'))
111-
112- # Overrides
113- _config = {'prefer-ipv6': False,
114- 'cluster-network': '10.100.1.0/24',
115- 'access-network': '10.200.1.0/24'}
116- config.side_effect = lambda key: _config.get(key)
117-
118- get_address_in_network.return_value = OVERRIDE_AMQP_IP
119- self.assertEqual(
120- OVERRIDE_AMQP_IP,
121- net_ip.get_relation_ip('amqp',
122- config_override='access-network'))
123-
124- get_address_in_network.return_value = OVERRIDE_CLUSTER_IP
125- self.assertEqual(OVERRIDE_CLUSTER_IP,
126- net_ip.get_relation_ip(
127- 'cluster',
128- config_override='cluster-network'))
129-
130- # Network-get calls
131- _config = {'prefer-ipv6': False,
132- 'cluster-network': None,
133- 'access-network': None}
134- config.side_effect = lambda key: _config.get(key)
135-
136- network_get_primary_address.return_value = AMQP_IP
137- self.assertEqual(AMQP_IP, net_ip.get_relation_ip('amqp'))
138-
139- network_get_primary_address.return_value = CLUSTER_IP
140- self.assertEqual(CLUSTER_IP,
141- net_ip.get_relation_ip(
142- config_override='cluster-network',
143- interface='cluster'))
144+ assert_charm_supports_ipv6.assert_called_with()

Subscribers

People subscribed via source and target branches