From 392b70480b9251df0a101bcb9520150bb3e7ba45 Mon Sep 17 00:00:00 2001 From: mzhnichkov Date: Tue, 26 Apr 2016 12:13:13 +0300 Subject: [PATCH] Added ability to update PXE DHCP parameters after deploy It's possible to change PXE settings on post deploy stage, but, in fact, dnsmasq configuration will not be changed. This patch introduce puppet call to get these changes applied. Function puppetApply was refactored to return Bool value, previously it has returned None or False. Change-Id: I3fc7097035f492ad24483205cedc0d2c773af24b Closes-Bug: #1500667 Depends-On: I9200c90747dba1e5c5b4e5457e423cb4c3ff3062 (cherry picked from commit 599d801eb0cd3058aae293ebae1a6e5dd0548f3b) --- fuelmenu/common/network.py | 4 +- fuelmenu/common/puppet.py | 21 +++++ fuelmenu/consts.py | 6 ++ fuelmenu/modules/cobblerconf.py | 124 +++++++++++++++++++++++++-- fuelmenu/modules/interfaces.py | 2 +- fuelmenu/tests/test_common_puppet.py | 2 +- setup.py | 1 + specs/fuelmenu.spec | 1 + 8 files changed, 151 insertions(+), 10 deletions(-) diff --git a/fuelmenu/common/network.py b/fuelmenu/common/network.py index c18b3c6..f08ed36 100644 --- a/fuelmenu/common/network.py +++ b/fuelmenu/common/network.py @@ -33,8 +33,8 @@ def inSameSubnet(ip1, ip2, netmask_or_cidr): cidr1 = netaddr.IPNetwork("%s/%s" % (ip1, netmask_or_cidr)) cidr2 = netaddr.IPNetwork("%s/%s" % (ip2, netmask_or_cidr)) return cidr1 == cidr2 - except netaddr.AddrFormatError: - log.exception('Invalid address specified: {0}'.format(cidr1)) + except netaddr.AddrFormatError as e: + log.exception(e.message) return False diff --git a/fuelmenu/common/puppet.py b/fuelmenu/common/puppet.py index 4ec4822..6ede2ee 100644 --- a/fuelmenu/common/puppet.py +++ b/fuelmenu/common/puppet.py @@ -68,3 +68,24 @@ def puppetApply(classes): log.error("Exit code: %d. Error: %s Stdout: %s", code, err, out) return False + return True + + +def puppetApplyManifest(manifest): + log = logging + log.info("Start puppet apply with manifest {0}".format(manifest)) + + cmd = ["puppet", "apply", "--debug", "--verbose", "--logdest", + consts.PUPPET_LOGFILE, manifest] + + log.debug(' '.join(cmd)) + err_code, _, errout = utils.execute(cmd) + + if err_code != 0: + msg = "Puppet apply failed. Check logs for more details." + log.error(msg) + return False, msg + + msg = "Puppet apply successfully executed." + log.info(msg) + return True, msg diff --git a/fuelmenu/consts.py b/fuelmenu/consts.py index 66b9336..d01680f 100644 --- a/fuelmenu/consts.py +++ b/fuelmenu/consts.py @@ -17,9 +17,13 @@ LOGFILE = "/var/log/fuelmenu.log" PUPPET_LOGFILE = "/var/log/puppet/fuelmenu-puppet.log" +PUPPET_NAILGUN = "/etc/puppet/modules/fuel/examples/nailgun.pp" +PUPPET_FUEL_MASTER = "/etc/puppet/modules/fuel/examples/host.pp" +PUPPET_DHCP_RANGES = "/etc/puppet/modules/fuel/examples/dhcp-ranges.pp" SETTINGS_FILE = "/etc/fuel/astute.yaml" RELEASE_FILE = "/etc/fuel_release" +HIERA_NET_SETTINGS = "/etc/hiera/networks.yaml" PRE_DEPLOYMENT_MODE = "pre" POST_DEPLOYMENT_MODE = "post" @@ -27,3 +31,5 @@ POST_DEPLOYMENT_MODE = "post" PUPPET_TYPE_LITERAL = "literal" PUPPET_TYPE_RESOURCE = "resource" PUPPET_TYPE_CLASS = "class" + +ADMIN_NETWORK_ID = 1 diff --git a/fuelmenu/modules/cobblerconf.py b/fuelmenu/modules/cobblerconf.py index 7528fd0..1fc49ac 100644 --- a/fuelmenu/modules/cobblerconf.py +++ b/fuelmenu/modules/cobblerconf.py @@ -13,18 +13,26 @@ # License for the specific language governing permissions and limitations # under the License. +import logging +import os + +from fuelclient.cli import error +from fuelclient.objects import NetworkGroup +import netaddr +import urwid +import urwid.raw_display +import urwid.web_display +import yaml + from fuelmenu.common import dialog from fuelmenu.common.errors import BadIPException from fuelmenu.common.modulehelper import ModuleHelper from fuelmenu.common.modulehelper import WidgetType from fuelmenu.common import network +from fuelmenu.common import puppet import fuelmenu.common.urwidwrapper as widget from fuelmenu.common import utils -import logging -import netaddr -import urwid -import urwid.raw_display -import urwid.web_display +from fuelmenu import consts log = logging.getLogger('fuelmenu.pxe_setup') blank = urwid.Divider() @@ -80,6 +88,10 @@ to advertise via DHCP to nodes", self.load() self.extdhcp = True self.screen = None + self.apply_dialog_message = { + 'title': "Apply failed in module {0}".format(self.name), + "message": "Error applying changes. Check logs for details." + } def check(self, args): """Validates all fields have valid values and some sanity checks.""" @@ -278,10 +290,110 @@ interface first.") log.error("Check failed. Not applying") log.error("%s" % (responses)) return False - self.save(responses) + if utils.is_post_deployment(): + self.parent.apply_tasks.add(self.update_dhcp) return True + def update_dhcp(self): + settings = self.parent.settings.get("ADMIN_NETWORK") + if not self._update_nailgun(settings): + return False + if os.path.exists(consts.HIERA_NET_SETTINGS): + result, msg = self._update_hiera_dnsmasq(settings) + else: + result = self._update_dnsmasq(settings) + if not result: + ModuleHelper.display_dialog( + self, error_msg=self.apply_dialog_message["message"], + title=self.apply_dialog_message["title"]) + return False + cobbler_sync = ["cobbler", "sync"] + code, out, err = utils.execute(cobbler_sync) + if code != 0: + log.error(err) + ModuleHelper.display_dialog( + self, error_msg=self.apply_dialog_message["message"], + title=self.apply_dialog_message["title"]) + return False + return True + + def _update_nailgun(self, settings): + msg = "Apply changes to Nailgun" + log.info(msg) + self.parent.footer.set_text(msg) + self.parent.refreshScreen() + + # TODO(mzhnichkov) this manifest apply twice(here and in feature + # groups). Need to combine this calls + result, msg = puppet.puppetApplyManifest(consts.PUPPET_NAILGUN) + if not result: + ModuleHelper.display_dialog( + self, error_msg=self.apply_dialog_message["message"], + title=self.apply_dialog_message["title"]) + return False + + data = { + "gateway": settings["dhcp_gateway"], + "ip_ranges": [ + [settings["dhcp_pool_start"], settings["dhcp_pool_end"]] + ] + } + try: + NetworkGroup(consts.ADMIN_NETWORK_ID).set(data) + except error.HTTPError as e: + log.error(e.message) + ModuleHelper.display_dialog( + self, error_msg=self.apply_dialog_message["message"], + title=self.apply_dialog_message["title"]) + return False + return True + + def _update_hiera_dnsmasq(self, settings): + """Update Hiera and dnsmasq + + PXE related configuration should be written in separate + configuration file when you create additional admin + network(this behavior was introduced in nailgun's + DnsmasqUpdateTask class) + """ + + msg = "Apply changes to Hiera and Dnsmasq" + log.info(msg) + self.parent.footer.set_text(msg) + self.parent.refreshScreen() + with open(consts.HIERA_NET_SETTINGS, "r") as hiera_settings: + networks = yaml.safe_load(hiera_settings) + net = netaddr.IPNetwork( + "{0}/{1}".format(settings["ipaddress"], + settings["netmask"])) + for admin_net in networks["admin_networks"]: + if str(net.cidr) == admin_net["cidr"]: + admin_net["ip_ranges"] = [ + [settings["dhcp_pool_start"], + settings["dhcp_pool_end"]] + ] + admin_net["gateway"] = settings["dhcp_gateway"] + with open(consts.HIERA_NET_SETTINGS, "w") as hiera_settings: + yaml.safe_dump(networks, hiera_settings) + return puppet.puppetApplyManifest(consts.PUPPET_DHCP_RANGES) + + def _update_dnsmasq(self, settings): + puppet_classes = [{ + "type": "resource", + "class": "fuel::dnsmasq::dhcp_range", + "name": "default", + "params": { + "dhcp_start_address": settings["dhcp_pool_start"], + "dhcp_end_address": settings["dhcp_pool_end"], + "dhcp_netmask": settings["netmask"], + "dhcp_gateway": settings["dhcp_gateway"], + "next_server": settings["ipaddress"] + } + }] + log.debug("Start puppet with data {0}".format(puppet_classes)) + return puppet.puppetApply(puppet_classes) + def cancel(self, button): ModuleHelper.cancel(self, button) self.setNetworkDetails() diff --git a/fuelmenu/modules/interfaces.py b/fuelmenu/modules/interfaces.py index 3d6f86d..a007354 100644 --- a/fuelmenu/modules/interfaces.py +++ b/fuelmenu/modules/interfaces.py @@ -306,7 +306,7 @@ class interfaces(urwid.WidgetWrap): try: self.parent.refreshScreen() result = puppet.puppetApply(puppetclasses) - if result is False: + if not result: raise Exception("Puppet apply failed") ModuleHelper.getNetwork(self) gateway = self.get_default_gateway_linux() diff --git a/fuelmenu/tests/test_common_puppet.py b/fuelmenu/tests/test_common_puppet.py index 95ee0e4..01c1959 100644 --- a/fuelmenu/tests/test_common_puppet.py +++ b/fuelmenu/tests/test_common_puppet.py @@ -58,7 +58,7 @@ class TestPuppetApply(unittest.TestCase): ] def test_puppet_apply(self, m_execute, m_log): - self.assertEqual(puppet.puppetApply(self.classes), None) + self.assertEqual(puppet.puppetApply(self.classes), True) m_execute.assert_called_once_with(self.command, stdin=self.input) m_log.info.assert_called_once_with('Puppet start') self.assertFalse(m_log.error.called) diff --git a/setup.py b/setup.py index a8441d5..7bef2aa 100644 --- a/setup.py +++ b/setup.py @@ -23,6 +23,7 @@ requirements = [ 'netifaces>=0.5', 'urwid>=1.1.1', 'requests>=2.5.2,!=2.8.0,!=2.9.0', + 'python-fuelclient>=9.0.0' ] if sys.version_info[0:2] == (2, 6): diff --git a/specs/fuelmenu.spec b/specs/fuelmenu.spec index 484108f..17bf741 100644 --- a/specs/fuelmenu.spec +++ b/specs/fuelmenu.spec @@ -26,6 +26,7 @@ Requires: python-urwid >= 1.1.0 Requires: PyYAML Requires: screen Requires: python-six +Requires: python-fuelclient %if 0%{?rhel} == 6 Requires: python-ordereddict %endif