Hi Rafael,

Ack, with the changes below this looks good. It would be good to replace the 
subprocess call with an admin operation in a later patch.

/ Johan

-----Original Message-----
From: Rafael Odzakow 
Sent: den 19 november 2015 15:28
To: Johan Mårtensson O
Cc: [email protected]
Subject: [PATCH 1 of 1] pyosaf: add sample script to scale opensaf 
configuration [#1569]

 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)

------------------------------------------------------------------------------
Go from Idea to Many App Stores Faster with Intel(R) XDK
Give your users amazing mobile app experiences with Intel(R) XDK.
Use one codebase in this all-in-one HTML5 development environment.
Design, debug & build mobile apps & 2D/3D high-impact games for multiple OSs.
http://pubads.g.doubleclick.net/gampad/clk?id=254741551&iu=/4140
_______________________________________________
Opensaf-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/opensaf-devel

Reply via email to