Merge lp:~openstack-charmers/charm-helpers/snabbswitch into lp:charm-helpers

Proposed by Edward Hope-Morley
Status: Work in progress
Proposed branch: lp:~openstack-charmers/charm-helpers/snabbswitch
Merge into: lp:charm-helpers
Diff against target: 353 lines (+255/-2)
4 files modified
charmhelpers/contrib/openstack/context.py (+78/-0)
charmhelpers/contrib/openstack/neutron.py (+13/-0)
charmhelpers/core/host.py (+31/-2)
tests/contrib/openstack/test_os_contexts.py (+133/-0)
To merge this branch: bzr merge lp:~openstack-charmers/charm-helpers/snabbswitch
Reviewer Review Type Date Requested Status
OpenStack Charmers Pending
Review via email: mp+237037@code.launchpad.net
To post a comment you must log in.
209. By Edward Hope-Morley

synced charm-helpers

210. By Edward Hope-Morley

synced lp:charm-helpers

211. By Edward Hope-Morley

synced trunk

212. By Edward Hope-Morley

synced lp:charm-helpers

213. By Edward Hope-Morley

synced lp:charm-helpers

214. By Edward Hope-Morley

zones config file

215. By Edward Hope-Morley

config name change

216. By Edward Hope-Morley

remove bz2 dep on snabbswitch-zones

217. By Edward Hope-Morley

Added log when unknown plugin provided to NeutronContext

218. By Edward Hope-Morley

added missing neutron_plugin attr to snabb ctxt

219. By Edward Hope-Morley

protect against neutron not installed

220. By Edward Hope-Morley

removed bad docstring

221. By Edward Hope-Morley

.

222. By Edward Hope-Morley

use ml2_conf.ini for snabb

223. By Edward Hope-Morley

sync /next

224. By Edward Hope-Morley

add support for conffiles dir

225. By Edward Hope-Morley

add support for conffiles dir

226. By Edward Hope-Morley

synced lp:charm-helpers

Unmerged revisions

226. By Edward Hope-Morley

synced lp:charm-helpers

225. By Edward Hope-Morley

add support for conffiles dir

224. By Edward Hope-Morley

add support for conffiles dir

223. By Edward Hope-Morley

sync /next

222. By Edward Hope-Morley

use ml2_conf.ini for snabb

221. By Edward Hope-Morley

.

220. By Edward Hope-Morley

removed bad docstring

219. By Edward Hope-Morley

protect against neutron not installed

218. By Edward Hope-Morley

added missing neutron_plugin attr to snabb ctxt

217. By Edward Hope-Morley

Added log when unknown plugin provided to NeutronContext

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'charmhelpers/contrib/openstack/context.py'
2--- charmhelpers/contrib/openstack/context.py 2015-10-07 21:51:23 +0000
3+++ charmhelpers/contrib/openstack/context.py 2015-10-23 09:05:27 +0000
4@@ -14,11 +14,15 @@
5 # You should have received a copy of the GNU Lesser General Public License
6 # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
7
8+import bz2
9 import glob
10 import json
11 import os
12+import base64
13 import re
14 import time
15+import pwd
16+
17 from base64 import b64decode
18 from subprocess import check_call
19
20@@ -819,6 +823,8 @@
21
22 class NeutronContext(OSContextGenerator):
23 interfaces = []
24+ SNABBSWITCH_ML2_ZONES_FILE = '/etc/snabbswitch/ml2_zones.txt'
25+ CONFFILES_DIR = 'conffiles'
26
27 @property
28 def plugin(self):
29@@ -912,6 +918,74 @@
30
31 return n1kv_ctxt
32
33+ def _snabb_init_zones_file(self, zones_file):
34+ log("Configuring zones file '%s'" % (zones_file), level=INFO)
35+ dirname = os.path.dirname(zones_file)
36+ if dirname and not os.path.isdir(dirname):
37+ os.makedirs(dirname)
38+
39+ conffile_occurences = 0
40+ if os.path.isdir(self.CONFFILES_DIR):
41+ fname = os.path.basename(zones_file)
42+ for conf in glob.glob("%s/%s*" % (self.CONFFILES_DIR, fname)):
43+ if conffile_occurences:
44+ log("Found more than one occurence of %s in %s*" %
45+ (fname, self.CONFFILES_DIR), level=INFO)
46+
47+ conffile_occurences += 1
48+ # First attempt to treat the file as bz2, falling back to raw
49+ try:
50+ data = bz2.BZ2File(conf).read()
51+ except IOError:
52+ with open(conf, 'r') as fd:
53+ data = fd.read()
54+
55+ with open(zones_file, 'w') as fd:
56+ fd.write(str(data))
57+
58+ if not conffile_occurences:
59+ encoded_defs = config('snabbswitch-zones')
60+ if encoded_defs:
61+ out = base64.b64decode(encoded_defs)
62+ with open(zones_file, 'w') as fd:
63+ fd.write(out.decode('ascii'))
64+
65+ log("Updated snabbswitch zones file '%s'" % (zones_file),
66+ level=INFO)
67+ else:
68+ log("No snabbswitch zones provided", level=INFO)
69+ # Make sure a zones file exists if a path is set.
70+ if not os.path.isfile(zones_file):
71+ with open(zones_file, 'w') as fd:
72+ fd.write('[]')
73+
74+ # Make sure neutron user can read file.
75+ try:
76+ user = pwd.getpwnam('neutron')
77+ uid, gid = user.pw_uid, user.pw_gid
78+ os.chown(zones_file, uid, gid)
79+ except KeyError:
80+ # may not be installed/configured yet
81+ pass
82+
83+ def snabb_ctxt(self):
84+ driver = neutron_plugin_attribute(self.plugin, 'driver',
85+ self.network_manager)
86+ snabb_config = neutron_plugin_attribute(self.plugin, 'config',
87+ self.network_manager)
88+ snabb_ctxt = {
89+ 'core_plugin': driver,
90+ 'neutron_plugin': 'snabb',
91+ 'neutron_security_groups': self.neutron_security_groups,
92+ 'local_ip': unit_private_ip(),
93+ 'config': snabb_config,
94+ }
95+
96+ snabb_ctxt['zone_definition_file'] = self.SNABBSWITCH_ML2_ZONES_FILE
97+ self._snabb_init_zones_file(self.SNABBSWITCH_ML2_ZONES_FILE)
98+
99+ return snabb_ctxt
100+
101 def calico_ctxt(self):
102 driver = neutron_plugin_attribute(self.plugin, 'driver',
103 self.network_manager)
104@@ -988,6 +1062,10 @@
105 ctxt.update(self.pg_ctxt())
106 elif self.plugin == 'midonet':
107 ctxt.update(self.midonet_ctxt())
108+ elif self.plugin == 'snabb':
109+ ctxt.update(self.snabb_ctxt())
110+ else:
111+ log("Unknown neutron plugin '%s'" % (self.plugin), level=INFO)
112
113 alchemy_flags = config('neutron-alchemy-flags')
114 if alchemy_flags:
115
116=== modified file 'charmhelpers/contrib/openstack/neutron.py'
117--- charmhelpers/contrib/openstack/neutron.py 2015-10-07 10:57:11 +0000
118+++ charmhelpers/contrib/openstack/neutron.py 2015-10-23 09:05:27 +0000
119@@ -161,6 +161,19 @@
120 'neutron-plugin-cisco'],
121 'server_services': ['neutron-server']
122 },
123+ 'snabb': {
124+ 'config': '/etc/neutron/plugins/ml2/ml2_conf.ini',
125+ 'driver': 'neutron.plugins.ml2.plugin.Ml2Plugin',
126+ 'contexts': [
127+ context.SharedDBContext(user=config('neutron-database-user'),
128+ database=config('neutron-database'),
129+ relation_prefix='neutron',
130+ ssl_dir=NEUTRON_CONF_DIR)],
131+ 'services': [],
132+ 'packages': [],
133+ 'server_packages': ['neutron-server'],
134+ 'server_services': ['neutron-server']
135+ },
136 'Calico': {
137 'config': '/etc/neutron/plugins/ml2/ml2_conf.ini',
138 'driver': 'neutron.plugins.ml2.plugin.Ml2Plugin',
139
140=== modified file 'charmhelpers/core/host.py'
141--- charmhelpers/core/host.py 2015-10-15 05:34:04 +0000
142+++ charmhelpers/core/host.py 2015-10-23 09:05:27 +0000
143@@ -35,7 +35,10 @@
144
145 import six
146
147-from .hookenv import log
148+from .hookenv import (
149+ log,
150+ ERROR
151+)
152 from .fstab import Fstab
153
154
155@@ -109,12 +112,38 @@
156 return started
157
158
159-def service(action, service_name):
160+def service(action, service_name, inst_args=None):
161 """Control a system service"""
162 cmd = ['service', service_name, action]
163+ if inst_args:
164+ cmd += inst_args
165 return subprocess.call(cmd) == 0
166
167
168+def get_service_args(conf, keys, allow_comments=True):
169+ """Fetch service args from conf file (if exists)"""
170+ args = []
171+ if os.path.isfile(conf):
172+ with open(conf, 'r') as fd:
173+ for i, line in enumerate(fd.readlines()):
174+ if allow_comments and line.startswith('#'):
175+ continue
176+
177+ cfg = line.split()
178+ a = len(cfg)
179+ b = len(keys)
180+ if a > b:
181+ log("Invalid parameters found in config file - found=%s "
182+ "expected=%s" % (a, b), level=ERROR)
183+ return None
184+
185+ args.append(['%s=%s' % (keys[i], c) for i, c in enumerate(cfg)])
186+
187+ return args
188+
189+ return None
190+
191+
192 def service_running(service):
193 """Determine whether a system service is running"""
194 try:
195
196=== modified file 'tests/contrib/openstack/test_os_contexts.py'
197--- tests/contrib/openstack/test_os_contexts.py 2015-10-07 10:57:11 +0000
198+++ tests/contrib/openstack/test_os_contexts.py 2015-10-23 09:05:27 +0000
199@@ -1,7 +1,13 @@
200 import charmhelpers.contrib.openstack.context as context
201+
202+import bz2
203 import yaml
204 import json
205 import unittest
206+import tempfile
207+import shutil
208+import os
209+import base64
210 from copy import copy, deepcopy
211 from mock import (
212 patch,
213@@ -10,6 +16,9 @@
214 call
215 )
216 from tests.helpers import patch_open
217+from charmhelpers.contrib.openstack.context import (
218+ NeutronContext
219+)
220
221 import six
222
223@@ -2145,6 +2154,130 @@
224 config.return_value = 'good_flag=woot=='
225 self.assertRaises(context.OSContextError, flags)
226
227+ @patch('charmhelpers.contrib.openstack.context.config')
228+ @patch('pwd.getpwnam')
229+ @patch('os.chown')
230+ def test_init_zones_file_no_file(self, mock_chown, mock_getpwnam,
231+ mock_config):
232+ with tempfile.NamedTemporaryFile() as tmpfile:
233+ mock_config.return_value = None
234+ NeutronContext()._snabb_init_zones_file(tmpfile.name)
235+
236+ with open(tmpfile.name) as fd:
237+ data = fd.read()
238+
239+ self.assertEqual(data, '')
240+ self.assertTrue(mock_chown.called)
241+
242+ @patch('charmhelpers.contrib.openstack.context.config')
243+ @patch('pwd.getpwnam')
244+ @patch('os.chown')
245+ def test_init_zones_file_no_defs(self, mock_chown, mock_getpwnam,
246+ mock_config):
247+ with tempfile.NamedTemporaryFile() as tmpfile:
248+
249+ def _mock_config(name):
250+ if name == 'snabbswitch-zones':
251+ return None
252+
253+ mock_config.side_effect = _mock_config
254+ NeutronContext()._snabb_init_zones_file(tmpfile.name)
255+
256+ with open(tmpfile.name) as fd:
257+ data = fd.read()
258+
259+ self.assertEqual(data, '')
260+ self.assertTrue(mock_getpwnam.called)
261+ self.assertTrue(mock_chown.called)
262+
263+ @patch('charmhelpers.contrib.openstack.context.config')
264+ @patch('subprocess.check_output')
265+ @patch('pwd.getpwnam')
266+ @patch('os.chown')
267+ def test_init_zones_file(self, mock_chown, mock_getpwnam,
268+ mock_check_output, mock_config):
269+ out = "[{'host': 'HostA}]"
270+ mock_check_output.return_value = out
271+
272+ zone_defs = base64.b64encode(out.encode('ascii'))
273+ with tempfile.NamedTemporaryFile() as tmpfile:
274+
275+ def _mock_config(name):
276+ if name == 'snabbswitch-zones':
277+ return zone_defs
278+
279+ mock_config.side_effect = _mock_config
280+ NeutronContext()._snabb_init_zones_file(tmpfile.name)
281+
282+ with open(tmpfile.name) as fd:
283+ data = fd.read()
284+
285+ self.assertTrue(mock_config.called)
286+ self.assertEqual(data, out)
287+ self.assertTrue(mock_getpwnam.called)
288+ self.assertTrue(mock_chown.called)
289+
290+ @patch('charmhelpers.contrib.openstack.context.config')
291+ @patch('subprocess.check_output')
292+ @patch('pwd.getpwnam')
293+ @patch('os.chown')
294+ def test_init_zones_file_raw(self, mock_chown, mock_getpwnam,
295+ mock_check_output, mock_config):
296+ out = "[{'host': 'HostA}]"
297+ mock_check_output.return_value = out
298+ ctxt = NeutronContext()
299+ ctxt.CONFFILES_DIR = tempfile.mkdtemp()
300+ ctxt.SNABBSWITCH_ML2_ZONES_FILE = os.path.join(tempfile.mkdtemp(),
301+ 'ml2_zones.txt')
302+ try:
303+ src = os.path.join(ctxt.CONFFILES_DIR, 'ml2_zones.txt')
304+ dst = os.path.join(ctxt.SNABBSWITCH_ML2_ZONES_FILE)
305+ with open(src, 'w') as fd:
306+ fd.write(out)
307+
308+ ctxt._snabb_init_zones_file(ctxt.SNABBSWITCH_ML2_ZONES_FILE)
309+
310+ with open(dst) as fd:
311+ data = fd.read()
312+ finally:
313+ shutil.rmtree(ctxt.CONFFILES_DIR)
314+
315+ self.assertFalse(mock_config.called)
316+ self.assertEqual(data, out)
317+ self.assertTrue(mock_getpwnam.called)
318+ self.assertTrue(mock_chown.called)
319+
320+ @patch('charmhelpers.contrib.openstack.context.config')
321+ @patch('subprocess.check_output')
322+ @patch('pwd.getpwnam')
323+ @patch('os.chown')
324+ def test_init_zones_file_bz2(self, mock_chown, mock_getpwnam,
325+ mock_check_output, mock_config):
326+ out = "[{'host': 'HostA}]"
327+ mock_check_output.return_value = out
328+ ctxt = NeutronContext()
329+ ctxt.CONFFILES_DIR = tempfile.mkdtemp()
330+ ctxt.SNABBSWITCH_ML2_ZONES_FILE = os.path.join(tempfile.mkdtemp(),
331+ 'ml2_zones.txt')
332+ try:
333+ src = os.path.join(ctxt.CONFFILES_DIR, 'ml2_zones.txt')
334+ dst = os.path.join(ctxt.SNABBSWITCH_ML2_ZONES_FILE)
335+ bz_file = bz2.BZ2File(src, 'w')
336+ bz_file.write(out.encode('ascii'))
337+ bz_file.close()
338+
339+ ctxt._snabb_init_zones_file(ctxt.SNABBSWITCH_ML2_ZONES_FILE)
340+
341+ with open(dst) as fd:
342+ data = fd.read()
343+ finally:
344+ shutil.rmtree(ctxt.CONFFILES_DIR)
345+
346+ self.assertFalse(mock_config.called)
347+ self.assertEqual(data, out)
348+ self.assertTrue(mock_getpwnam.called)
349+ self.assertTrue(mock_chown.called)
350+
351 @patch.object(context, 'config')
352 def test_os_configflag_context_custom(self, config):
353 flags = context.OSConfigFlagContext(

Subscribers

People subscribed via source and target branches