1. Server group and notification to contacts

    Article: AN0002437Updated: 25.01.2021

    This case study describes a solution providing notifications to all contact persons stated at servers that are affected by an action recorded in the Operations log.

    Requirements

    The solution has following features:

    Every server can have N contacts defined - see the printscreen below.

     

    The Operations log contains actions that are planned and that can refer to N servers or server groups. There is an example of a record in the Operations log referring to several servers and a server group in the printscreen below.

    Group of servers works as a set containing defined servers. If a change relates to all the servers in the group, we can easily select single group instead of looking for particular servers. Example of a server group is below.

     

    After creating a record in the Operations log an email should be sent to all server contacts stated in the Operations log directly or within their membership in the stated server groups. If the change relates to more servers that have the same user as a contact, the given user will receive just a single email stating all the servers due to which he or she received the email.

    We can see an example of such an email in the printscreen below.

    In case of machines that work as a cluster we want to send the notification also to contacts stated at all the nodes that are members of the cluster and also to all the virtual servers or cluster services running on this cluster. Relationship of the cluster, cluster services, nodes and virtual servers is explained in an article describing machines types from Configuration management perspective and it is displayed also in the below figure.

    Vztah clusteru, jeho nódů a na něm běžících clusterových služeb a virtuálních serverů 

     

    Solution

    Relationships of the key entities are captured in the following Entity-relationship diagram.

    The solution consists in mere two objects:

    • rule After saving a new record in the class Operations log, that secures search through contacts of all the impacted servers and sending the notifications
    • notification that defines the email structure

    Rule of type Script that is calling the notification:

    var clMachine = OGDataParent.Model.ClassDefs['machine'];
    var clMachineGroup = OGDataParent.Model.ClassDefs['machine_group'];

    //machines - to avoid duplicate checks
    var idMachines = OG.CreateIntList();

    //users - to insert everyone just once
    var htPersons = OG.CreateHashtable(); //key PersonId, value person
    var htPersonsMachines = OG.CreateHashtable(); //key PersonId, value collection of machines
    var idPersons = OG.CreateIntList();

    ProcessServers( OGActualDataRow.GetDR('ci'));
    AddServers_MemberOf_VirtualOn();
    SendEmails();

    //-------------------------------------------------
    function ProcessServers( drlCi)
    {
      if (!OG.IsNull(drlCi))
      {
        for( var i = 0; i < drlCi.Count; ++i)
        {
          var drCi = drlCi[i];

          if ( drCi.ChildClassDefId == clMachine.Id)
          {
            var drMachine = OG.DataRow.GetDataById(clMachine.Id, drCi.ChildDataRowId);
            ProcessMachine(drMachine);
          }
          else if ( drCi.ChildClassDefId == clMachineGroup.Id)
          {

            var drf = OG.DataRow.GetDataRowFilter(clMachine.Id);
            drf['machine_group'] = drCi.ChildDataRowId;
            var drlM = OG.DataRow.GetDataByFilter(drf);
            for( i2 = 0; i2 < drlM.Count; ++i2)
            {
              ProcessMachine(drlM[i2]);
            }
          }
        }
      }
    }

    //find machines that are refering to idMachines in column member_of or virtual_on
    function AddServers_MemberOf_VirtualOn()
    {
      var drf = OG.DataRow.GetDataRowFilter(clMachine.Id);

      var sql = 'dr' + clMachine.Id + '.' + clMachine.Columns['member-of'].DBColumnName + ' in (' +  OG.TextUtils.ConvertIntToSeparatedString(idMachines, ',', '') + ')';
      var c1 = OG.DataRow.CreateConSql(sql);
      sql = 'dr' + clMachine.Id + '.' + clMachine.Columns['virtual-on'].DBColumnName + ' in (' + OG.TextUtils.ConvertIntToSeparatedString(idMachines, ',', '') + ')';
      var c2 = OG.DataRow.CreateConSql(sql);
      drf.AddOrCon(c1, c2, true);

      var drlM = OG.DataRow.GetDataByFilter(drf);
      for( i2 = 0; i2 < drlM.Count; ++i2)
      {
        ProcessMachine(drlM[i2]);
      }
    }

    //check machine, only if it was not checked yet
    function ProcessMachine(drMachine)
    {

      //only when the server was not yet processed - to avoid cycle
      if (!OG.IsNull(drMachine) && !idMachines.Contains(drMachine.Id))
      {
        idMachines.Add(drMachine.Id);
        AddToPersonsToServer(drMachine['machine_contacts'], drMachine);
      }
    }

    //check persons and add machine to each of them
    function AddToPersonsToServer(addPersons, drMachine)
    {
      if (!OG.IsNull(addPersons))
      {
        for( var i = 0; i < addPersons.Count; ++i)
        {
          var p = addPersons[i];
          if (!idPersons.Contains(p.Id))
          {
            idPersons.Add(p.Id);
            htPersons[p.Id] = p;
            htPersonsMachines[p.Id] = OG.DataRow.CreateNewList();
          }
          htPersonsMachines[p.Id].Add(drMachine);
        }
      }
    }

    //send email - to each user just his/her servers
    function SendEmails()
    {
      var nAll = OG.Notification.GetAll().GetByModelId( OGDataParent.Model.Id, false);
      var n = nAll.GetDefaultByCode(OGDataParent.Model.Id, 'operations_log');

      for( var i = 0; i < idPersons.Count; ++i)
      {
        var p = htPersons[idPersons[i]];
        var machines = htPersonsMachines[idPersons[i]];
        var machinesEI = machines.ToEntityInfoList();
        machinesEI.SortByDisplayName();

        //create email
        var m = OG.Email.CreateMessage();
        m.AddTo(p);
        m.AssignEmailForDataRow = OGActualDataRow;
        m.SendEmailAsOne = false; //odeslat kazdemu svuj email

        var p2 = OG.Person.GetById(p.Id);

        if ( OG.IsNull(p2.NotificationLanguageId))
        {
          m.Notification = n;
        }
        else
        {
          m.Notification = nAll.GetByCodeLanguage( 'operations_log', p2.NotificationLanguageId);
        }

        //insert servers
        var ids = OG.CreateIntList();
        var s = '';
        for( im = 0; im < machinesEI.Count; ++im)
        {
          //check that each server is in the list just once
          if (!ids.Contains(machinesEI[im].Id))
          {
            ids.Add(machinesEI[im].Id);
            if ( im > 0)
            {
              s += ', ';
            }
            s += machinesEI[im].DisplayName;
          }
        }
        m.AddVariable( 'servers', s);

        //send email
        OG.Email.Send(m);
      }
    }

     

    Notification used by the script:

×