python/pyosaf/utils/immom/__init__.py | 11 +
python/pyosaf/utils/immom/object.py | 3 +
python/samples/scale_opensaf | 323 ++++++++++++++++++++++++++++++++++
3 files changed, 337 insertions(+), 0 deletions(-)
This script will add and remove IMM configuration for a node. Check the
help from argparser (-h) for more info. This version is limited and can be
viewed as a demo. Scale-in is done after scale-out to clean up any
configuration added.
diff --git a/python/pyosaf/utils/immom/__init__.py
b/python/pyosaf/utils/immom/__init__.py
--- a/python/pyosaf/utils/immom/__init__.py
+++ b/python/pyosaf/utils/immom/__init__.py
@@ -185,3 +185,14 @@ def get_error_strings(ccb_handle):
saImmOmCcbGetErrorStrings(ccb_handle, c_strings)
return unmarshalNullArray(c_strings)
+
+
+def get_rdn_attribute_for_class(class_name):
+ ''' Returns the RDN attribute for the given class. This is safe to call
+ from OI callbacks
+ '''
+ desc = class_description_get(class_name)
+ for attr_desc in desc:
+ if attr_desc.attrFlags & saImm.saImm.SA_IMM_ATTR_RDN:
+ return attr_desc.attrName
+ return None
diff --git a/python/pyosaf/utils/immom/object.py
b/python/pyosaf/utils/immom/object.py
--- a/python/pyosaf/utils/immom/object.py
+++ b/python/pyosaf/utils/immom/object.py
@@ -61,6 +61,9 @@ class ImmObject(object):
else:
raise
+ self.__dict__["rdn_attribute"] = \
+ pyosaf.utils.immom.get_rdn_attribute_for_class(class_name)
+
def get_value_type(self, attrname):
''' returns IMM value type of the named attribute '''
for attrdef in self.class_desc[self.class_name]:
diff --git a/python/samples/scale_opensaf b/python/samples/scale_opensaf
new file mode 100755
--- /dev/null
+++ b/python/samples/scale_opensaf
@@ -0,0 +1,323 @@
+#!/usr/bin/env python
+""" Adds and removes IMM configuration for nodes """
+
+import re
+import hashlib
+import time
+import argparse
+import os
+import subprocess
+from pyosaf.utils import immom
+from pyosaf.utils.immom.ccb import Ccb
+from pyosaf.utils.immom.iterator import InstanceIterator as inst_iter
+from pyosaf.saAmf import eSaAmfRedundancyModelT, eSaAmfAdminOperationIdT
+
+TWO_N = eSaAmfRedundancyModelT.SA_AMF_2N_REDUNDANCY_MODEL
+NWAYACTIVE = eSaAmfRedundancyModelT.SA_AMF_N_WAY_ACTIVE_REDUNDANCY_MODEL
+NORED = eSaAmfRedundancyModelT.SA_AMF_NO_REDUNDANCY_MODEL
+
+ADMIN_UNLOCK = eSaAmfAdminOperationIdT.SA_AMF_ADMIN_UNLOCK
+ADMIN_LOCK = eSaAmfAdminOperationIdT.SA_AMF_ADMIN_LOCK
+ADMIN_LOCK_IN = eSaAmfAdminOperationIdT.SA_AMF_ADMIN_LOCK_INSTANTIATION
+ADMIN_UNLOCK_IN = eSaAmfAdminOperationIdT.SA_AMF_ADMIN_UNLOCK_INSTANTIATION
+
+
+def print_object(immobj, new_dn=None):
+ """ Print an ImmObject and attributes with the type enum """
+ if new_dn:
+ print new_dn
+ else:
+ print immobj.dn
+ for key, value in immobj.attrs.iteritems():
+ val = ''
+ if len(value[1]):
+ val = value[1]
+ if value[1][0]:
+ val = value[1][0]
+ if val:
+ print '\t{0}: {1} : {2}'.format(key, val, value[0])
+
+
+def find_object_type(class_name, parent_list):
+ """ Find objects of a certain class under a parent list
+ return: list of ImmObjects
+ """
+ result = []
+ for par in parent_list:
+ for immobj in inst_iter(class_name, par.dn):
+ result.append(immobj)
+ return result
+
+
+def find_node_dns(hostname):
+ """ Find the DNs of CLM and AMF node for the hostname
+ returns: AMF node DN and CLM node DN
+ """
+ for node in inst_iter('SaAmfNode'):
+ # Get the first value part of the DN
+ hname = node.saAmfNodeClmNode.split(',')[0].split('=')[1]
+ if hname == hostname:
+ return node.dn, node.saAmfNodeClmNode
+ raise Exception('Could not find IMM object for hostname [%s]' % hostname)
+
+
+def redundancy_of_su(su_dn):
+ """ The redundancy of a SU
+ returns: saAmfSgtRedundancyModel attribute value
+ """
+ # Get the SG DN from the SU DN
+ sg_of_su = su_dn.split(',', 1)[1]
+ itsg = immom.get(sg_of_su)
+ # Get the redundancy from the SG type
+ itsg_t = immom.get(itsg.saAmfSGType)
+ return itsg_t.saAmfSgtRedundancyModel
+
+
+def find_sus(amfnode_dn, scalable_redundancy):
+ """ Find all SUs hosted by a AMF node matching some redundancy models
+ returns: list of SUs (ImmObject)
+ """
+ sus = []
+ for itsu in inst_iter('SaAmfSU'):
+ if amfnode_dn == itsu.saAmfSUHostedByNode:
+ if redundancy_of_su(itsu.dn) in scalable_redundancy:
+ sus.append(itsu)
+ return sus
+
+
+def find_sis_apps(sus, redundancy):
+ """ Match the SUs to SIs for a given redundancy model
+ returns: The matched SIs as list of ImmObjects
+ The apps of SUs as list of DNs
+ """
+ matched_sus = []
+ matched_sgs = []
+ matched_apps = []
+ protected_sis = []
+ su_svctypes = []
+
+ # Collect the SUs, SGs and Apps of a certain redundancy
+ for itsu in sus:
+ if redundancy_of_su(itsu.dn) == redundancy:
+ matched_sus.append(itsu)
+ # Get SG and App DNs from the SU DN
+ matched_sgs.append(itsu.dn.split(',', 1)[1])
+ matched_apps.append(itsu.dn.split(',', 2)[2])
+
+ # Keep the order and make the items unique
+ matched_apps = sorted(set(matched_apps), key=matched_apps.index)
+
+ # Collect the SIs protected by the SGs
+ for itsi in inst_iter('SaAmfSI'):
+ if itsi.saAmfSIProtectedbySG in matched_sgs:
+ protected_sis.append(itsi)
+
+ # Collect the SvcTypes of the SUs
+ for itsu in matched_sus:
+ su_type = immom.get(itsu.saAmfSUType)
+ for svc_type in su_type.saAmfSutProvidesSvcTypes:
+ su_svctypes.append(svc_type)
+
+ # Match SvcType in SU and SI
+ result_sis = []
+ for itsi in protected_sis:
+ if itsi.saAmfSvcType in su_svctypes:
+ result_sis.append(itsi)
+
+ return result_sis, matched_apps
+
+
+def prepare_for_write(objects):
+ """ Prepare objects for write by removing runtime attributes
+ returns: List of ImmObjects without runtime attributes
+ """
+ conf_objects = []
+ for immobj in objects:
+ immobj = immom.get(immobj.dn, ['SA_IMM_SEARCH_GET_CONFIG_ATTR'])
+ del immobj.attrs['SaImmAttrAdminOwnerName']
+ del immobj.attrs['SaImmAttrClassName']
+ del immobj.attrs['SaImmAttrImplementerName']
+ conf_objects.append(immobj)
+ return conf_objects
+
+
+def find_node_groups(amf_node):
+ """ fetch all node groups a node belongs to """
+ matching_groups = []
+ for ngrp in inst_iter('SaAmfNodeGroup'):
+ if str.find(''.join(ngrp.saAmfNGNodeList), amf_node) >= 0:
+ matching_groups.append(ngrp)
+ return matching_groups
+
+
+def collect_scalable_immobjects(from_amfnode_dn, from_clmnode_dn):
+ """ Read AMF objects of an allowed type and redundancy from a node.
+ returns: list of collected ImmObjects
+ """
+ sus = find_sus(from_amfnode_dn, [TWO_N, NWAYACTIVE, NORED])
+ comps = find_object_type('SaAmfComp', sus)
+ hchks = find_object_type('SaAmfHealthcheck', comps)
+ comps_cs = find_object_type('SaAmfCompCsType', comps)
+ sis, _ = find_sis_apps(sus, NORED)
+ csis = find_object_type('SaAmfCSI', sis)
+ csiattrs = find_object_type('SaAmfCSIAttribute', csis)
+
+ swbundles = []
+ for immobj in inst_iter('SaAmfNodeSwBundle', from_amfnode_dn):
+ swbundles.append(immobj)
+
+ from_amfnode = immom.get(from_amfnode_dn)
+ from_clmnode = immom.get(from_clmnode_dn)
+
+ # Order is important
+ return [from_amfnode, from_clmnode] + sus + sis + csis + comps + \
+ comps_cs + swbundles + hchks + csiattrs
+
+
+def modify_immobject(immobj, suffix, new_amfnode_dn, new_hostname):
+ """ Rename DN and change attributes for one IMM object """
+ rename_str = None
+ immobj.new_dn = immobj.dn
+ match = re.search('safSu=[^,]*|safSi[^,]*', immobj.new_dn)
+ if match:
+ # We have a SU or SI in the DN string. Generate a hash from it.
+ rename_str = immobj.new_dn[match.start():match.end()] + new_hostname
+ rename_str += suffix
+ rename_str = hashlib.md5(rename_str).hexdigest()[:10]
+
+ # Rename the DN and store it in new_dn
+ immobj.new_dn = re.sub(r'(safSu|safSi)=([^,]*),', r'\1={0},'.
+ format(rename_str), immobj.new_dn)
+ immobj.new_dn = re.sub('safNode=[^,]*,', 'safNode={0},'.
+ format(new_hostname), immobj.new_dn)
+ immobj.new_dn = re.sub('safAmfNode=[^,]*,', 'safAmfNode={0},'.
+ format(new_hostname), immobj.new_dn)
+
+ # Change attributes to match the new node. Admin state is set to something
+ # accepted by the AMF OI
+ if immobj.class_name == 'SaAmfSU':
+ immobj.saAmfSUHostNodeOrNodeGroup = new_amfnode_dn
+ immobj.saAmfSUAdminState = ADMIN_UNLOCK
+
+ elif immobj.class_name == 'SaAmfNode':
+ immobj.saAmfNodeAdminState = ADMIN_LOCK_IN
+ immobj.saAmfNodeClmNode = re.sub(
+ 'safNode=[^,]*,', 'safNode={0},'.format(new_hostname),
+ immobj.saAmfNodeClmNode)
+
+
+def split_rdn_and_parent(immobj):
+ """ Split the DN of the passed in object into RDN and parent DN
+ returns: RDN and parent DN as strings
+ """
+ rdn = immobj.new_dn.split(',', 1)[0]
+ parent = immobj.new_dn.split(',', 1)[1]
+ # Handle the case when we have two backslashes
+ while rdn[-1] == '\\':
+ parent = parent.split(',', 1)[1]
+ rdn = parent.split(',', 1)[0]
+ return rdn, parent
+
+
+def scale_out(new_hostname, from_hostname):
+ """ Scale out one node
+ params:
+ new_hostname - hostname of the new node
+ from_hostname - hostname of the template node
+ """
+ print '-Scaling out to [%s] config copied from [%s]' % (new_hostname,
+ from_hostname)
+ for itsu in inst_iter('SaAmfSU'):
+ # Get the first value part of the DN
+ hname = itsu.saAmfSUHostedByNode.split(',')[0].split('=')[1]
+ if hname == new_hostname:
+ print 'Node already has SUs, no scaling-out done'
+ return 0
+
+ new_amfnode = new_hostname
+ from_amfnode_dn, from_clmnode_dn = find_node_dns(from_hostname)
+ new_amfnode_dn = re.sub('safAmfNode=[^,]*,', 'safAmfNode={0},'.
+ format(new_amfnode), from_amfnode_dn)
+ objects = collect_scalable_immobjects(from_amfnode_dn, from_clmnode_dn)
+ conf_objects = prepare_for_write(objects)
+ #nodegroups_to_join = find_node_groups(from_amfnode_dn)
+
+ suffix = str(time.time())
+ for immobj in conf_objects:
+ modify_immobject(immobj, suffix, new_amfnode_dn, new_hostname)
+
+ objs = list()
+ # Limit the scale-out objects to what we can scale-in for now.
+ # objects not scaled out: comps, comps_cs, swbundles, hchks, csiattrs
+ allowed_objs = ['SaAmfNode', 'SaClmNode', 'SaAmfSU', 'SaAmfSI', 'SaAmfCSI']
+ for obj in conf_objects:
+ if obj.class_name in allowed_objs:
+ objs.append(obj)
+
+ ccb = Ccb(flags=None)
+
+ for immobj in objs:
+ rdn, parent = split_rdn_and_parent(immobj)
+ immobj.attrs[immobj.rdn_attribute][1] = [rdn]
+ print_object(immobj, immobj.new_dn)
+ del immobj.attrs['new_dn']
+ ccb.create(immobj, parent)
+ ccb.apply()
+ print '-Scaling out done'
+
+
+def scale_in(hostname):
+ """ Remove the configuration for the host node """
+ print '-Scaling in: %s' % hostname
+ amfnode_dn, clmnode_dn = find_node_dns(hostname)
+ clmnode = immom.get(clmnode_dn)
+ amfnode = immom.get(amfnode_dn)
+
+ subprocess.call(['amf-adm', 'lock', clmnode_dn])
+
+ sus = find_sus(amfnode_dn, [TWO_N, NWAYACTIVE, NORED])
+
+ ccb = Ccb(flags=None)
+ ccb.delete(amfnode.dn)
+ ccb.delete(clmnode.dn)
+ for itsu in sus:
+ print itsu.dn
+ ccb.delete(itsu.dn)
+
+ sis, apps = find_sis_apps(sus, NORED)
+ for itsi in sis:
+ # Check if this SI has a assignment
+ siass = inst_iter('SaAmfSIAssignment')
+ assigned = [ass for ass in siass if str.find(ass.dn, itsi.dn) != -1]
+
+ # Check if the SU App is used by this SI... Why? Not sure, the example
+ # I followed did even more stuff here. TODO: Investigate why.
+ usedapp = [app for app in apps if str.find(itsi.dn, app) != -1]
+ si_app = itsi.dn.split(',')[1:][0] # Last split is the SI App
+ if not assigned and usedapp and si_app in apps:
+ csis = find_object_type('SaAmfCSI', [itsi])
+ for csi in csis:
+ print csi.dn
+ ccb.delete(csi.dn)
+ print itsi.dn
+ ccb.delete(itsi.dn)
+ ccb.apply()
+ print '-Scaling in done'
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(
+ description='Scales OpenSAF by adding and removing node configuration')
+ parser.add_argument(
+ '--hostname', type=str, required=True,
+ help='Hostname for the new node.')
+ parser.add_argument(
+ '--copy-from', type=str, required=False, help='Hostname of the '
+ 'existing node to use as template. If not set, the host where the '
+ 'command is executed will be used as template.',
+ default=os.uname()[1])
+
+ args = parser.parse_args()
+
+ scale_out(args.hostname, args.copy_from)
+ scale_in(args.hostname)
------------------------------------------------------------------------------
_______________________________________________
Opensaf-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/opensaf-devel