fuel-web/fuelmenu/fuelmenu/common/modulehelper.py

217 lines
9.0 KiB
Python

# -*- coding: utf-8 -*-
# Copyright 2013 Mirantis, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import fuelmenu.common.urwidwrapper as widget
from fuelmenu.settings import Settings
import logging
import netifaces
import re
import socket
import struct
import subprocess
import urwid
import urwid.raw_display
import urwid.web_display
log = logging.getLogger('fuelmenu.modulehelper')
blank = urwid.Divider()
class ModuleHelper(object):
@classmethod
def load(cls, modobj):
#Read in yaml
defaultsettings = Settings().read(modobj.parent.defaultsettingsfile)
oldsettings = defaultsettings.copy()
oldsettings.update(Settings().read(modobj.parent.settingsfile))
for setting in modobj.defaults.keys():
if "label" in setting:
continue
elif "/" in setting:
part1, part2 = setting.split("/")
modobj.defaults[setting]["value"] = oldsettings[part1][part2]
else:
modobj.defaults[setting]["value"] = oldsettings[setting]
if modobj.netsettings and oldsettings["ADMIN_NETWORK"]["interface"] \
in modobj.netsettings.keys():
modobj.activeiface = oldsettings["ADMIN_NETWORK"]["interface"]
return oldsettings
@classmethod
def save(cls, modobj, responses):
newsettings = dict()
for setting in responses.keys():
if "/" in setting:
part1, part2 = setting.split("/")
if part1 not in newsettings:
#We may not touch all settings, so copy oldsettings first
newsettings[part1] = modobj.oldsettings[part1]
newsettings[part1][part2] = responses[setting]
else:
newsettings[setting] = responses[setting]
return newsettings
@classmethod
def cancel(self, cls, button=None):
for index, fieldname in enumerate(cls.fields):
if fieldname != "blank" and "label" not in fieldname:
try:
cls.edits[index].set_edit_text(cls.defaults[fieldname][
'value'])
except AttributeError:
log.warning("Field %s unable to reset text" % fieldname)
@classmethod
def screenUI(cls, modobj, headertext, fields, defaults,
showallbuttons=False, buttons_visible=True):
log.debug("Preparing screen UI for %s" % modobj.name)
#Define text labels, text fields, and buttons first
header_content = []
for text in headertext:
if isinstance(text, str):
header_content.append(urwid.Text(text))
else:
header_content.append(text)
edits = []
toolbar = modobj.parent.footer
for key in fields:
#Example: key = hostname, label = Hostname, value = fuel-pm
if key == "blank":
edits.append(blank)
elif defaults[key]["value"] == "radio":
label = widget.TextLabel(defaults[key]["label"])
if "choices" in defaults[key]:
choices_list = defaults[key]["choices"]
else:
choices_list = ["Yes", "No"]
choices = widget.ChoicesGroup(choices_list,
default_value="Yes",
fn=modobj.radioSelect)
columns = widget.Columns([('weight', 2, label),
('weight', 3, choices)])
#Attach choices rb_group so we can use it later
columns.rb_group = choices.rb_group
edits.append(columns)
elif defaults[key]["value"] == "label":
edits.append(widget.TextLabel(defaults[key]["label"]))
else:
ispassword = "PASSWORD" in key.upper()
caption = defaults[key]["label"]
default = defaults[key]["value"]
tooltip = defaults[key]["tooltip"]
edits.append(
widget.TextField(key, caption, 23, default, tooltip,
toolbar, ispassword=ispassword))
listbox_content = []
listbox_content.extend(header_content)
listbox_content.append(blank)
listbox_content.extend(edits)
listbox_content.append(blank)
#Wrap buttons into Columns so it doesn't expand and look ugly
if buttons_visible:
#Button to check
button_check = widget.Button("Check", modobj.check)
#Button to revert to previously saved settings
button_cancel = widget.Button("Cancel", modobj.cancel)
#Button to apply (and check again)
button_apply = widget.Button("Apply", modobj.apply)
if modobj.parent.globalsave and showallbuttons is False:
check_col = widget.Columns([button_check])
else:
check_col = widget.Columns([button_check, button_cancel,
button_apply, ('weight', 2, blank)])
listbox_content.append(check_col)
#Add everything into a ListBox and return it
listwalker = widget.TabbedListWalker(listbox_content)
screen = urwid.ListBox(listwalker)
modobj.edits = edits
modobj.walker = listwalker
modobj.listbox_content = listbox_content
return screen
@classmethod
def getNetwork(cls, modobj):
"""Returns addr, broadcast, netmask for each network interface."""
re_ifaces = re.compile(r"lo|vir|vbox|docker|veth")
for iface in netifaces.interfaces():
if re_ifaces.search(iface):
continue
try:
modobj.netsettings.update({iface: netifaces.ifaddresses(iface)[
netifaces.AF_INET][0]})
modobj.netsettings[iface]["onboot"] = "Yes"
except (TypeError, KeyError):
modobj.netsettings.update({iface: {"addr": "", "netmask": "",
"onboot": "no"}})
modobj.netsettings[iface]['mac'] = netifaces.ifaddresses(iface)[
netifaces.AF_LINK][0]['addr']
#Set link state
try:
with open("/sys/class/net/%s/operstate" % iface) as f:
content = f.readlines()
modobj.netsettings[iface]["link"] = content[0].strip()
except IOError:
log.warning("Unable to read operstate file for %s" % iface)
modobj.netsettings[iface]["link"] = "unknown"
#Change unknown link state to up if interface has an IP
if modobj.netsettings[iface]["link"] == "unknown":
if modobj.netsettings[iface]["addr"] != "":
modobj.netsettings[iface]["link"] = "up"
#Read bootproto from /etc/sysconfig/network-scripts/ifcfg-DEV
modobj.netsettings[iface]['bootproto'] = "none"
try:
with open("/etc/sysconfig/network-scripts/ifcfg-%s" % iface)\
as fh:
for line in fh:
if re.match("^BOOTPROTO=", line):
modobj.netsettings[iface]['bootproto'] = \
line.split('=')[1].strip()
break
except Exception:
#Check for dhclient process running for this interface
if modobj.getDHCP(iface):
modobj.netsettings[iface]['bootproto'] = "dhcp"
else:
modobj.netsettings[iface]['bootproto'] = "none"
modobj.gateway = modobj.get_default_gateway_linux()
@classmethod
def getDHCP(cls, iface):
"""Returns True if the interface has a dhclient process running."""
noout = open('/dev/null', 'w')
dhclient_running = subprocess.call(["pgrep", "-f", "dhclient.*%s" %
(iface)], stdout=noout,
stderr=noout)
return (dhclient_running == 0)
@classmethod
def get_default_gateway_linux(cls):
"""Read the default gateway directly from /proc."""
with open("/proc/net/route") as fh:
for line in fh:
fields = line.strip().split()
if fields[1] != '00000000' or not int(fields[3], 16) & 2:
continue
return socket.inet_ntoa(struct.pack("<L", int(fields[2], 16)))