diff --git a/fuelmenu/common/network.py b/fuelmenu/common/network.py index 31be819..c1237eb 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 3bd05d8..6ede2ee 100644 --- a/fuelmenu/common/puppet.py +++ b/fuelmenu/common/puppet.py @@ -68,6 +68,7 @@ def puppetApply(classes): log.error("Exit code: %d. Error: %s Stdout: %s", code, err, out) return False + return True def puppetApplyManifest(manifest): diff --git a/fuelmenu/consts.py b/fuelmenu/consts.py index 932e605..8c95c93 100644 --- a/fuelmenu/consts.py +++ b/fuelmenu/consts.py @@ -19,9 +19,11 @@ 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" DEFAULT_LOCK_FILE = "/var/run/fuelmenu.lock" @@ -31,3 +33,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 978bb71..37a80f2 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() @@ -78,6 +86,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.""" @@ -276,10 +288,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 30e46cf..3709a55 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 c12a55e..e841265 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 693cea8..80d86c4 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