PI Services

Le blog des collaborateurs de PI Services

Zabbix – Principe des périodes de maintenance


A l’instar de la plupart des outils de supervision, Zabbix propose un système de mise en maintenance de la supervision.

Pour configurer une nouvelle maintenance, Aller dans le menu Configuration / Maintenance:

image


Cliquer Create Maintenance Period

image


Renseigner le nom de la période.

Intéressant, le choix est donné de collecter ou non les données.

Attention: les champs de date Active Since et Active Till ne représente pas la période de maintenance elle même mais le temps au sein duquel nous allons créer une ou plusieurs périodes dans l’onglet correspondant (Periods)

Dans l’exemple ci-dessous on positionne une plage de temps d’un an.

image


Aller dans l’onglet Periods. Cliquer Add

image


4 modes sont disponibles: One Time Only, Daily, Weekly et Monthly.

imageimageimageimageimage


Dans l’onglet Hosts and groups, selectionner le/les hosts ou le/les host groups

image


Une fois la période crée, elle apparait en état Approaching lorsque la date Active Since approche.

image


Lorsque les hosts concernés sont effectivement en période de maintenance, l’icone image apparait a coté, jusqu’a la fin de la période.


image

image


Dans cet exemple, dans un an, la règle de maintenance passera en mode Expired.

image

SCOM 2019 – An Item With The Same Key Has Already Been Added après l’installation de l’UR1


SCOM 2019 UR1 a introduit de nouveaux management packs universels pour Linux. Ils vont permettre de simplifier la supervision des différentes distributions : plus besoin d’un MP différent pour chaque distribution et chaque version de cette distribution, les MP universels pourront gérer toutes les futures versions des distributions supportées.

Cependant, le déploiement de ces MP universels dans un environnement disposant encore du MP RHEL 6 et de serveurs découverts dans cette version provoque un petit souci : la vue Unix/Linux Computers ne se charge plus et l’erreur suivante apparait :

clip_image001

Par ailleurs, l’erreur suivante est visible dans le journal d’événements :

System.ArgumentException: An item with the same key has already been added.
   at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
   at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
   at Microsoft.SystemCenter.CrossPlatform.UI.OM.Integration.MonitoringObjectPathToMonitoringObjectDictionary..ctor(IEnumerable`1 monitoringObjects)
   at Microsoft.SystemCenter.CrossPlatform.UI.OM.Integration.UnixComputerOperatingSystemHelper.JoinCollections(IEnumerable`1 managementServers, IEnumerable`1 resourcePools, IEnumerable`1 unixcomputers, IEnumerable`1 operatingSystems)
   at Microsoft.SystemCenter.CrossPlatform.UI.OM.Integration.UnixComputerOperatingSystemHelper.GetUnixComputerOperatingSystemInstances(String criteria)
   at Microsoft.SystemCenter.CrossPlatform.UI.OM.Integration.Administration.UnixAgentQuery.DoQuery(String criteria)
   at Microsoft.EnterpriseManagement.Mom.Internal.UI.Cache.Query`1.DoQuery(String criteria, Nullable`1 lastModified)
   at Microsoft.EnterpriseManagement.Mom.Internal.UI.Cache.Query`1.FullUpdateQuery(CacheSession session, IndexTable& indexTable, Boolean forceUpdate, DateTime queryTime)
   at Microsoft.EnterpriseManagement.Mom.Internal.UI.Cache.Query`1.InternalSyncQuery(CacheSession session, IndexTable indexTable, UpdateReason reason, UpdateType updateType)
   at Microsoft.EnterpriseManagement.Mom.Internal.UI.Cache.Query`1.InternalQuery(CacheSession session, UpdateReason reason)
   at Microsoft.EnterpriseManagement.Mom.Internal.UI.Cache.Query`1.TryDoQuery(UpdateReason reason, CacheSession session)
   at Microsoft.EnterpriseManagement.Mom.Internal.UI.Console.ConsoleJobExceptionHandler.ExecuteJob(IComponent component, EventHandler`1 job, Object sender, ConsoleJobEventArgs args)

La raison en est simple : le nouveau MP universel a découvert une nouvelle fois vos serveurs RHEL 6, ce qui crée des doublons dans la base de données et un plantage de la console lorsqu’elle tente de les lister.

Vous disposez maintenant de deux solutions :

- Supprimer le MP RHEL 6 qui, par ailleurs, n’est plus supporté dans SCOM 2019. Vos serveurs RHEL6 seront toujours supervisés par le MP universel, mais ce dernier ne contient pas exactement les même moniteurs, il faudra donc vérifier qu’il répond à vos besoins. Par ailleurs, si vous aviez développé des règles et moniteurs qui ciblent spécifiquement la classe RHEL 6, il faudra les réécrire.
Par ailleurs, vous ne pourrez plus découvrir de nouveaux serveurs RHEL 6 si vous supprimez ce management pack.

- Désactiver la découverte des serveurs RHEL 6 via un override, puis exécuter la commande Remove-SCOMDisabledClassInstance.

Une fois débarrassé de ces doublons, tout devrait rentrer dans l’ordre !

SCOM – Création et peuplement dynamique de groupes à partir d’une clé de registre (le retour en mieux)

J’avais publié il y a quelques années un article montrant comment créer et peupler automatiquement des groupes à partir d’une clé de registre, à l’aide d’un script vbs : Création et peuplement dynamique de groupes à partir d’une clé de registre.

J’ai récemment rencontré un besoin similaire, et j’ai cette fois voulu expérimenter une technique différente et que je considère comme plus élégante, car elle ne se base que sur l’utilisation standard de deux modules natifs, sans faire appel au moindre bout de script.

Je ne reviendrai pas ici sur la nécessité de déclarer une classe unhosted, non singleton et avec un attribut clé pour le groupe : tout cela est détaillé dans l’article précédent.

Entrons donc directement dans le vif du sujet !

Comme je viens de le rappeler, un groupe est l’instance d’une classe. Les objets membres de ce groupe sont eux aussi des instances de différentes classe et ils sont rattachés au groupe à l’aide d’une relation de containment.

Notre objectif est donc de créer des instances de la classe du groupe ainsi que des relations de containment entre ces instances et les objets qui vont venir peupler les groupes.

Et pour ce faire, sans utiliser aucun script, il existe un module parfaitement adapté : System.Discovery.FilteredClassAndRelationshipSnapshotDataMapper.

clip_image002

Ce module va tout simplement créer une instance de la classe que vous lui indiquerez, avec les propriétés que vous lui indiquerez ; ainsi qu’une instance de la relation de votre choix, entre les instances indiquées.

Bien entendu, il n’est pas question ici de remplir les champs de manière statique : ce module sera intégré dans votre workflow de découverte et récupérera donc toutes les informations dont il a besoin depuis les modules précédents.

Dans cet exemple, nous souhaitons peupler les groupes à partir d’une clé de registre : nous devrions donc créer notre datasourcemoduletype avec le très classique module Microsoft.Windows.Discovery.RegistryProvider comme datasource, puisque son rôle est justement d’aller lire dans la base de registre.

Mais il existe une solution encore plus simple : un module natif combinant RegistryProvider et SnapshotDataMapper existe déjà ! Il s’agit du module Microsoft.Windows.FilteredRegistryClassAndRelationshipDiscoveryProvider.

Une fois ces éléments mis bout à bout, on arrive au fragment suivant qu’il suffit de modifier en y indiquant la clé de registre qui vous intéresse :

<ManagementPackFragment SchemaVersion="2.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <TypeDefinitions>
    <EntityTypes>
      <ClassTypes>
        <ClassType ID="Test.My.Computers.Group" Accessibility="Public" Abstract="false" Base="MSIL!Microsoft.SystemCenter.InstanceGroup" Hosted="false" Singleton="false" Extension="false" >
          <Property ID="RegistryValue" Type="string" AutoIncrement="false" Key="true" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false" Scale="0" />
        </ClassType>
        
      </ClassTypes>
    </EntityTypes>
  </TypeDefinitions>
  <Monitoring>
    <Discoveries>
      <Discovery ID="Test.My.Computers.Group.Discovery" Enabled="true" Target="Windows!Microsoft.Windows.OperatingSystem" ConfirmDelivery="false" Remotable="true" Priority="Normal">
        <Category>Discovery</Category>
        <DiscoveryTypes>
          <DiscoveryClass TypeID="Test.My.Computers.Group">
            <Property TypeID="Test.My.Computers.Group" PropertyID="RegistryValue" />
          </DiscoveryClass>
        </DiscoveryTypes>
        <DataSource ID="DS" TypeID="Windows!Microsoft.Windows.FilteredRegistryClassAndRelationshipDiscoveryProvider">
          <ComputerName>$Target/Host/Property[Type="Windows!Microsoft.Windows.Computer"]/PrincipalName$</ComputerName>
          <RegistryAttributeDefinitions>
            <RegistryAttributeDefinition>
              <AttributeName>RegistryValueExists</AttributeName>
              <Path>SOFTWARE\Test\RegistryValue</Path>
              <PathType>1</PathType>
              <!-- 0=regKey 1=regValue -->
              <AttributeType>0</AttributeType>
              <!-- 0=CheckIfExists (Boolean) 1=treat data as (String) 2=treat data as (Integer) -->
            </RegistryAttributeDefinition>
            <RegistryAttributeDefinition>
              <AttributeName>RegistryValue</AttributeName>
              <Path>SOFTWARE\Test\RegistryValue</Path>
              <PathType>1</PathType>
              <!-- 0=regKey 1=regValue -->
              <AttributeType>1</AttributeType>
              <!-- 0=CheckIfExists (Boolean) 1=treat data as (String) 2=treat data as (Integer) -->
            </RegistryAttributeDefinition>
          </RegistryAttributeDefinitions>
          <Frequency>14400</Frequency>
          <ClassId>$MPElement[Name="Test.My.Computers.Group"]$</ClassId>
          <ClassInstanceSettings>
            <Settings>
              <Setting>
                <Name>$MPElement[Name='System!System.Entity']/DisplayName$</Name>
                <Value>Test My Group - $Data/Values/RegistryValue$</Value>
              </Setting>
              <Setting>
                <Name>$MPElement[Name='Test.My.Computers.Group']/RegistryValue$</Name>
                <Value>$Data/Values/RegistryValue$</Value>
              </Setting>
            </Settings>
          </ClassInstanceSettings>
          <RelationshipId>$MPElement[Name="MSIL!Microsoft.SystemCenter.InstanceGroupContainsEntities"]$</RelationshipId>
          <SourceTypeId>$MPElement[Name="Test.My.Computers.Group"]$</SourceTypeId>
          <SourceRoleSettings>
            <Settings>
              <Setting>
                <Name>$MPElement[Name='Test.My.Computers.Group']/RegistryValue$</Name>
                <Value>$Data/Values/RegistryValue$</Value>
              </Setting>
            </Settings>
          </SourceRoleSettings>
          <TargetTypeId>$MPElement[Name="Windows!Microsoft.Windows.Computer"]$</TargetTypeId>
          <TargetRoleSettings>
            <Settings>
              <Setting>
                <Name>$MPElement[Name='Windows!Microsoft.Windows.Computer']/PrincipalName$</Name>
                <Value>$Target/Host/Property[Type="Windows!Microsoft.Windows.Computer"]/PrincipalName$</Value>
              </Setting>
            </Settings>
          </TargetRoleSettings>
          <Expression>
            <SimpleExpression>
              <ValueExpression>
                <XPathQuery Type="Boolean">Values/RegistryValueExists</XPathQuery>  
              </ValueExpression>
              <Operator>Equal</Operator> 
              <ValueExpression>
                <Value Type="Boolean">true</Value> 
              </ValueExpression>
            </SimpleExpression>
          </Expression>
        </DataSource>
      </Discovery>
    </Discoveries>
  </Monitoring>
  <LanguagePacks>
    <LanguagePack ID="ENU" IsDefault="true">
      <DisplayStrings>
        <DisplayString ElementID="Test.My.Computers.Group">
          <Name>Test Computers Group</Name>
        </DisplayString>
        <DisplayString ElementID="Test.My.Computers.Group.Discovery">
          <Name>Test Computers Group Discovery</Name>
          <Description>This discovery rule creates and populates groups of Windows Computer Objects that contain a registry key, based on this key's value</Description>
        </DisplayString>
      </DisplayStrings>
    </LanguagePack>
  </LanguagePacks>
</ManagementPackFragment>

 

ZABBIX - Script - Tout les hosts, leur status et leurs templates liés

Le script ci-dessous affiche tout les hosts avec leur status et les templates associés

 

GetZabbixAlIHostsAndTemplates(Native_API).ps1 (2,46 kb)

 

### GET ALL ZABBIX HOSTS WITH THEIR STATUS AND TEMPLATES LINKED


Param(
[Parameter(Mandatory=$false)] $baseurl='https://MyZabbixSrv.My.domain/zabbix',
$credential = (Get-Credential -Credential "MyAccount"),
)

 
$global:JsonParams = @{}
     
# 
Function ConnectZabbix($cred,$baseurl)
{
$JsonParams.body =  @{
        "jsonrpc"= "2.0"
        "method"= "user.login"
        "params"= @{
            "user"= $cred.UserName
            "password"= $cred.GetNetworkCredential().Password
        }
        "id"= 1
        "auth"= $null
    } | ConvertTo-Json
    $JsonParams.uri = "$baseurl/api_jsonrpc.php"
    $JsonParams.headers = @{"Content-Type" = "application/json"}
    $JsonParams.method = "Post"
 
 
[System.Net.ServicePointManager]::SecurityProtocol = 'tls12'
[Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
 
$global:Connresult = Invoke-WebRequest @JsonParams -UseBasicParsing
 
}
 
# Invoke ConnectZabbix
ConnectZabbix -cred $credential -baseurl $baseurl
 
 
 
## GET HOSTS ##
$JsonParams.body = @{
    "jsonrpc"= "2.0"
    "method"= "host.get"
    "params"= @{
        output = "hostid","name","available","status"
        selectParentTemplates = "templateid","name"
        }

    auth = ($Connresult.Content | ConvertFrom-Json).result
    id = 2
} | ConvertTo-Json

$ZabbixHosts = Invoke-WebRequest @JsonParams -UseBasicParsing
$ZabbixHosts = $ZabbixHosts.Content | ConvertFrom-Json



## CREATE EMPTY TABLEAU THAT WILL STORE HOSTS AND TEMPLATES
$HostsAndTemp = @()

foreach ($Zabbhost in $ZabbixHosts.result)
{

$obj = New-Object psobject

$obj | Add-Member -Name "HOSTNAME" -membertype Noteproperty -Value $Zabbhost.name
$obj | Add-Member -Name "ZBX AGENT STATUS" -membertype Noteproperty -Value `
    $( 
      switch ($Zabbhost.status)
      {
      0 {"ENABLED"}
      1 {"DISABLED"}
      }
     )

$obj | Add-Member -Name "ZBX AGENT AVAILABLE" -membertype Noteproperty -Value `
    $( 
      switch ($Zabbhost.available)
      {
      1 {"AVAILABLE"}
      2 {"UNAVAILABLE"}
      0 {"UNKNOWN"}
      }
     )

$obj | Add-Member -Name "ZABBIX TEMPLATES LINKED" -membertype Noteproperty -Value $Zabbhost.parentTemplates.name


$HostsAndTemp += $obj

}


# TABLEAU FINAL
write-host "`n---- $($ZabbixHosts.result.Count) HOSTS ---`n"
write-host "HOSTNAME - ZBX AGENT STATUS - ZBX AGENT AVAILABLE - ZABBIX TEMPLATES LINKED`n"


$HostsAndTemp | sort hostname | ft -AutoSize