From mboxrd@z Thu Jan 1 00:00:00 1970 From: rmccabe@sourceware.org Date: 12 Dec 2007 15:43:15 -0000 Subject: [Cluster-devel] conga/luci/site/luci/Extensions LuciValidation.py Message-ID: <20071212154315.848.qmail@sourceware.org> List-Id: To: cluster-devel.redhat.com MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit CVSROOT: /cvs/cluster Module name: conga Changes by: rmccabe at sourceware.org 2007-12-12 15:43:15 Added files: luci/site/luci/Extensions: LuciValidation.py Log message: Separate out cluster form post validation to allow async validation Patches: http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/LuciValidation.py.diff?cvsroot=cluster&r1=NONE&r2=1.1 /cvs/cluster/conga/luci/site/luci/Extensions/LuciValidation.py,v --> standard output revision 1.1 --- conga/luci/site/luci/Extensions/LuciValidation.py +++ - 2007-12-12 15:43:15.733027000 +0000 @@ -0,0 +1,1185 @@ +# Copyright (C) 2007 Red Hat, Inc. +# +# This program is free software; you can redistribute +# it and/or modify it under the terms of version 2 of the +# GNU General Public License as published by the +# Free Software Foundation. + +from ClusterModel.RefObject import RefObject +from ClusterModel.Service import Service +from ClusterModel.Method import Method +from ClusterModel.Fence import Fence +from ClusterModel.FailoverDomain import FailoverDomain +from ClusterModel.FailoverDomainNode import FailoverDomainNode +from ClusterModel.QuorumD import QuorumD +from ClusterModel.Heuristic import Heuristic +from ClusterModel.FenceXVMd import FenceXVMd +from ClusterModel.Lockserver import Lockserver +from ClusterModel.Vm import Vm + +from conga_constants import LUCI_DEBUG_MODE, FDOM, FDOM_ADD, SERVICE_CONFIG, SERVICE_ADD, VM_CONFIG, VM_ADD +from FenceHandler import validateFenceDevice, validateNewFenceDevice, validate_fenceinstance, FD_VAL_SUCCESS + +from LuciSyslog import get_logger +from xml.dom import minidom +from ResourceHandler import create_resource +from LuciZope import GetReqVars + +luci_log = get_logger() + +def validate_node_fence_config(model, request): + errors = list() + + fvar = GetReqVars(request, + [ 'fence_xml', 'fence_level', 'nodename', 'clustername' ]) + + if fvar['fence_xml'] is None: + if LUCI_DEBUG_MODE is True: + luci_log.debug_verbose('vNFC0: no fence_xml for node %s' \ + % fvar['nodename']) + return (False, {'errors': ['No fence data was supplied']}) + + if fvar['fence_level'] is None: + if LUCI_DEBUG_MODE is True: + luci_log.debug_verbose('vNFC1: no fence level for %s' \ + % fvar['nodename']) + return (False, {'errors': ['No fence level was supplied']}) + + try: + fence_level = int(fvar['fence_level']) + except Exception, e: + if LUCI_DEBUG_MODE is True: + luci_log.debug_verbose('vNFC2: invalid fence level: %s: %r %s' \ + % (fvar['fence_level'], e, str(e))) + return (False, {'errors': ['"%s" is an invalid fence level' % fvar['fence_level'] ]}) + + nodename = fvar['nodename'] + if nodename is None: + if LUCI_DEBUG_MODE is True: + luci_log.debug_verbose('vNFC3: no nodename: %r %s' % (e, str(e))) + return (False, {'errors': ['No node name was given']}) + + clustername = fvar['clustername'] + if clustername is None: + if LUCI_DEBUG_MODE is True: + luci_log.debug_verbose('vNFC4: no clustername: %r %s' % (e, str(e))) + return (False, {'errors': ['No cluster name was given']}) + + try: + doc = minidom.parseString(fvar['fence_xml']) + except Exception, e: + if LUCI_DEBUG_MODE is True: + luci_log.debug_verbose('vNFC7: error: %r %s' % (e, str(e))) + return (False, {'errors': ['The fence data submitted is not properly formed']}) + + try: + node = model.retrieveNodeByName(nodename) + except Exception, e: + if LUCI_DEBUG_MODE is True: + luci_log.debug_verbose('vNFC8: unable to find node name %s in current node list: %r %s' % (nodename, e, str(e))) + return (False, {'errors': ['Unable to find the cluster node %s in the node list' % nodename ]}) + + levels = node.getFenceLevels() + try: + method_id = levels[fence_level - 1].getAttribute('name') + if not method_id: + raise Exception, 'No method ID' + fence_method = Method() + fence_method.addAttribute('name', str(method_id)) + levels[fence_level - 1] = fence_method + except Exception, e: + method_id = fence_level + fence_method = Method() + fence_method.addAttribute('name', str(method_id)) + + forms = doc.getElementsByTagName('form') + if len(forms) < 1: + delete_target = None + for l in levels: + # delete the fence level + if l.getAttribute('name') == method_id: + delete_target = l + break + if delete_target is not None: + try: + node.getChildren()[0].removeChild(delete_target) + except Exception, e: + if LUCI_DEBUG_MODE is True: + luci_log.debug_verbose('vNFC9: %s: %r %s' \ + % (method_id, e, str(e))) + return (False, {'errors': ['An error occurred while deleting fence method %s' % method_id ]}) + else: + return (True, {'messages': ['No changes were made'] }) + + form_hash = {} + for i in forms: + form_id = i.getAttribute('id') + if not form_id: + continue + ielems = i.getElementsByTagName('input') + if not ielems or len(ielems) < 1: + continue + + dummy_form = {} + + for i in ielems: + try: + input_type = str(i.getAttribute('type')) + except Exception, e: + if LUCI_DEBUG_MODE is True: + luci_log.debug_verbose('vNFC10: input type: %r %s' \ + % (e, str(e))) + continue + + if not input_type or input_type == 'button': + continue + + try: + dummy_form[str(i.getAttribute('name'))] = str(i.getAttribute('value')) + except Exception, e: + if LUCI_DEBUG_MODE is True: + luci_log.debug_verbose('vNFC11: parsing XML: %r %s' \ + % (e, str(e))) + + if len(dummy_form) < 1: + continue + + if dummy_form.has_key('fence_instance'): + try: + parent = dummy_form['parent_fencedev'] + except: + if LUCI_DEBUG_MODE is True: + luci_log.debug_verbose('vNFC12: no parent for instance') + return (False, {'errors': [ 'Unable to determine what device the current instance uses' ]}) + + try: + form_hash[parent][1].append(dummy_form) + del dummy_form['fence_instance'] + except Exception, e: + if LUCI_DEBUG_MODE is True: + luci_log.debug_verbose('vNFC13: no parent for instance') + return (False, {'errors': [ 'Unable to determine what device the current instance uses' ]}) + else: + form_hash[form_id] = (dummy_form, list()) + + fh_keys = form_hash.keys() + fh_keys.sort() + for i in fh_keys: + fencedev_name = None + fencedev_unknown = False + + try: + fence_form, instance_list = form_hash[i] + except Exception, e: + if LUCI_DEBUG_MODE is True: + luci_log.debug_verbose('vNFC14: %r %s' % (e, str(e))) + continue + + try: + fence_type = fence_form['fence_type'] + if not fence_type: + raise Exception, 'fence type is blank' + except Exception, e: + if LUCI_DEBUG_MODE is True: + luci_log.debug_verbose('vNFC15: %s: %r %s' % (i, e, str(e))) + fence_type = None + + if fence_form.has_key('existing_device'): + try: + fencedev_name = fence_form['name'] + if not fencedev_name.strip(): + raise Exception, 'no fence name' + except Exception, e: + errors.append('You must provide a unique name for all fence devices') + continue + + if fence_type is None: + # An unknown fence device agent. Pull the data out of + # the model and persist it and all instances. + # All we care about is its name. + fencedev_unknown = True + else: + if not fence_form.has_key('sharable'): + # If it's a shared fence device that already exists, the + # user could not have edited it (without playing dirty + # games), so it's safe to pull the existing entry from + # the model. All we need is the device name, and nothing + # else needs to be done here. + # + # For an existing non-shared device update the device + # in the model, since the user could have edited it. + retcode, retmsg = validateFenceDevice(fence_form, model) + if retcode != FD_VAL_SUCCESS: + errors.extend(retmsg) + continue + else: + fencedev_name = retmsg + + # Add back the tags under the method block + # for the fence instance + if type == 'fence_manual': + instance_list.append({'name': fencedev_name, 'nodename': nodename }) + else: + instance_list.append({'name': fencedev_name }) + else: + # The user created a new fence device. + retcode, retmsg = validateNewFenceDevice(fence_form, model) + if retcode != FD_VAL_SUCCESS: + errors.extend(retmsg) + continue + else: + fencedev_name = retmsg + + # If it's not shared, we need to create an instance form + # so the appropriate XML goes into the block inside + # . All we need for that is the device name. + if not fence_form.has_key('sharable'): + if type == 'fence_manual': + instance_list.append({'name': fencedev_name, 'nodename': nodename }) + else: + instance_list.append({'name': fencedev_name }) + + if fencedev_unknown is True: + # Save any instances for this fence device. + # XXX FIX ME - instances must be saved. + pass + + for inst in instance_list: + retcode, retobj = validate_fenceinstance(inst, fencedev_name, fence_type) + if retcode != FD_VAL_SUCCESS: + errors.extend(retobj) + continue + fence_method.addChild(retobj) + + if len(node.getChildren()) > 0: + # There's already a block + found_target = False + for idx in xrange(len(levels)): + if levels[idx].getAttribute('name') == method_id: + found_target = True + break + + if found_target is False: + # There's a fence block, but no relevant method + # block + node.getChildren()[0].addChild(fence_method) + else: + # There is no tag under the node yet. + fence_node = Fence() + fence_node.addChild(fence_method) + node.addChild(fence_node) + + if len(errors) > 0: + return (False, {'errors': errors }) + return (True, {}) + +def validate_clusvc_add(model, request): + errors = list() + fvar = GetReqVars(request, [ 'form_xml', 'domain', 'recovery', 'svc_name', 'action' ]) + + form_xml = fvar['form_xml'] + if form_xml is None: + form_xml = '' + if LUCI_DEBUG_MODE is True: + luci_log.debug_verbose('vSA0: no form_xml') + + forms = [] + if form_xml.strip(): + try: + doc = minidom.parseString(form_xml) + forms = doc.getElementsByTagName('form') + if len(forms) < 1: + raise Exception, 'invalid XML' + except Exception, e: + if LUCI_DEBUG_MODE is True: + luci_log.debug_verbose('vSA1: error: %r %s: %r' % (e, str(e), form_xml)) + return (False, { 'errors': [ 'The resource data submitted for this service is not properly formed' ]}) + + form_hash = {} + form_hash['toplevel'] = { 'form': None, 'kids': [] } + for i in forms: + form_id = i.getAttribute('id') + form_parent = i.getAttribute('parent') + if not form_id or not form_parent: + continue + ielems = i.getElementsByTagName('input') + if not ielems or len(ielems) < 1: + continue + if not form_id in form_hash: + form_hash[form_id] = {'form': i, 'kids': []} + elif not form_hash[form_id]['form']: + form_hash[form_id]['form'] = i + if not form_parent in form_hash: + form_hash[form_parent] = {'form': None, 'kids': []} + form_hash[form_parent]['kids'].append(form_id) + dummy_form = {} + + for i in ielems: + try: + input_type = str(i.getAttribute('type')) + except: + continue + if not input_type or input_type == 'button': + continue + try: + dummy_form[str(i.getAttribute('name'))] = str(i.getAttribute('value')) + except Exception, e: + if LUCI_DEBUG_MODE is True: + luci_log.debug_verbose('vSA2: parsing XML: %r %s' \ + % (e, str(e))) + + try: + res_type = dummy_form['type'].strip() + if not res_type: + raise Exception, 'no resource type' + except Exception, e: + if LUCI_DEBUG_MODE is True: + luci_log.debug_verbose('vSA3: %r %s' % (e, str(e))) + return (False, { 'errors': [ 'No resource type was specified' ]}) + + try: + if res_type == 'ip': + dummy_form['resourceName'] = dummy_form['ip_address'] + except Exception, e: + if LUCI_DEBUG_MODE is True: + luci_log.debug_verbose('vSA3a: type is ip but no addr: %r %s' \ + % (e, str(e))) + return (False, { 'errors': [ 'No IP address was given' ]}) + + try: + if dummy_form.has_key('immutable'): + newRes = model.getResourceByName(dummy_form['resourceName']) + resObj = RefObject(newRes) + resObj.setRef(newRes.getName()) + else: + resObj = create_resource(res_type, dummy_form, model) + except Exception, e: + resObj = None + if LUCI_DEBUG_MODE is True: + luci_log.debug_verbose('vSA4: type %s: %r %s' \ + % (res_type, e, str(e))) + + if resObj is None: + return (False, { 'errors': [ 'An error occurred while adding %s' % res_type ]}) + + if dummy_form.has_key('__independent_subtree'): + resObj.addAttribute('__independent_subtree', '1') + else: + resObj.removeAttribute('__independent_subtree') + form_hash[form_id]['obj'] = resObj + + if len(errors) > 0: + return (False, {'errors': errors}) + + fdom = fvar['domain'] + + recovery = fvar['recovery'] + if recovery is not None and recovery != 'restart' and recovery != 'relocate' and recovery != 'disable': + errors.append('You entered an invalid recovery option: "%s" Valid options are "restart" "relocate" and "disable."') + + service_name = fvar['svc_name'] + if service_name is None: + if LUCI_DEBUG_MODE is True: + luci_log.debug_verbose('vSA5: no service name') + errors.append('No service name was given') + + autostart = '1' + try: + if not request.form.has_key('autostart') or request.form['autostart'] == '0': + autostart = '0' + except Exception, e: + autostart = None + if LUCI_DEBUG_MODE is True: + luci_log.debug_verbose('vSA5a: error getting autostart: %r %s' \ + % (e, str(e))) + + exclusive = '0' + try: + if not request.form.has_key('exclusive') or request.form['exclusive'] != '1': + exclusive = '0' + else: + exclusive = '1' + except Exception, e: + exclusive = '0' + + try: + cur_service = model.retrieveServiceByName(service_name) + except Exception, e: + if LUCI_DEBUG_MODE is True: + luci_log.debug_verbose('vSA5c: no service named %s found: %r %s' \ + % (service_name, e, str(e))) + cur_service = None + + action = fvar['action'] + if action is None: + return (False, {'errors': [ 'No action was given for service %s' % service_name ] }) + + if action == 'edit': + if cur_service is None: + return (False, {'errors': [ 'The service %s could not be found for editing' % service_name ]}) + model.deleteService(service_name) + elif action == 'add': + if cur_service is not None: + return (False, {'errors': [ 'A service with the name %s already exists' % service_name ]}) + else: + if LUCI_DEBUG_MODE is True: + luci_log.debug_verbose('vSA4a: unknown action %s' \ + % request.form['action']) + return (False, {'errors': [ 'An unknown action was specified' ]}) + + def buildSvcTree(parent, child_id_list): + for i in child_id_list: + try: + child = form_hash[i]['obj'] + if not child: + raise Exception, 'No object for %s' % i + except Exception, e: + if LUCI_DEBUG_MODE is True: + luci_log.debug_verbose('bST0: %r %s' % (e, str(e))) + continue + parent.addChild(child) + if 'kids' in form_hash[i]: + buildSvcTree(child, form_hash[i]['kids']) + + new_service = Service() + new_service.addAttribute('name', service_name) + if fdom: + new_service.addAttribute('domain', fdom) + if recovery: + new_service.addAttribute('recovery', recovery) + new_service.addAttribute('exclusive', str(exclusive)) + if autostart is not None: + new_service.attr_hash['autostart'] = autostart + + buildSvcTree(new_service, form_hash['toplevel']['kids']) + model.resourcemanager_ptr.addChild(new_service) + model.setModified(True) + + if action == 'edit': + action_type = SERVICE_CONFIG + action_msg = 'Configuring service "%s"' % service_name + else: + action_type = SERVICE_ADD + action_msg = 'Creating service "%s"' % service_name + + return (True, { 'action_type': action_type, 'action_msg': action_msg }) + +def validate_clures_add(model, request): + fvar = GetReqVars(request, [ 'type' ]) + + res_type = fvar['type'] + if res_type is None: + if LUCI_DEBUG_MODE is True: + luci_log.debug_verbose('VRA0: type is blank') + return (False, { 'errors': [ 'No resource type was given']}) + + try: + res = create_resource(res_type, request.form, model) + except Exception, e: + return (False, { 'errors': e } ) + + resname = '' + try: + resname = res.getName() + model.getResourcesPtr().addChild(res) + except Exception, e: + if LUCI_DEBUG_MODE is True: + luci_log.debug_verbose('EditResource0: %r %s' % (e, str(e))) + return (False, { 'errors': [ 'Error configuring resource %s %s: ' % (resname, str(e)) ] }) + return (True, { 'res_name': resname }) + +def validate_fdom(model, request): + errors = list() + fvar = GetReqVars(request, [ 'clustername', 'name', 'oldname' ]) + + name = fvar['name'] + if name is None: + errors.append('No name was given for this failover domain') + + prioritized = False + try: + prioritized = request.form.has_key('prioritized') + except: + prioritized = False + + restricted = False + try: + restricted = request.form.has_key('restricted') + except: + restricted = False + + nofailback = False + try: + nofailback = request.form.has_key('nofailback') + except: + nofailback = False + + oldname = fvar['oldname'] + + if oldname is None or oldname != name: + if model.getFailoverDomainByName(name) is not None: + errors.append('A failover domain named "%s" already exists' % name) + + fdom = None + if oldname is not None: + fdom = model.getFailoverDomainByName(oldname) + if fdom is None: + if LUCI_DEBUG_MODE is True: + luci_log.debug_verbose('validateFdom1: No fdom named %s exists' % oldname) + errors.append('No failover domain named "%s" exists' % oldname) + else: + fdom.addAttribute('name', name) + fdom.children = list() + else: + fdom = FailoverDomain() + fdom.addAttribute('name', name) + + if fdom is None or len(errors) > 0: + return (False, {'errors': errors }) + + if prioritized: + fdom.addAttribute('ordered', '1') + else: + fdom.addAttribute('ordered', '0') + + if restricted: + fdom.addAttribute('restricted', '1') + else: + fdom.addAttribute('restricted', '0') + + if nofailback: + fdom.addAttribute('nofailback', '1') + else: + fdom.addAttribute('nofailback', '0') + + for i in model.getNodeNames(): + if request.form.has_key(i): + fdn = FailoverDomainNode() + fdn.addAttribute('name', i) + if prioritized: + priority = 1 + try: + priority = int(request.form['__PRIORITY__%s' % i].strip()) + if priority < 1: + priority = 1 + except Exception, e: + priority = 1 + fdn.addAttribute('priority', str(priority)) + fdom.addChild(fdn) + + try: + fdom_ptr = model.getFailoverDomainPtr() + if not oldname: + fdom_ptr.addChild(fdom) + except Exception, e: + if LUCI_DEBUG_MODE is True: + luci_log.debug_verbose('validateFdom2: %r %s' % (e, str(e))) + errors.append('Unable to update the cluster configuration') + + if len(errors) > 0: + return (False, { 'errors': errors }) + + if oldname: + action = FDOM + status_msg = 'Updating failover domain "%s"' % oldname + else: + action = FDOM_ADD + status_msg = 'Creating failover domain "%s"' % name + + return (True, { 'msg': status_msg, 'action': action }) + +def validate_config_mcast(model, form): + gulm_ptr = model.getGULMPtr() + if gulm_ptr: + return (False, { 'errors': ['Multicast cannot be used with GULM locking']}) + errors = list() + try: + mcast_val = form['mcast'].strip().lower() + if mcast_val != 'true' and mcast_val != 'false': + raise KeyError, mcast_val + if mcast_val == 'true': + mcast_manual = True + else: + mcast_manual = False + except KeyError, e: + errors.append('An invalid multicast selection was made') + return (False, {'errors': errors}) + + mcast_interface = None + if form.has_key('mcast_interface'): + mcast_interface = form['mcast_interface'].strip() + + if mcast_manual is True and form.has_key('cluster_version') and form['cluster_version'].strip() == 'rhel4' and not mcast_interface: + errors.append('No multicast interface was specified') + return (False, {'errors': errors}) + + if mcast_manual is True: + import socket + try: + addr_str = form['mcast_address'].strip() + socket.inet_pton(socket.AF_INET, addr_str) + except KeyError, e: + addr_str = None + errors.append('No multicast address was given') + except socket.error, e: + try: + socket.inet_pton(socket.AF_INET6, addr_str) + except socket.error, e: + addr_str = None + errors.append('An invalid multicast address was given: %s') + else: + addr_str = None + + try: + if not addr_str: + if mcast_interface: + errors.append('A multicast interface was specified, but no multicast address was given') + return (False, {'errors': errors}) + model.del_cluster_multicast() + else: + model.set_cluster_multicast(addr_str, mcast_if=mcast_interface) + except Exception, e: + if LUCI_DEBUG_MODE is True: + luci_log.debug('Error updating mcast properties: %r %s' \ + % (e, str(e))) + errors.append('Unable to update cluster multicast properties') + + if len(errors) > 0: + return (False, {'errors': errors}) + + return (True, {}) + +def validate_config_qdisk(model, form): + errors = list() + + gulm_ptr = model.getGULMPtr() + if gulm_ptr is not None: + return (False, { 'errors': ['Quorum disk cannot be used with GULM locking']}) + + try: + qdisk_val = form['quorumd'].strip().lower() + if qdisk_val != 'true' and qdisk_val != 'false': + raise KeyError(qdisk_val) + if qdisk_val == 'true': + qdisk_val = 1 + else: + qdisk_val = 0 + except KeyError, e: + return (False, {'errors': ['An invalid quorum partition selection was made']}) + + cp = model.getClusterPtr() + qdp = model.getQuorumdPtr() + + if not qdisk_val: + if qdp: + try: + cp.removeChild(qdp) + except Exception, e: + return (False, {'errors': [ 'Error disabling quorum partition: %s' % str(e) ] }) + return (True, {}) + + try: + interval = int(form['interval']) + if interval < 0: + raise ValueError, 'Interval must be 0 or greater' + except KeyError, e: + errors.append('No Interval value was given') + except ValueError, e: + errors.append('An invalid Interval value was given: %s' % str(e)) + + try: + votes = int(form['votes']) + if votes < 1: + raise ValueError, 'Votes must be greater than 0' + except KeyError, e: + errors.append('No Votes value was given') + except ValueError, e: + errors.append('An invalid Votes value was given: %s' % str(e)) + + try: + tko = int(form['tko']) + if tko < 0: + raise ValueError, 'TKO must be 0 or greater' + except KeyError, e: + errors.append('No TKO value was given') + except ValueError, e: + errors.append('An invalid TKO value was given: %s' % str(e)) + + try: + min_score = int(form['min_score']) + if min_score < 1: + raise ValueError('Minimum Score must be greater than 0') + except KeyError, e: + errors.append('No Minimum Score value was given') + except ValueError, e: + errors.append('An invalid Minimum Score value was given: %s' % str(e)) + + # Either device or label must be present + device = None + try: + device = form['device'].strip() + except: + device = None + + label = None + try: + label = form['label'].strip() + except: + label = None + + if not device and not label: + errors.append('No Device or Label value was given') + + num_heuristics = 0 + try: + num_heuristics = int(form['num_heuristics']) + 1 + if num_heuristics < 1: + raise ValueError, form['num_heuristics'] + except KeyError, e: + errors.append('No number of heuristics was given') + except ValueError, e: + errors.append('An invalid number of heuristics was given: %s' % str(e)) + + heuristics = list() + for i in xrange(num_heuristics): + try: + h = form['heuristic%d' % i] + if not h or len(h) != 3 or not (h[0].strip() and h[1].strip() and h[2].strip()): + continue + except: + continue + + try: + hprog = h[0] + if not hprog: + raise Exception, 'no hprog' + except Exception, e: + errors.append('No program was given for heuristic %d' % (i + 1)) + try: + hint = int(h[1]) + if hint < 1: + raise ValueError, 'Heuristic interval values must be greater than 0' + except KeyError, e: + errors.append('No interval was given for heuristic %d' % (i + 1)) + except ValueError, e: + errors.append('An invalid interval was given for heuristic %d: %s' \ + % ((i + 1), str(e))) + + try: + hscore = int(h[2]) + if hscore < 1: + raise ValueError, 'Heuristic scores must be greater than 0' + except KeyError, e: + errors.append('No score was given for heuristic %d' % (i + 1)) + except ValueError, e: + errors.append('An invalid score was given for heuristic %d: %s' \ + % ((i + 1), str(e))) + + heuristics.append([ hprog, hint, hscore ]) + + if len(errors) > 0: + return (False, {'errors': errors }) + + qd = QuorumD() + qd.addAttribute('interval', str(interval)) + qd.addAttribute('votes', str(votes)) + qd.addAttribute('tko', str(tko)) + qd.addAttribute('min_score', str(min_score)) + + if device: + qd.addAttribute('device', str(device)) + else: + qd.addAttribute('label', str(label)) + + if qdp: + try: + cp.removeChild(qdp) + except: + pass + cp.addChild(qd) + + for h in heuristics: + new_h = Heuristic() + new_h.addAttribute('program', str(h[0])) + new_h.addAttribute('interval', str(h[1])) + new_h.addAttribute('score', str(h[2])) + qd.addChild(new_h) + + if len(errors) > 0: + return (False, {'errors': errors }) + + return (True, {}) + +def validate_config_fence(model, form): + errors = list() + + if model.getGULMPtr() is not None: + return (False, {'errors': [ 'GULM clusters do not support fenced' ]}) + + try: + post_fail_delay = int(form['post_fail_delay']) + if post_fail_delay < 0: + raise ValueError('post fail delay values must be 0 or greater') + except KeyError, e: + errors.append('No post fail delay was given') + except ValueError, e: + errors.append('Invalid post fail delay: %s' % str(e)) + + try: + post_join_delay = int(form['post_join_delay']) + if post_join_delay < 0: + raise ValueError('post join delay values must be 0 or greater') + except KeyError, e: + errors.append('No post join delay was given') + except ValueError, e: + errors.append('Invalid post join delay: %s' % str(e)) + + run_xvmd = form.has_key('run_xvmd') + + if run_xvmd is True and not model.hasFenceXVM(): + fenceXVMd = FenceXVMd() + model.addFenceXVM(fenceXVMd) + elif not run_xvmd: + model.delFenceXVM() + + try: + fd = model.getFenceDaemonPtr() + old_pj_delay = fd.getPostJoinDelay() + old_pf_delay = fd.getPostFailDelay() + + if post_join_delay == old_pj_delay and post_fail_delay == old_pf_delay: + errors.append('No fence daemon properties were changed') + else: + fd.setPostJoinDelay(str(post_join_delay)) + fd.setPostFailDelay(str(post_fail_delay)) + except Exception, e: + if LUCI_DEBUG_MODE is True: + luci_log.debug_verbose('Unable to update fence daemon properties: %r %s' % (e, str(e))) + errors.append('An error occurred while attempting to update fence daemon properties: %s' % str(e)) + + if len(errors) > 0: + return (False, {'errors': errors }) + + return (True, {}) + +def validate_config_gulm(model, form): + gulm_ptr = model.getGULMPtr() + if not gulm_ptr: + return (False, {'errors': [ 'This cluster is not using GULM locking' ]}) + + node_list = map(lambda x: x.getName(), gulm_ptr.getChildren()) + for i in model.getNodeNames(): + if not i in node_list: + node_list.append(i) + + gulm_lockservers = list() + for node in node_list: + if form.has_key(node) and form[node] == 'on': + ls = Lockserver() + ls.addAttribute('name', node) + gulm_lockservers.append(ls) + + try: + xlockservers = filter(lambda x: x.strip(), form['__GULM__']) + except: + xlockservers = list() + + for i in xlockservers: + if not i in node_list: + ls = Lockserver() + ls.addAttribute('name', i) + gulm_lockservers.append(ls) + + num_ls = len(gulm_lockservers) + if not num_ls in (1, 3, 5): + return (False, {'errors': [ 'You must have exactly 1, 3, or 5 GULM lock servers. You submitted %d lock servers' % num_ls ]}) + + model.GULM_ptr.children = gulm_lockservers + return (True, {}) + +def validate_config_general(model, form): + errors = list() + + try: + cp = model.getClusterPtr() + old_name = model.getClusterAlias() + old_ver = int(cp.getConfigVersion()) + except Exception, e: + if LUCI_DEBUG_MODE is True: + luci_log.debug_verbose('getConfigVersion: %s' % str(e)) + errors.append('unable to determine the current configuration version') + return (False, {'errors': errors}) + + try: + cluster_name = form['cluname'].strip() + if not cluster_name: + raise KeyError('cluname') + except KeyError, e: + errors.append('No cluster name was given') + + if len(cluster_name) > 15: + errors.append('A cluster\'s name must be less than 16 characters long') + + try: + version_num = int(form['cfgver']) + if version_num < old_ver: + raise ValueError, 'configuration version number must be %d or greater' % old_ver + except KeyError, e: + errors.append('No cluster configuration version was given') + except ValueError, e: + errors.append('An invalid configuration version was given: %s' % str(e)) + + if len(errors) < 1: + try: + if cluster_name != old_name: + cp.addAttribute('alias', cluster_name) + cp.setConfigVersion(str(version_num)) + except Exception, e: + if LUCI_DEBUG_MODE is True: + luci_log.debug_verbose('unable to update general properties: %r %s' % (e, str(e))) + errors.append('Unable to update the cluster configuration') + + try: + cluster_version = form['cluster_version'].strip() + if cluster_version != 'rhel5': + raise Exception, 'not rhel5' + except: + if len(errors) > 0: + return (False, {'errors': errors}) + return (True, {}) + + totem = model.getTotemPtr() + if totem is None: + totem = model.addTotemPtr() + + try: + token = form['token'].strip() + if not token: + raise KeyError, 'token' + token = int(token) + if token < 1: + raise ValueError, '%d is an invalid value for token timeout' % token + totem.addAttribute('token', str(token)) + except KeyError, e: + try: + totem.removeAttribute('token') + except: + pass + except Exception, e: + errors.append(str(e)) + + try: + trblc = form['token_retransmits_before_loss_const'].strip() + if not trblc: + raise KeyError, 'token_retransmits_before_loss_const' + trblc = int(trblc) + if trblc < 1: + raise ValueError, '%d is an invalid value for number of token retransmits before loss' % trblc + totem.addAttribute('token_retransmits_before_loss_const', str(trblc)) + except KeyError, e: + try: + totem.removeAttribute('token_retransmits_before_loss_const') + except: + pass + except Exception, e: + errors.append(str(e)) + + try: + join = form['join'].strip() + if not join: + raise KeyError, 'join' + join = int(join) + if join < 1: + raise ValueError, '%d is an invalid value for join timeout' % join + totem.addAttribute('join', str(join)) + except KeyError, e: + try: + totem.removeAttribute('join') + except: + pass + except Exception, e: + errors.append(str(e)) + + try: + consensus = form['consensus'].strip() + if not consensus: + raise KeyError, 'consensus' + consensus = int(consensus) + if consensus < 1: + raise ValueError, '%d is an invalid value for consensus timeout' % consensus + totem.addAttribute('consensus', str(consensus)) + except KeyError, e: + try: + totem.removeAttribute('consensus') + except: + pass + except Exception, e: + errors.append(str(e)) + + if len(errors) > 0: + return (False, {'errors': errors}) + return (True, {}) + +def validate_cluster_daemon_form(self, request): + errors = list() + + fvar = GetReqVars(request, [ 'nodename' ]) + nodename = fvar['nodename'] + if nodename is None: + errors.append('Unable to determine the current node name') + if LUCI_DEBUG_MODE is True: + luci_log.debug_verbose('VDP1: no nodename was given') + + disable_list = list() + enable_list = list() + + for i in request.form.items(): + try: + if i[0][:11] == '__daemon__:': + daemon_prop = i[1] + if len(daemon_prop) == 2: + if daemon_prop[1] == '1': + disable_list.append(daemon_prop[0]) + else: + if daemon_prop[1] == '0' and daemon_prop[2] == 'on': + enable_list.append(daemon_prop[0]) + except Exception, e: + if LUCI_DEBUG_MODE is True: + luci_log.debug_verbose('VDP3: error: %s: %r %s' \ + % (str(i), e, str(e))) + + if len(enable_list) < 1 and len(disable_list) < 1: + if LUCI_DEBUG_MODE is True: + luci_log.debug_verbose('VDP4: no changes made') + errors.append('No changes to daemon properies were made on node "%s"' \ + % nodename) + + if len(errors) > 0: + return (False, {'errors': errors}) + + return (True, { 'enable_list': enable_list, 'disable_list': disable_list }) + +def validate_vmsvc_form(model, request): + errors = list() + + fvar = GetReqVars(request, [ 'vmname', 'oldname', 'vmpath', 'recovery', 'domain', ]) + + vm_name = fvar['vmname'] + if vm_name is None: + if LUCI_DEBUG_MODE is True: + luci_log.debug_verbose('validateVM2: no vm name') + errors.append('No virtual machine name was given') + + vm_path = fvar['vmpath'] + if vm_path is None: + if LUCI_DEBUG_MODE is True: + luci_log.debug_verbose('validateVM3: no vm path') + errors.append('No path to the virtual machine configuration directory was given for VM service "%s"' % vm_name) + + autostart = 1 + if request.form.has_key('autostart'): + autostart = 1 + else: + autostart = 0 + + exclusive = 0 + if request.form.has_key('exclusive'): + exclusive = 1 + else: + exclusive = 0 + + recovery = fvar['recovery'] + if recovery is not None and recovery != 'restart' and recovery != 'relocate' and recovery != 'disable': + errors.append('You entered an invalid recovery option "%s" for VM service "%s". Valid options are "restart" "relocate" and "disable"' % (recovery, vm_name)) + + if len(errors) > 0: + return (False, {'errors': errors }) + + isNew = False + old_name = fvar['oldname'] + if old_name is None: + isNew = True + + delete_vm = False + if request.form.has_key('delete'): + try: + xvm = model.retrieveVMsByName(old_name) + if not xvm: + raise Exception, 'not found' + rmptr = model.getResourceManagerPtr() + rmptr.removeChild(xvm) + delete_vm = True + except: + return (False, { 'errors': [ 'No virtual machine service named "%s" exists' % old_name ]}) + else: + if isNew is True: + xvm = Vm() + xvm.addAttribute('name', vm_name) + xvm.addAttribute('path', vm_path) + rmptr = model.getResourceManagerPtr() + rmptr.addChild(xvm) + else: + try: + xvm = model.retrieveVMsByName(old_name) + if not xvm: + raise Exception, 'not found' + except: + return (False, { 'errors': [ 'No virtual machine service named "%s" exists' % old_name ]}) + xvm.addAttribute('name', vm_name) + xvm.addAttribute('path', vm_path) + + xvm.addAttribute('autostart', str(autostart)) + xvm.addAttribute('exclusive', str(exclusive)) + + fdom = fvar['domain'] + if fdom: + xvm.addAttribute('domain', fdom) + else: + try: + xvm.removeAttribute('domain') + except: + pass + + if recovery: + xvm.addAttribute('recovery', recovery) + else: + try: + xvm.removeAttribute('recovery') + except: + pass + + if delete_vm is True: + action = VM_CONFIG + status_msg = 'Deleting virtual machine service "%s"' % vm_name + elif isNew is True: + action = VM_ADD + status_msg = 'Creating virtual machine service "%s"' % vm_name + else: + action = VM_CONFIG + status_msg = 'Configuring virtual machine service "%s"' % vm_name + + return (True, { 'action_type': action, 'action_msg': status_msg }) + +def validate_fence_del(model, request): + fvar = GetReqVars(request, [ 'orig_name' ]) + + fencedev_name = fvar['orig_name'] + if fencedev_name is None: + return (False, { 'errors': [ 'No fence device name in form submission' ] }) + + try: + fdev = model.getFenceDeviceByName(fencedev_name) + if fdev: + if model.deleteFenceDevice(fdev) is not True: + raise Exception, 'failed to remove %s' % fdev.getName() + model.removeFenceInstancesForFenceDevice(fencedev_name) + else: + raise Exception, 'no fence device named "%s" was found' \ + % fencedev_name + except Exception, e: + if LUCI_DEBUG_MODE is True: + luci_log.debug_verbose('DFD3: %s: %r %s' \ + % (fencedev_name, e, str(e))) + return (False, { 'errors': [ 'Error removing fence device %s: %s' \ + % (fencedev_name, str(e)) ]}) + return (True, { 'name': fencedev_name })