Share settings between modules
This patch change menu modules is such way that they share settings and change it in memory not on file system. Settings dumped on fs only when save called explicitly by user from UI. Partial-Bug: #1527111 Change-Id: If2930991501b1718174d1b84b0d8d53af6f6a789
This commit is contained in:
parent
2aa3d81bc7
commit
42f24e8aec
@ -30,7 +30,7 @@ from fuelmenu.common import dialog
|
||||
from fuelmenu.common import network
|
||||
import fuelmenu.common.urwidwrapper as widget
|
||||
from fuelmenu.common import utils
|
||||
from fuelmenu import settings
|
||||
from fuelmenu import settings as settings_module
|
||||
|
||||
log = logging.getLogger('fuelmenu.modulehelper')
|
||||
|
||||
@ -89,40 +89,31 @@ class ModuleHelper(object):
|
||||
settings[part1] = value
|
||||
|
||||
@classmethod
|
||||
def load(cls, modobj, ignoredparams=None):
|
||||
"""Returns settings found in settings files that are found in class
|
||||
def load_to_defaults(cls, settings, defaults, ignoredparams=None):
|
||||
"""Update module defaults by appropriate values from settings.
|
||||
|
||||
:param cls: ModuleHelper object
|
||||
:param modobj: object from calling class
|
||||
:param ignoredparams: list of parameters to skip lookup from settings
|
||||
:returns: OrderedDict of settings for calling class
|
||||
settings: Settings object
|
||||
defaults: module object's defaults from calling class
|
||||
ignoredparams: list of parameters to skip lookup from settings
|
||||
"""
|
||||
|
||||
# Read in yaml
|
||||
defaultsettings = settings.Settings().read(
|
||||
modobj.parent.defaultsettingsfile)
|
||||
usersettings = settings.Settings().read(modobj.parent.settingsfile)
|
||||
oldsettings = utils.dict_merge(defaultsettings, usersettings)
|
||||
|
||||
types_to_skip = (WidgetType.BUTTON, WidgetType.LABEL)
|
||||
for setting, setting_def in six.iteritems(modobj.defaults):
|
||||
for setting, setting_def in six.iteritems(defaults):
|
||||
if (setting_def.get('type') in types_to_skip or
|
||||
ignoredparams and setting in ignoredparams):
|
||||
continue
|
||||
try:
|
||||
setting_def["value"] = cls.get_setting(oldsettings, setting)
|
||||
setting_def["value"] = cls.get_setting(settings, setting)
|
||||
except KeyError:
|
||||
log.warning("Failed to load %s value from settings", setting)
|
||||
return oldsettings
|
||||
|
||||
@classmethod
|
||||
def save(cls, modobj, responses):
|
||||
newsettings = collections.OrderedDict()
|
||||
def make_settings_from_responses(cls, responses):
|
||||
"""Create new Settings object from responses."""
|
||||
newsettings = settings_module.Settings()
|
||||
|
||||
for setting in responses:
|
||||
cls.set_setting(newsettings,
|
||||
setting,
|
||||
responses[setting],
|
||||
modobj.oldsettings)
|
||||
cls.set_setting(newsettings, setting, responses[setting])
|
||||
return newsettings
|
||||
|
||||
@classmethod
|
||||
|
@ -11,7 +11,6 @@
|
||||
# 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 copy
|
||||
import logging
|
||||
import random as _random
|
||||
import string
|
||||
@ -23,44 +22,6 @@ from fuelmenu import consts
|
||||
log = logging.getLogger('fuelmenu.common.utils')
|
||||
random = _random.SystemRandom()
|
||||
|
||||
try:
|
||||
from collections import OrderedDict
|
||||
except ImportError:
|
||||
from ordereddict import OrderedDict
|
||||
|
||||
|
||||
def dict_merge(a, b):
|
||||
"""Recursively merges values in dicts from b into a
|
||||
|
||||
All values in b override a, even if b is not a dict:
|
||||
Example:
|
||||
x = {'a': {'b' : 'val'}}
|
||||
y = {'a': 'notval'}
|
||||
z = {'z': None}
|
||||
dict_merge(x, y) returns {'a': 'notval'}
|
||||
dict_merge(x, z) returns {'a': {'b': 'val'}, {'z': None}}
|
||||
dict_merge(z, x) returns {'z': None, 'a': {'b': 'val'}}
|
||||
|
||||
:param a: the first dict
|
||||
:param b: any value
|
||||
:returns: resulting value of b merged into a, with b taking precedence
|
||||
"""
|
||||
if not isinstance(a, (dict, OrderedDict)):
|
||||
raise TypeError('First parameter is not a dict')
|
||||
|
||||
result = copy.deepcopy(a)
|
||||
try:
|
||||
for k, v in b.iteritems():
|
||||
if k in result and isinstance(result[k],
|
||||
(dict, OrderedDict)):
|
||||
result[k] = dict_merge(result[k], v)
|
||||
else:
|
||||
result[k] = copy.deepcopy(v)
|
||||
except AttributeError:
|
||||
# Non-iterable objects should be just returned
|
||||
return b
|
||||
return result
|
||||
|
||||
|
||||
def get_deployment_mode():
|
||||
"""Report if any fuel containers are already created."""
|
||||
|
@ -14,6 +14,7 @@
|
||||
# under the License.
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from fuelmenu.common import dialog
|
||||
from fuelmenu.common import errors
|
||||
from fuelmenu.common import network
|
||||
@ -21,7 +22,9 @@ from fuelmenu.common import timeout
|
||||
from fuelmenu.common import urwidwrapper as widget
|
||||
from fuelmenu.common import utils
|
||||
from fuelmenu import consts
|
||||
from fuelmenu.settings import Settings
|
||||
from fuelmenu import settings as settings_module
|
||||
|
||||
|
||||
import logging
|
||||
import operator
|
||||
from optparse import OptionParser
|
||||
@ -83,13 +86,22 @@ class FuelSetup(object):
|
||||
self.footer = None
|
||||
self.frame = None
|
||||
self.screen = None
|
||||
self.defaultsettingsfile = os.path.join(os.path.dirname(__file__),
|
||||
"settings.yaml")
|
||||
self.settingsfile = consts.SETTINGS_FILE
|
||||
self.managediface = network.get_physical_ifaces()[0]
|
||||
# Set to true to move all settings to end
|
||||
self.globalsave = True
|
||||
self.version = utils.get_fuel_version()
|
||||
|
||||
# settings load
|
||||
self.settings = settings_module.Settings()
|
||||
|
||||
self.settings.load(
|
||||
os.path.join(os.path.dirname(__file__), "settings.yaml"),
|
||||
template_kwargs={"mos_version": self.version})
|
||||
|
||||
self.settings.load(
|
||||
consts.SETTINGS_FILE,
|
||||
template_kwargs={"mos_version": self.version})
|
||||
|
||||
self.main()
|
||||
self.choices = []
|
||||
|
||||
@ -304,6 +316,8 @@ class FuelSetup(object):
|
||||
except AttributeError as e:
|
||||
log.debug("Module %s does not have save function: %s"
|
||||
% (modulename, e))
|
||||
|
||||
self.settings.write(outfn=consts.SETTINGS_FILE)
|
||||
return True, None
|
||||
|
||||
|
||||
@ -370,12 +384,15 @@ def save_only(iface, settingsfile=consts.SETTINGS_FILE):
|
||||
default_settings_file = os.path.join(os.path.dirname(__file__),
|
||||
"settings.yaml")
|
||||
mos_version = utils.get_fuel_version()
|
||||
settings = Settings().read(
|
||||
|
||||
settings = settings_module.Settings()
|
||||
|
||||
settings.load(
|
||||
default_settings_file,
|
||||
template_kwargs={"mos_version": mos_version})
|
||||
settings.update(Settings().read(
|
||||
settingsfile,
|
||||
template_kwargs={"mos_version": mos_version}))
|
||||
|
||||
settings.load(settingsfile, template_kwargs={"mos_version": mos_version})
|
||||
|
||||
settings_upd = \
|
||||
{
|
||||
"ADMIN_NETWORK/interface": iface,
|
||||
@ -429,8 +446,7 @@ def save_only(iface, settingsfile=consts.SETTINGS_FILE):
|
||||
settings[setting] = settings_upd[setting]
|
||||
|
||||
# Write astute.yaml
|
||||
Settings().write(settings, defaultsfile=default_settings_file,
|
||||
outfn=settingsfile)
|
||||
settings.write(outfn=settingsfile)
|
||||
|
||||
|
||||
def main(*args, **kwargs):
|
||||
|
@ -27,7 +27,6 @@ from fuelmenu.common.modulehelper import BLANK_KEY
|
||||
from fuelmenu.common.modulehelper import ModuleHelper
|
||||
from fuelmenu.common.modulehelper import WidgetType
|
||||
from fuelmenu.common import utils
|
||||
from fuelmenu.settings import Settings
|
||||
|
||||
log = logging.getLogger('fuelmenu.mirrors')
|
||||
blank = urwid.Divider()
|
||||
@ -49,7 +48,6 @@ class bootstrapimg(urwid.WidgetWrap):
|
||||
self.priority = 55
|
||||
self.visible = True
|
||||
self.parent = parent
|
||||
self._mos_version = None
|
||||
|
||||
# UI Text
|
||||
self.header_content = ["Bootstrap image configuration"]
|
||||
@ -117,15 +115,9 @@ class bootstrapimg(urwid.WidgetWrap):
|
||||
"callback": self.add_repo
|
||||
}
|
||||
}
|
||||
self.oldsettings = self.load()
|
||||
self.load()
|
||||
self.screen = None
|
||||
|
||||
@property
|
||||
def mos_version(self):
|
||||
if not self._mos_version:
|
||||
self._mos_version = utils.get_fuel_version()
|
||||
return self._mos_version
|
||||
|
||||
@property
|
||||
def responses(self):
|
||||
ret = dict()
|
||||
@ -311,7 +303,6 @@ class bootstrapimg(urwid.WidgetWrap):
|
||||
return repos_for_ui
|
||||
|
||||
def add_repo(self, data=None):
|
||||
|
||||
defaults = self._get_fresh_defaults()
|
||||
repo_list = defaults[BOOTSTRAP_REPOS_KEY]['value']
|
||||
repo_list.append(
|
||||
@ -335,38 +326,18 @@ class bootstrapimg(urwid.WidgetWrap):
|
||||
log.warning("unexpected error: {0}".format(e))
|
||||
|
||||
def load(self):
|
||||
# Read in yaml
|
||||
default_settings = Settings().read(
|
||||
self.parent.defaultsettingsfile,
|
||||
template_kwargs={"mos_version": self.mos_version})
|
||||
settings = default_settings
|
||||
settings.update(Settings().read(self.parent.settingsfile))
|
||||
settings = self.parent.settings
|
||||
ModuleHelper.load_to_defaults(settings, self.defaults)
|
||||
|
||||
self._update_defaults(self.defaults, settings)
|
||||
self._select_fields_to_show(self.defaults)
|
||||
return settings
|
||||
|
||||
def _make_settings_from_responses(self, responses):
|
||||
settings = dict()
|
||||
for setting in responses:
|
||||
new_value = responses[setting]
|
||||
ModuleHelper.set_setting(settings, setting, new_value,
|
||||
self.oldsettings)
|
||||
return settings
|
||||
|
||||
def save(self, responses):
|
||||
newsettings = ModuleHelper.make_settings_from_responses(responses)
|
||||
self.parent.settings.merge(newsettings)
|
||||
|
||||
newsettings = self._make_settings_from_responses(responses)
|
||||
|
||||
Settings().write(newsettings,
|
||||
defaultsfile=self.parent.defaultsettingsfile,
|
||||
outfn=self.parent.settingsfile)
|
||||
|
||||
# Set oldsettings to reflect new settings
|
||||
self.oldsettings = newsettings
|
||||
# Update self.defaults
|
||||
|
||||
self._update_defaults(self.defaults, newsettings)
|
||||
self._update_defaults(self.defaults, self.parent.settings)
|
||||
|
||||
def check_url(self, url, proxies):
|
||||
try:
|
||||
@ -401,7 +372,7 @@ class bootstrapimg(urwid.WidgetWrap):
|
||||
def _get_fresh_defaults(self):
|
||||
defaults = copy.copy(self.defaults)
|
||||
self._update_defaults(defaults,
|
||||
self._make_settings_from_responses(
|
||||
ModuleHelper.make_settings_from_responses(
|
||||
self.responses))
|
||||
return defaults
|
||||
|
||||
|
@ -20,7 +20,6 @@ from fuelmenu.common.modulehelper import WidgetType
|
||||
from fuelmenu.common import network
|
||||
import fuelmenu.common.urwidwrapper as widget
|
||||
from fuelmenu.common import utils
|
||||
from fuelmenu.settings import Settings
|
||||
import logging
|
||||
import netaddr
|
||||
import urwid
|
||||
@ -78,8 +77,8 @@ to advertise via DHCP to nodes",
|
||||
"type": WidgetType.LABEL},
|
||||
}
|
||||
|
||||
self.load()
|
||||
self.extdhcp = True
|
||||
self.oldsettings = self.load()
|
||||
self.screen = None
|
||||
|
||||
def check(self, args):
|
||||
@ -246,14 +245,16 @@ interface first.")
|
||||
|
||||
# Extra checks for post-deployment changes
|
||||
if utils.is_post_deployment():
|
||||
settings = self.parent.settings
|
||||
|
||||
# Admin interface cannot change
|
||||
if self.activeiface != \
|
||||
self.oldsettings["ADMIN_NETWORK"]["interface"]:
|
||||
if self.activeiface != settings["ADMIN_NETWORK"]["interface"]:
|
||||
errors.append("Cannot change admin interface after deployment")
|
||||
|
||||
# PXE network range must contain previous PXE network range
|
||||
old_range = network.range(
|
||||
self.oldsettings["ADMIN_NETWORK"]["dhcp_pool_start"],
|
||||
self.oldsettings["ADMIN_NETWORK"]["dhcp_pool_end"])
|
||||
settings["ADMIN_NETWORK"]["dhcp_pool_start"],
|
||||
settings["ADMIN_NETWORK"]["dhcp_pool_end"])
|
||||
new_range = network.range(
|
||||
responses["ADMIN_NETWORK/dhcp_pool_start"],
|
||||
responses["ADMIN_NETWORK/dhcp_pool_end"])
|
||||
@ -286,42 +287,27 @@ interface first.")
|
||||
self.setNetworkDetails()
|
||||
|
||||
def load(self):
|
||||
oldsettings = ModuleHelper.load(self)
|
||||
if oldsettings["ADMIN_NETWORK"]["interface"] \
|
||||
in self.netsettings.keys():
|
||||
self.activeiface = oldsettings["ADMIN_NETWORK"]["interface"]
|
||||
return oldsettings
|
||||
settings = self.parent.settings
|
||||
ModuleHelper.load_to_defaults(settings, self.defaults)
|
||||
|
||||
iface = settings["ADMIN_NETWORK"]["interface"]
|
||||
if iface in self.netsettings.keys():
|
||||
self.activeiface = iface
|
||||
|
||||
def save(self, responses):
|
||||
# Generic settings start ##
|
||||
newsettings = ModuleHelper.save(self, responses)
|
||||
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] = self.oldsettings[part1]
|
||||
newsettings[part1][part2] = responses[setting]
|
||||
else:
|
||||
newsettings[setting] = responses[setting]
|
||||
# Generic settings end
|
||||
newsettings = ModuleHelper.make_settings_from_responses(responses)
|
||||
|
||||
# Need to calculate and netmask
|
||||
newsettings['ADMIN_NETWORK']['netmask'] = \
|
||||
self.netsettings[newsettings['ADMIN_NETWORK']['interface']][
|
||||
"netmask"]
|
||||
|
||||
Settings().write(newsettings,
|
||||
defaultsfile=self.parent.defaultsettingsfile,
|
||||
outfn=self.parent.settingsfile)
|
||||
|
||||
# Set oldsettings to reflect new settings
|
||||
self.oldsettings = newsettings
|
||||
# Update self.defaults
|
||||
for index, fieldname in enumerate(self.fields):
|
||||
if fieldname != "blank" and "label" not in fieldname:
|
||||
self.defaults[fieldname]['value'] = responses[fieldname]
|
||||
|
||||
self.parent.settings.merge(newsettings)
|
||||
self.parent.footer.set_text("Changes saved successfully.")
|
||||
|
||||
def getNetwork(self):
|
||||
|
@ -19,7 +19,7 @@ from fuelmenu.common import network
|
||||
from fuelmenu.common import replace
|
||||
import fuelmenu.common.urwidwrapper as widget
|
||||
from fuelmenu.common import utils
|
||||
from fuelmenu.settings import Settings
|
||||
|
||||
import logging
|
||||
import netaddr
|
||||
import os
|
||||
@ -73,7 +73,7 @@ DNS (space separated)",
|
||||
is accessible"}
|
||||
}
|
||||
|
||||
self.oldsettings = self.load()
|
||||
self.load()
|
||||
self.screen = None
|
||||
self.fixEtcHosts()
|
||||
|
||||
@ -232,7 +232,7 @@ is accessible"}
|
||||
if "localhost" in line:
|
||||
etchosts.write(line)
|
||||
elif responses["HOSTNAME"] in line \
|
||||
or self.oldsettings["HOSTNAME"] \
|
||||
or self.parent.settings["HOSTNAME"] \
|
||||
or self.netsettings[self.parent.managediface]['addr'] \
|
||||
in line:
|
||||
continue
|
||||
@ -282,7 +282,7 @@ is accessible"}
|
||||
# Precedence of DNS information:
|
||||
# Class defaults, fuelmenu default YAML, astute.yaml, uname,
|
||||
# /etc/resolv.conf
|
||||
oldsettings = ModuleHelper.load(self, ignoredparams=['TEST_DNS'])
|
||||
oldsettings = self.parent.settings
|
||||
|
||||
# Read hostname from uname
|
||||
try:
|
||||
@ -302,17 +302,9 @@ is accessible"}
|
||||
if nameservers:
|
||||
oldsettings["DNS_UPSTREAM"] = nameservers
|
||||
|
||||
for setting in self.defaults.keys():
|
||||
try:
|
||||
if "/" in setting:
|
||||
part1, part2 = setting.split("/")
|
||||
self.defaults[setting]["value"] = oldsettings[part1][part2]
|
||||
else:
|
||||
self.defaults[setting]["value"] = oldsettings[setting]
|
||||
except Exception:
|
||||
log.warning("No setting named %s found." % setting)
|
||||
continue
|
||||
return oldsettings
|
||||
ModuleHelper.load_to_defaults(oldsettings,
|
||||
self.defaults,
|
||||
ignoredparams=['TEST_DNS'])
|
||||
|
||||
def getDNS(self, resolver="/etc/resolv.conf"):
|
||||
nameservers = []
|
||||
@ -341,26 +333,9 @@ is accessible"}
|
||||
return searches, domain, ",".join(nameservers)
|
||||
|
||||
def save(self, responses):
|
||||
# Generic settings start
|
||||
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] = self.oldsettings[part1]
|
||||
newsettings[part1][part2] = responses[setting]
|
||||
else:
|
||||
newsettings[setting] = responses[setting]
|
||||
# Generic settings end
|
||||
newsettings = ModuleHelper.make_settings_from_responses(responses)
|
||||
self.parent.settings.merge(newsettings)
|
||||
|
||||
# log.debug(str(newsettings))
|
||||
Settings().write(newsettings,
|
||||
defaultsfile=self.parent.defaultsettingsfile,
|
||||
outfn=self.parent.settingsfile)
|
||||
|
||||
# Set oldsettings to reflect new settings
|
||||
self.oldsettings = newsettings
|
||||
# Update self.defaults
|
||||
for index, fieldname in enumerate(self.fields):
|
||||
if fieldname != "blank":
|
||||
|
@ -19,7 +19,6 @@ import urwid
|
||||
|
||||
from fuelmenu.common.modulehelper import ModuleHelper
|
||||
from fuelmenu.common.modulehelper import WidgetType
|
||||
from fuelmenu.settings import Settings
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@ -54,7 +53,7 @@ class feature_groups(urwid.WidgetWrap):
|
||||
"type": WidgetType.CHECKBOX,
|
||||
}
|
||||
}
|
||||
self.oldsettings = self.load()
|
||||
self.load()
|
||||
self.screen = None
|
||||
|
||||
@property
|
||||
@ -81,34 +80,23 @@ class feature_groups(urwid.WidgetWrap):
|
||||
|
||||
def load(self):
|
||||
# Read in yaml
|
||||
defaultsettings = Settings().read(self.parent.defaultsettingsfile)
|
||||
oldsettings = defaultsettings
|
||||
oldsettings.update(Settings().read(self.parent.settingsfile))
|
||||
oldsettings = self.parent.settings
|
||||
|
||||
for setting in self.defaults:
|
||||
try:
|
||||
part1, part2 = setting.split("/")
|
||||
self.defaults[setting]["value"] = part2 in oldsettings[part1]
|
||||
except Exception as e:
|
||||
log.warning("unexpected error: %s", e.message)
|
||||
return oldsettings
|
||||
|
||||
def save(self, responses):
|
||||
newsettings = {}
|
||||
for setting in responses.keys():
|
||||
part1, part2 = setting.split("/")
|
||||
if part1 not in newsettings:
|
||||
newsettings[part1] = []
|
||||
if responses[setting]:
|
||||
newsettings[part1].append(part2)
|
||||
settings = self.parent.settings
|
||||
newsettings = ModuleHelper.make_settings_from_responses(responses)
|
||||
settings.merge(newsettings)
|
||||
|
||||
Settings().write(newsettings,
|
||||
defaultsfile=self.parent.defaultsettingsfile,
|
||||
outfn=self.parent.settingsfile)
|
||||
|
||||
self.oldsettings = newsettings
|
||||
for setting in self.defaults:
|
||||
part1, part2 = setting.split("/")
|
||||
self.defaults[setting]["value"] = part2 in newsettings[part1]
|
||||
self.defaults[setting]["value"] = part2 in settings[part1]
|
||||
|
||||
def cancel(self, button):
|
||||
ModuleHelper.cancel(self, button)
|
||||
|
@ -13,14 +13,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
try:
|
||||
from collections import OrderedDict
|
||||
except Exception:
|
||||
# python 2.6 or earlier use backport
|
||||
from ordereddict import OrderedDict
|
||||
|
||||
from fuelmenu.common.modulehelper import ModuleHelper
|
||||
from fuelmenu.settings import Settings
|
||||
import logging
|
||||
import re
|
||||
import urwid
|
||||
@ -58,7 +51,7 @@ class fueluser(urwid.WidgetWrap):
|
||||
"value": ""},
|
||||
}
|
||||
|
||||
self.oldsettings = self.load()
|
||||
self.load()
|
||||
self.screen = None
|
||||
|
||||
def check(self, args):
|
||||
@ -139,33 +132,18 @@ class fueluser(urwid.WidgetWrap):
|
||||
return True
|
||||
|
||||
def save(self, responses):
|
||||
# Generic settings start
|
||||
newsettings = OrderedDict()
|
||||
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
|
||||
try:
|
||||
newsettings[part1] = self.oldsettings[part1]
|
||||
except Exception:
|
||||
if part1 not in newsettings.keys():
|
||||
newsettings[part1] = OrderedDict()
|
||||
log.warning("issues setting newsettings %s " % setting)
|
||||
log.warning("current newsettings: %s" % newsettings)
|
||||
newsettings[part1][part2] = responses[setting]
|
||||
else:
|
||||
newsettings[setting] = responses[setting]
|
||||
Settings().write(newsettings,
|
||||
defaultsfile=self.parent.defaultsettingsfile,
|
||||
outfn=self.parent.settingsfile)
|
||||
newsettings = ModuleHelper.make_settings_from_responses(responses)
|
||||
self.parent.settings.merge(newsettings)
|
||||
|
||||
self.parent.footer.set_text("Changes applied successfully.")
|
||||
# Reset fields
|
||||
self.cancel(None)
|
||||
|
||||
def load(self):
|
||||
return ModuleHelper.load(self, ignoredparams=['CONFIRM_PASSWORD'])
|
||||
ModuleHelper.load_to_defaults(
|
||||
self.parent.settings,
|
||||
self.defaults,
|
||||
ignoredparams=['CONFIRM_PASSWORD'])
|
||||
|
||||
def cancel(self, button):
|
||||
ModuleHelper.cancel(self, button)
|
||||
|
@ -18,7 +18,6 @@ from fuelmenu.common.modulehelper import ModuleHelper
|
||||
from fuelmenu.common.modulehelper import WidgetType
|
||||
import fuelmenu.common.urwidwrapper as widget
|
||||
from fuelmenu.common import utils
|
||||
from fuelmenu.settings import Settings
|
||||
import logging
|
||||
import re
|
||||
import urwid
|
||||
@ -63,7 +62,7 @@ class ntpsetup(urwid.WidgetWrap):
|
||||
# Load info
|
||||
self.gateway = self.get_default_gateway_linux()
|
||||
|
||||
self.oldsettings = self.load()
|
||||
self.load()
|
||||
self.screen = None
|
||||
|
||||
def check(self, args):
|
||||
@ -176,32 +175,18 @@ class ntpsetup(urwid.WidgetWrap):
|
||||
return ModuleHelper.get_default_gateway_linux()
|
||||
|
||||
def load(self):
|
||||
return ModuleHelper.load(self, ignoredparams=['ntpenabled'])
|
||||
ModuleHelper.load_to_defaults(
|
||||
self.parent.settings, self.defaults, ignoredparams=['ntpenabled'])
|
||||
|
||||
def save(self, responses):
|
||||
# Generic settings start
|
||||
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] = self.oldsettings[part1]
|
||||
newsettings[part1][part2] = responses[setting]
|
||||
else:
|
||||
newsettings[setting] = responses[setting]
|
||||
# Generic settings end
|
||||
settings = self.parent.settings
|
||||
newsettings = ModuleHelper.make_settings_from_responses(responses)
|
||||
settings.merge(newsettings)
|
||||
|
||||
Settings().write(newsettings,
|
||||
defaultsfile=self.parent.defaultsettingsfile,
|
||||
outfn=self.parent.settingsfile)
|
||||
|
||||
# Set oldsettings to reflect new settings
|
||||
self.oldsettings = newsettings
|
||||
# Update defaults
|
||||
for index, fieldname in enumerate(self.fields):
|
||||
if fieldname != "blank" and fieldname in newsettings:
|
||||
self.defaults[fieldname]['value'] = newsettings[fieldname]
|
||||
if fieldname != "blank" and fieldname in settings:
|
||||
self.defaults[fieldname]['value'] = settings[fieldname]
|
||||
|
||||
def checkNTP(self, server):
|
||||
# Use ntpdate to verify server answers NTP requests
|
||||
|
@ -13,7 +13,6 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import collections
|
||||
import logging
|
||||
import os
|
||||
|
||||
@ -91,7 +90,7 @@ class restore(urwid.WidgetWrap):
|
||||
},
|
||||
}
|
||||
|
||||
self.oldsettings = self.load()
|
||||
self.load()
|
||||
|
||||
def cancel(self, button):
|
||||
helper.ModuleHelper.cancel(self, button)
|
||||
@ -101,7 +100,7 @@ class restore(urwid.WidgetWrap):
|
||||
|
||||
def check_settings(self, settings):
|
||||
required_keys = []
|
||||
responses = collections.OrderedDict()
|
||||
responses = settings_utils.Settings()
|
||||
for key, subkeys in KEYS_TO_RESTORE:
|
||||
if key not in settings:
|
||||
if subkeys:
|
||||
@ -139,8 +138,8 @@ class restore(urwid.WidgetWrap):
|
||||
|
||||
path = os.path.abspath(path)
|
||||
try:
|
||||
with open(path) as f:
|
||||
settings = yaml.load(f)
|
||||
settings = settings_utils.Settings()
|
||||
settings.load(path)
|
||||
except IOError as err:
|
||||
self.show_error_msg("Could not fetch settings: {0}".format(err),
|
||||
exc_info=True)
|
||||
@ -181,22 +180,11 @@ class restore(urwid.WidgetWrap):
|
||||
return True
|
||||
|
||||
def load(self):
|
||||
return helper.ModuleHelper.load(self, ignoredparams=('PATH',))
|
||||
helper.ModuleHelper.load_to_defaults(
|
||||
self.parent.settings, self.defaults, ignoredparams=('PATH',))
|
||||
|
||||
def save(self, responses):
|
||||
newsettings = helper.ModuleHelper.save(self, responses)
|
||||
# TODO(akscram): The restore module writes settings itself into
|
||||
# the configuration file and requires from the
|
||||
# user to exit from the menu without saving
|
||||
# changes. It is a necessary requirement due to
|
||||
# the limitations of fuel menu. For more
|
||||
# information see the bug report
|
||||
# https://bugs.launchpad.net/fuel/+bug/1527111.
|
||||
settings_utils.Settings().write(
|
||||
newsettings,
|
||||
defaultsfile=self.parent.defaultsettingsfile,
|
||||
outfn=self.parent.settingsfile)
|
||||
self.oldsettings = newsettings
|
||||
self.parent.settings.merge(responses)
|
||||
|
||||
def screenUI(self):
|
||||
return helper.ModuleHelper.screenUI(
|
||||
|
@ -16,7 +16,6 @@
|
||||
import crypt
|
||||
from fuelmenu.common import modulehelper as helper
|
||||
from fuelmenu.common import utils
|
||||
from fuelmenu import settings as settings_module
|
||||
|
||||
import logging
|
||||
import urwid
|
||||
@ -123,13 +122,10 @@ class rootpw(urwid.WidgetWrap):
|
||||
return True
|
||||
|
||||
def save_settings(self, hashed_pwd):
|
||||
bootstrap = helper.ModuleHelper.load(self)['BOOTSTRAP']
|
||||
bootstrap['hashed_root_password'] = hashed_pwd
|
||||
|
||||
settings_module.Settings().write(
|
||||
{'BOOTSTRAP': bootstrap},
|
||||
defaultsfile=self.parent.defaultsettingsfile,
|
||||
outfn=self.parent.settingsfile)
|
||||
newsettings = {'BOOTSTRAP': {
|
||||
'hashed_root_password': hashed_pwd,
|
||||
}}
|
||||
self.parent.settings.merge(newsettings)
|
||||
|
||||
def cancel(self, button):
|
||||
helper.ModuleHelper.cancel(self, button)
|
||||
|
@ -13,14 +13,8 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
try:
|
||||
from collections import OrderedDict
|
||||
except Exception:
|
||||
# python 2.6 or earlier use backport
|
||||
from ordereddict import OrderedDict
|
||||
from fuelmenu.common.modulehelper import ModuleHelper
|
||||
from fuelmenu.common import pwgen
|
||||
from fuelmenu.settings import Settings
|
||||
import logging
|
||||
import urwid
|
||||
import urwid.raw_display
|
||||
@ -116,7 +110,7 @@ class servicepws(urwid.WidgetWrap):
|
||||
}
|
||||
self.fields = self.defaults.keys()
|
||||
|
||||
self.oldsettings = self.load()
|
||||
self.load()
|
||||
self.screen = None
|
||||
|
||||
def check(self, args):
|
||||
@ -144,35 +138,13 @@ class servicepws(urwid.WidgetWrap):
|
||||
self.save(responses)
|
||||
|
||||
def load(self):
|
||||
return ModuleHelper.load(self)
|
||||
ModuleHelper.load_to_defaults(self.parent.settings, self.defaults)
|
||||
|
||||
def save(self, responses):
|
||||
# Generic settings start
|
||||
newsettings = OrderedDict()
|
||||
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
|
||||
try:
|
||||
newsettings[part1] = self.oldsettings[part1]
|
||||
except Exception:
|
||||
if part1 not in newsettings.keys():
|
||||
newsettings[part1] = OrderedDict()
|
||||
log.warning("issues setting newsettings %s " % setting)
|
||||
log.warning("current newsettings: %s" % newsettings)
|
||||
newsettings[part1][part2] = responses[setting]
|
||||
else:
|
||||
newsettings[setting] = responses[setting]
|
||||
Settings().write(newsettings,
|
||||
defaultsfile=self.parent.defaultsettingsfile,
|
||||
outfn=self.parent.settingsfile)
|
||||
|
||||
# Generic settings end
|
||||
newsettings = ModuleHelper.make_settings_from_responses(responses)
|
||||
self.parent.settings.merge(newsettings)
|
||||
log.debug('done saving servicepws')
|
||||
|
||||
# Set oldsettings to reflect new settings
|
||||
self.oldsettings = newsettings
|
||||
# Update defaults
|
||||
for index, fieldname in enumerate(self.fields):
|
||||
if fieldname != "blank" and fieldname in newsettings:
|
||||
|
@ -13,6 +13,7 @@
|
||||
# under the License.
|
||||
|
||||
import collections
|
||||
import copy
|
||||
import logging
|
||||
from string import Template
|
||||
|
||||
@ -24,6 +25,8 @@ except Exception:
|
||||
|
||||
import yaml
|
||||
|
||||
log = logging.getLogger('fuelmenu.settings')
|
||||
|
||||
|
||||
def construct_ordered_mapping(self, node, deep=False):
|
||||
if not isinstance(node, yaml.MappingNode):
|
||||
@ -76,29 +79,74 @@ def represent_ordered_mapping(self, tag, mapping, flow_style=None):
|
||||
else:
|
||||
node.flow_style = best_style
|
||||
return node
|
||||
|
||||
# Settings object is the instance of OrderedDict, so multi_representer
|
||||
# of OrderedDict can handle both types (OrderedDict and Settings)
|
||||
yaml.representer.Representer.add_multi_representer(
|
||||
OrderedDict, yaml.representer.SafeRepresenter.represent_dict)
|
||||
yaml.representer.BaseRepresenter.represent_mapping = represent_ordered_mapping
|
||||
yaml.representer.Representer.add_representer(OrderedDict, yaml.representer.
|
||||
SafeRepresenter.represent_dict)
|
||||
|
||||
|
||||
class Settings(object):
|
||||
def read(self, yamlfile, template_kwargs=None):
|
||||
def dict_merge(a, b):
|
||||
"""Recursively merges values in dicts from b into a
|
||||
|
||||
All values in b override a, even if b is not a dict:
|
||||
Example:
|
||||
x = {'a': {'b' : 'val'}}
|
||||
y = {'a': 'notval'}
|
||||
z = {'z': None}
|
||||
dict_merge(x, y) returns {'a': 'notval'}
|
||||
dict_merge(x, z) returns {'a': {'b': 'val'}, {'z': None}}
|
||||
dict_merge(z, x) returns {'z': None, 'a': {'b': 'val'}}
|
||||
|
||||
:param a: the first dict
|
||||
:param b: any value
|
||||
:returns: resulting value of b merged into a, with b taking precedence
|
||||
"""
|
||||
if not isinstance(a, (dict, OrderedDict)):
|
||||
raise TypeError('First parameter is not a dict')
|
||||
|
||||
result = copy.deepcopy(a)
|
||||
try:
|
||||
for k, v in b.iteritems():
|
||||
if k in result and isinstance(result[k],
|
||||
(dict, OrderedDict)):
|
||||
result[k] = dict_merge(result[k], v)
|
||||
else:
|
||||
result[k] = copy.deepcopy(v)
|
||||
except AttributeError:
|
||||
# Non-iterable objects should be just returned
|
||||
return b
|
||||
return result
|
||||
|
||||
|
||||
class Settings(OrderedDict):
|
||||
def load(self, settings_file, template_kwargs=None):
|
||||
"""Load setting from file and merge them to existing object
|
||||
|
||||
settings_file: path to setings.yaml file
|
||||
|
||||
template_kwargs: dict with parameters that will be placed
|
||||
instead labeles in settings file before yaml parsing
|
||||
"""
|
||||
try:
|
||||
with open(yamlfile) as infile:
|
||||
with open(settings_file) as infile:
|
||||
settings = yaml.load(Template(
|
||||
infile.read()).safe_substitute(template_kwargs or {}))
|
||||
return settings or OrderedDict()
|
||||
except Exception:
|
||||
if yamlfile is not None:
|
||||
logging.error("Unable to read YAML: %s", yamlfile)
|
||||
return OrderedDict()
|
||||
|
||||
def write(self, newvalues, tree=None, defaultsfile='settings.yaml',
|
||||
outfn='mysettings.yaml'):
|
||||
settings = self.read(defaultsfile)
|
||||
settings.update(self.read(outfn))
|
||||
settings.update(newvalues)
|
||||
self.merge(settings)
|
||||
except Exception:
|
||||
log.error("Unable to read YAML: %s", settings_file)
|
||||
|
||||
return self
|
||||
|
||||
def write(self, outfn='mysettings.yaml'):
|
||||
"""Write settings to file."""
|
||||
with open(outfn, 'w') as outfile:
|
||||
yaml.dump(settings, outfile, default_style='"',
|
||||
yaml.dump(self, outfile, default_style='"',
|
||||
default_flow_style=False)
|
||||
return True
|
||||
return True
|
||||
|
||||
def merge(self, other):
|
||||
"""Merge this settings object with other."""
|
||||
self.update(dict_merge(self, other))
|
||||
|
@ -65,4 +65,4 @@ BOOTSTRAP:
|
||||
suite: "mos${mos_version}-holdback"
|
||||
type: "deb"
|
||||
PRODUCTION: docker
|
||||
FEATURE_GROUPS: []
|
||||
FEATURE_GROUPS: {}
|
||||
|
@ -19,6 +19,7 @@ import netifaces
|
||||
import unittest
|
||||
|
||||
from fuelmenu.common import modulehelper
|
||||
from fuelmenu import settings as settings_module
|
||||
|
||||
|
||||
def custom_mock_open(lines):
|
||||
@ -81,7 +82,6 @@ class TestModuleHelperGet(TestModuleHelperBase):
|
||||
self.settings, incorrect_key)
|
||||
|
||||
|
||||
@mock.patch('fuelmenu.settings.Settings.read', side_effect=lambda x: x)
|
||||
@mock.patch('fuelmenu.common.modulehelper.ModuleHelper.get_setting',
|
||||
return_value='loaded')
|
||||
class TestModuleHelperLoad(TestModuleHelperBase):
|
||||
@ -89,8 +89,8 @@ class TestModuleHelperLoad(TestModuleHelperBase):
|
||||
super(TestModuleHelperLoad, self).setUp()
|
||||
self.modobj.defaults = dict()
|
||||
self.modobj.parent = mock.Mock()
|
||||
self.modobj.parent.defaultsettingsfile = {'key1': 'value1'}
|
||||
self.modobj.parent.settingsfile = {'key2': 'value2'}
|
||||
self.modobj.parent.settings = settings_module.Settings(
|
||||
{'key1': 'value1', 'key2': 'value2'})
|
||||
|
||||
def test_load_types_skipping(self, *_):
|
||||
widget_types = {
|
||||
@ -110,8 +110,7 @@ class TestModuleHelperLoad(TestModuleHelperBase):
|
||||
},
|
||||
})
|
||||
|
||||
self._check('load', {'key2': 'value2', 'key1': 'value1'},
|
||||
self.modobj)
|
||||
self._run('load_to_defaults', self.modobj, self.modobj.defaults)
|
||||
for _, setting in self.modobj.defaults.items():
|
||||
self.assertEqual(
|
||||
widget_types[setting['type']], setting['value'],
|
||||
@ -125,8 +124,8 @@ class TestModuleHelperLoad(TestModuleHelperBase):
|
||||
3: {'value': 'skipped', 'should': 'skipped'},
|
||||
})
|
||||
ignores = [1, 3]
|
||||
self._check('load', {'key2': 'value2', 'key1': 'value1'},
|
||||
self.modobj, ignores)
|
||||
self._run('load_to_defaults', self.modobj,
|
||||
self.modobj.defaults, ignores)
|
||||
|
||||
for _, setting in self.modobj.defaults.items():
|
||||
self.assertEqual(setting['value'], setting['should'])
|
||||
@ -136,32 +135,25 @@ class TestModuleHelperLoad(TestModuleHelperBase):
|
||||
self, m_warning, m_get_setting, *_):
|
||||
m_get_setting.side_effect = KeyError
|
||||
self.modobj.defaults.update({'key': {'value': ''}})
|
||||
self._check('load', {'key2': 'value2', 'key1': 'value1'},
|
||||
self.modobj)
|
||||
self._run('load_to_defaults', self.modobj, self.modobj.defaults)
|
||||
self.assertEqual(self.modobj.defaults['key']['value'], '')
|
||||
m_warning.assert_called_once_with(
|
||||
"Failed to load %s value from settings", 'key')
|
||||
|
||||
def test_load_settings(self, *_):
|
||||
self._check('load', {'key1': 'value1', 'key2': 'value2'}, self.modobj)
|
||||
|
||||
|
||||
@mock.patch('fuelmenu.common.modulehelper.ModuleHelper.set_setting',
|
||||
return_value='')
|
||||
class TestModuleHelperSave(TestModuleHelperBase):
|
||||
def setUp(self):
|
||||
super(TestModuleHelperSave, self).setUp()
|
||||
self.modobj.oldsettings = mock.Mock()
|
||||
|
||||
def test_save(self, m_set_setting):
|
||||
def test_make_settings_from_responses(self):
|
||||
responses = {
|
||||
'key1': 'value1',
|
||||
'key2': 'value2'
|
||||
'key2/key3': 'value2'
|
||||
}
|
||||
self._check('save', {}, self.modobj, responses)
|
||||
for key, value in responses.items():
|
||||
m_set_setting.assert_any_call(
|
||||
{}, key, value, self.modobj.oldsettings)
|
||||
|
||||
expected = {'key1': 'value1', 'key2': {'key3': 'value2'}}
|
||||
self._check('make_settings_from_responses', expected, responses)
|
||||
|
||||
|
||||
class TestModuleHelperCancel(TestModuleHelperBase):
|
||||
|
@ -23,39 +23,6 @@ import unittest
|
||||
|
||||
|
||||
class TestUtils(unittest.TestCase):
|
||||
|
||||
def test_dict_merge_simple(self):
|
||||
a = {'a': 1}
|
||||
b = {'b': 2}
|
||||
data = utils.dict_merge(a, b)
|
||||
self.assertEqual({'a': 1, 'b': 2}, data)
|
||||
|
||||
def test_dict_merge_intended_behavior(self):
|
||||
"""If b is not a dict, it is the result."""
|
||||
|
||||
a = {'a': 1}
|
||||
b = None
|
||||
data = utils.dict_merge(a, b)
|
||||
self.assertEqual(None, data)
|
||||
|
||||
def test_dict_merge_bad_data(self):
|
||||
"""If a is not a dict, it should raise TypeError."""
|
||||
a = {'a': 1}
|
||||
b = None
|
||||
c = 1
|
||||
d = (1, 2, 3)
|
||||
e = set(['A', 'B', 'C'])
|
||||
self.assertRaises(TypeError, utils.dict_merge, b, a)
|
||||
self.assertRaises(TypeError, utils.dict_merge, c, a)
|
||||
self.assertRaises(TypeError, utils.dict_merge, d, a)
|
||||
self.assertRaises(TypeError, utils.dict_merge, e, a)
|
||||
|
||||
def test_dict_merge_override(self):
|
||||
a = {'a': {'c': 'val'}}
|
||||
b = {'b': 2, 'a': 'notval'}
|
||||
data = utils.dict_merge(a, b)
|
||||
self.assertEqual({'a': 'notval', 'b': 2}, data)
|
||||
|
||||
def make_process_mock(self, return_code=0, retval=('stdout', 'stderr')):
|
||||
process_mock = mock.Mock(
|
||||
communicate=mock.Mock(return_value=retval))
|
||||
|
@ -14,39 +14,102 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
try:
|
||||
from collections import OrderedDict
|
||||
except Exception:
|
||||
# python 2.6 or earlier use backport
|
||||
from ordereddict import OrderedDict
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
import mock
|
||||
import yaml
|
||||
|
||||
from fuelmenu import settings
|
||||
from fuelmenu import settings as settings_module
|
||||
|
||||
|
||||
def test_read_settings(tmpdir):
|
||||
yaml_file = tmpdir.join("yamlfile.yaml")
|
||||
yaml_file.write("""
|
||||
class TestDictMege(unittest.TestCase):
|
||||
def test_dict_merge_simple(self):
|
||||
a = {'a': 1}
|
||||
b = {'b': 2}
|
||||
data = settings_module.dict_merge(a, b)
|
||||
self.assertEqual({'a': 1, 'b': 2}, data)
|
||||
|
||||
def test_dict_merge_intended_behavior(self):
|
||||
"""If b is not a dict, it is the result."""
|
||||
|
||||
a = {'a': 1}
|
||||
b = None
|
||||
data = settings_module.dict_merge(a, b)
|
||||
self.assertEqual(None, data)
|
||||
|
||||
def test_dict_merge_bad_data(self):
|
||||
"""If a is not a dict, it should raise TypeError."""
|
||||
a = {'a': 1}
|
||||
b = None
|
||||
c = 1
|
||||
d = (1, 2, 3)
|
||||
e = {'A', 'B', 'C'}
|
||||
self.assertRaises(TypeError, settings_module.dict_merge, b, a)
|
||||
self.assertRaises(TypeError, settings_module.dict_merge, c, a)
|
||||
self.assertRaises(TypeError, settings_module.dict_merge, d, a)
|
||||
self.assertRaises(TypeError, settings_module.dict_merge, e, a)
|
||||
|
||||
def test_dict_merge_override(self):
|
||||
a = {'a': {'c': 'val'}}
|
||||
b = {'b': 2, 'a': 'notval'}
|
||||
data = settings_module.dict_merge(a, b)
|
||||
self.assertEqual({'a': 'notval', 'b': 2}, data)
|
||||
|
||||
|
||||
class TestSettings(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.directory = tempfile.mkdtemp()
|
||||
|
||||
yaml_file = os.path.join(self.directory, "__yamlfile.yaml")
|
||||
open(yaml_file, 'w').write("""
|
||||
sample:
|
||||
- one
|
||||
- a: b
|
||||
c: d
|
||||
one:
|
||||
a: b
|
||||
c: d
|
||||
""")
|
||||
data = settings.Settings().read(yaml_file.strpath)
|
||||
assert data == {
|
||||
'sample': [
|
||||
'one',
|
||||
{
|
||||
'a': 'b',
|
||||
'c': 'd',
|
||||
self.settings = settings_module.Settings()
|
||||
self.settings.load(yaml_file)
|
||||
|
||||
def tearDown(self):
|
||||
shutil.rmtree(self.directory, ignore_errors=True)
|
||||
|
||||
def test_read_settings(self):
|
||||
self.assertEqual(self.settings, {
|
||||
'sample': {
|
||||
'one': {
|
||||
'a': 'b',
|
||||
'c': 'd',
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
assert isinstance(data, OrderedDict)
|
||||
})
|
||||
|
||||
def test_merge_settings(self):
|
||||
yaml_file = os.path.join(self.directory, "yamlfile.yaml")
|
||||
open(yaml_file, 'w').write("""{sample: {one: {a: 666}}}""")
|
||||
|
||||
@mock.patch('fuelmenu.settings.file', side_effect=Exception('Error'))
|
||||
def test_read_settings_with_error(_):
|
||||
data = settings.Settings().read('some_path')
|
||||
assert data == {}
|
||||
assert isinstance(data, OrderedDict)
|
||||
self.settings.load(yaml_file)
|
||||
|
||||
self.assertEqual(self.settings, {
|
||||
'sample': {
|
||||
'one': {
|
||||
'a': 666,
|
||||
'c': 'd',
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@mock.patch('__builtin__.open', side_effect=Exception('Error'))
|
||||
def test_read_settings_with_error(self, _):
|
||||
data = settings_module.Settings()
|
||||
data.load('some_path')
|
||||
self.assertEqual(data, {})
|
||||
|
||||
def test_write_settings(self):
|
||||
outfile = os.path.join(self.directory, 'out.yaml')
|
||||
self.settings.write(outfile)
|
||||
|
||||
self.assertTrue(os.path.exists(outfile))
|
||||
self.assertTrue(yaml.safe_load(open(outfile)) == self.settings)
|
||||
|
Loading…
Reference in New Issue
Block a user