diff --git a/devstack/dsconf.py b/devstack/dsconf.py index 217db1c..43bf3f4 100644 --- a/devstack/dsconf.py +++ b/devstack/dsconf.py @@ -175,6 +175,12 @@ class LocalConf(object): if in_section: yield line + def _has_local_section(self): + for group in self.groups(): + if group == ("local", "localrc"): + return True + return False + def extract(self, group, conf, target): ini_file = IniFile(target) for section, name, value in self._conf(group, conf): @@ -186,9 +192,28 @@ class LocalConf(object): f.write(line) def _at_insert_point_local(self, name, func): + """Run function when we are at the right insertion point in file. + + This lets us process an arbitrary file and insert content at + the correct point. It has a few different state flags that we + are looking for. + + Does this file have a local section at all? If not, we need to + write one early in the file (this means we work with an empty + file, as well as a file that has only post-config sections. + + Are we currently in a local section, if so, we need to write + out content to the end, because items added to local always + have to be added at the end. + + Did we write out the work that we expected? If so, just blast + all lines to the end of the file. + + """ temp = tempfile.NamedTemporaryFile(mode='r') shutil.copyfile(self.fname, temp.name) - in_meta = False + in_local = False + has_local = self._has_local_section() done = False with open(self.fname, "w+") as writer: with open(temp.name) as reader: @@ -198,11 +223,17 @@ class LocalConf(object): continue if re.match(re.escape("[[local|localrc]]"), line): - in_meta = True - elif in_meta and re.match(re.escape("[["), line): + in_local = True + elif in_local and re.match(re.escape("[["), line): func(writer, None) done = True - in_meta = False + in_local = False + elif not has_local and re.match(re.escape("[["), line): + writer.write("[[local|localrc]]\n") + func(writer, None) + done = True + in_local = False + has_local = True # otherwise, just write what we found writer.write(line) diff --git a/devstack/tests/test_localconf_set_local.py b/devstack/tests/test_localconf_set_local.py index d508563..0ae8a3a 100644 --- a/devstack/tests/test_localconf_set_local.py +++ b/devstack/tests/test_localconf_set_local.py @@ -35,6 +35,15 @@ global_physnet_mtu=1450 compute = auto """ +BASIC_NO_LOCAL = """ +[[post-config|$NEUTRON_CONF]] +[DEFAULT] +global_physnet_mtu=1450 +[[post-config|$NOVA_CONF]] +[upgrade_levels] +compute = auto +""" + RESULT1 = """ [[local|localrc]] a=b @@ -78,6 +87,18 @@ global_physnet_mtu=1450 compute = auto """ +RESULT_NO_LOCAL = """ +[[local|localrc]] +a=b +c=d +[[post-config|$NEUTRON_CONF]] +[DEFAULT] +global_physnet_mtu=1450 +[[post-config|$NOVA_CONF]] +[upgrade_levels] +compute = auto +""" + class TestLcSet(testtools.TestCase): @@ -117,3 +138,21 @@ class TestLcSet(testtools.TestCase): with open(self._path) as f: content = f.read() self.assertEqual(content, RESULT3) + + +class TestNoLocal(testtools.TestCase): + + def setUp(self): + super(TestNoLocal, self).setUp() + self._path = self.useFixture(fixtures.TempDir()).path + self._path += "/local.conf" + with open(self._path, "w") as f: + f.write(BASIC_NO_LOCAL) + + def test_set_new(self): + conf = dsconf.LocalConf(self._path) + conf.set_local("a=b") + conf.set_local("c=d") + with open(self._path) as f: + content = f.read() + self.assertEqual(content, RESULT_NO_LOCAL)