Реализация GLPI API на GO
Данный модуль для языка GO реализует механизм работы с HTTP API системы GLPI. Описание АПИ доступно на github, и также в установленной системе по ссылке https://glpi/apirest.php .
Ниже представлен вариант использования на примере работы с принтером. Т.е. в примере будет выполнено: - Добавление производителя - Добавление модели принтера - Добавление домена - Добавление сети (виртуальная сущность связывающаяя активы на сетевом уровне) - Добавление принтера - Добавление сетевого порта и привязка его к принтеру - Добавление IP-адреса и привязка его к порту - Добавление модели картриджа - Добавление картриджа как еденицы учета - "Установка" картриджа (привязка его к соответствующему принтеру)
Алгоритм работы с активами и другими сущностями GLPI ничем не отличается от данного примера.
Краткое описание API
Инициализация сессии
Работа с API в GLPI производится в рамках сессии, которая определяется на основе кода доступа (токена). Данный код генерится и возвращается после положительной авторизации пользователя. Для этого требуется выполнить HTTP запрос к API методу "initSession" и передать в нём Application токен ("Настройки" -> "Общие" -> "API") и токен пользователя ("Администрирование" -> "Пользователи" -> Пользователь -> "Ключи Удаленного доступа") либо имя и пароль.
curl -s -X GET -H "Content-Type: application/json" -H "Authorization: user_token ${GLPI_USER_TOKEN}" -H "App-Token: ${GLPI_APP_TOKEN}" "https://glpi/apirest.php/initSession"
При положительном ответе система вернет:
{
"session_token": "6ud8p3llbvl4qkf56t7klvhvr"
}
В дальнейшем в запросах передается токен приложения и токен сессии:
curl -s -X GET -H "Content-Type: application/json" -H "Session-Token: ${GLPI_SESSION_TOKEN}" -H "App-Token: ${GLPI_APP_TOKEN}" "https://glpi/apirest.php/passivedcequipment"
Получение данных из GLPI
Для получения данных необходимо выполнить http-запрос к определенному методу API. Например для вывода информации о всех компьютерах ссылка будет выглядеть вот так:
"https://glpi/apirest.php/computer"
для получения данных по конкретному компьютеру:
"https://glpi/apirest.php/computer/123"
где "computer" - это тип актива (сущности, еденицы учета и т.п.), "123" - это уникальный идентификатор записи.
Пример использования go-модуля glpi
Импорт пакетов
package main
import (
"encoding/json"
"fmt"
"glpi"
"os"
)
Установка переменных Объявляем и инициализируем переменные:
var (
glpiServerAPI string
glpiUserToken string
glpiAppToken string
)
glpiServerAPI = os.Getenv("GLPI_SERVER_API")
glpiUserToken = os.Getenv("GLPI_USER_TOKEN")
glpiAppToken = os.Getenv("GLPI_APP_TOKEN")
- glpiServerAPI - адрес вызова API, в нашем случае берется из переменной окружения и равен https://glpi/apirest.php
- glpiUserToken - код доступа к API GLPI для конкретного пользователя
- glpiAppToken - код доступа к API GLPI
Для удобства работы, значения для данных переменных, беруться из переменных окружения.
Так как для начала работы необходимо получить код доступа для конкретной сессии, то произведем инициализацию сессиии:
glpiSession, err := glpi.NewSession(glpiServerAPI, glpiUserToken, glpiAppToken)
if err != nil {
fmt.Println(err)
return
}
glpi.NewSession - создает переменную типа Session и возвращает указатель на нее.
_, err = glpiSession.InitSession()
if err != nil {
fmt.Println(err)
return
}
glpiSession.InitSession() - инициализирует сессию, т.е. при вызове данной функции, происходит подключение к GLPI API и возвращается значение кода доступа (токена)сессии. Его значение сохраняется в поле структуры "glpiSession.sessionToken". Данный код можно посмотреть вызвав функцию "glpiSession.GetSessionToken()", т.е.
fmt.Println(glpiSession.GetSessionToken())
Установим значения некоторых переменных:
itemType := "Printer"
printerName := "acc-prn1"
printerDomain := "domain.local"
В GLPI API для поиска данных реализован метод search, вызываемый по ссылке http://glpi/apirest.php/search/itemtype/, где itemtype это тип еденицы учета либо другой сущности внутри GLPI (computer, monitor, printer, line и так далее).
В модуле glpi для поиска используется функция "SearchItem(itemType string, itemName string)" вызываемая с параметрами:
- itemType - вышеозначенный тип
- itemName - имя.
В случае положительного результата функция возвращает цифровой уникальный идентификатор записи "ID".
Тут стоит остановиться подробнее на описании структуры и типах. В GO-модуле glpi каждая из сущностей представлена ввиде структуры с соответсвующим именем. Внтури структуры описано соответствие её полей и полей в итоговом JSON запросе. Например для производителей:
type GlpiManufacturer struct {
Id int `json:"id"`
Comment string `json:"comment"`
Name string `json:"name"`
}
Имена и типы полей go-структуры соответсвуют именам и типам таковых в JSON, кроме первой буквы (в json она маленькая).
Для операций с записями в модуле реализованы функции с именами соответствующими glpi-типам (кроме простых типов с одинаковой струкрутрой). Функция на вход принимает тип запроса ("add" - добавление записи, "update" - редактирование, "delete" -удаление) и структуру соответствующего типа.
Т.е. для производителей это будет glpiSession.Manufacturer("add", manufacturersData) для принтеров glpiSession.Printer("add", printerData) и так далее.
Для простых типов введена структура:
type GlpiSimpleItem struct {
Id int `json:"id"`
Comment string `json:"comment"`
Name string `json:"name"`
}
Для моделей оборудования различного типа:
type GlpiItemModel struct {
Id int `json:"id"`
Comment string `json:"comment"`
Name string `json:"name"`
Product_number string `json:"product_number"`
}
И соответсвенно им и функции SimpleItem() и ItemModel().
В данном примере произведем поиск производителя по наименованию, и если записи с таким именем не найдено то добавим её.
// Get manufacturer ID or append new records/
manufacturersID := glpiSession.SearchItem("Manufacturer", "Systers")
if manufacturersID == 0 {
manufacturersData := glpi.GlpiManufacturer{
Name: "Systers",
Comment: "Holly Systers inc.",
}
manufacturersID = glpiSession.ItemOperation("add", "Manufacturer", manufacturersData)
}
Т.е. если manufacturersID будет равен 0 то инициализируем переменную типа GlpiManufacturer, заполним поля требуемыми значениями и вызовем нужную функцию. На выходе получим ID производителя для дальнейшего использования.
Найдем или добавим сеть (в данном контексте "сеть" - используется для объединения активов и построения схемы сети):
// Get network ID or append new records/
networksID := glpiSession.SearchItem("Network", "Head office")
if networksID == 0 {
networksData := glpi.GlpiSimpleItem{
Name: "Head office",
Comment: "Head office network",
}
networksID = glpiSession.ItemOperation("add", "Network", networksData)
}
Модель принтера:
// Search a used printer model, or append if don't find it.
printerModelID := glpiSession.SearchItem("PrinterModel", "Sys 1200")
if printerModelID == 0 {
printerModelData := glpi.GlpiItemModel{
Name: "Sys 1200",
}
printerModelID = glpiSession.ItemOperation("add", "PrinterModel", printerModelData)
}
Аналогичным образом поступаем и с остальными данными. Добавляем принтер с данными указанными в нижеприведенной структуре. В значениях полей указаны идентификаторы уже созданных записей, плюс переменные полученные выше (в принципе, для добавления принтера достаточно одного имени, остальное можно добавить позже):
printerData := glpi.GlpiPrinter{
Name: printerName,
Locations_id: 1,
Users_id_tech: 3,
Users_id: 2,
Groups_id_tech: 1,
Groups_id: 2,
Have_usb: 1,
Networks_id: networksID,
Manufacturers_id: manufacturersID,
Printermodels_id: printerModelID,
}
printerID := glpiSession.ItemOperation("add", "Printer", printerData)
fmt.Println("Printer was added, ID:", printerID)
Так как принтер подразумевается сетевой то добавим к нему сетевой порт и привяжем к нему IP-адрес. Данная функциональность подобным образом обрабатывается и для других активов GLPI, т.е. алгоритм работы тот-же меняются только itemtype.
Добавляем порт:
// Added Ethernet network port
networkPortData := glpi.GlpiNetworkPort{
Name: "Eth1",
Items_id: printerID,
Itemtype: itemType,
Logical_number: 1,
Instantiation_type: "NetworkPortEthernet",
}
glpiPortID := glpiSession.ItemOperation("add", "NetworkPort", networkPortData)
fmt.Println("Network port was added, ID: ", glpiPortID)
Добавляем домен:
// Getting domain ID, or add it
glpiFqdnID := glpiSession.SearchItem("FQDN", printerDomain)
if glpiFqdnID == 0 {
fqdnData := glpi.GlpiFQDN{
Name: printerDomain,
FQDN: printerDomain,
Comment: "Our local domain",
}
glpiFqdnID = glpiSession.ItemOperation("add", "fqdn", fqdnData)
}
fmt.Println("Domain - ", printerDomain, glpiFqdnID)
Связываем воедино сетевой порт, сетевое имя принтера и домен:
networkData := glpi.GlpiNetworkName{
Name: printerName,
Itemtype: "NetworkPort",
Items_id: glpiPortID,
Fqdns_id: glpiFqdnID,
Comment: "Network port",
}
glpiNetworkID := glpiSession.ItemOperation("add", "NetworkName", networkData)
fmt.Println("Added network name for", printerName, "with ID:", glpiNetworkID)
Добавляем IP-адрес и привязываем его к принтеру и сетевому имени (порту):
ipData := glpi.GlpiIPAddress{
Name: "172.30.30.123",
Itemtype: "NetworkName",
Items_id: glpiNetworkID,
Mainitems_id: printerID,
Mainitemtype: itemType,
Comment: "Network port",
}
glpiIpAddressID := glpiSession.ItemOperation("add", "IPAddress", ipData)
fmt.Println("Added IP address:", glpiIpAddressID)
Т.е. схема добавления "Железка" -> "Порт" -> "Сетевое имя (сеть)" -> "IP-адрес" будет общая для всех активов или устройств имеющих сетевые порты.
На этом с принтером всё. Теперь можно добавить картриджей. Сперва добавляем модель картриджа
// ADD cartridge
requestDataCartridgeItem := glpi.GlpiCartridgeItem{
// Id: "11",
Name: "С4092B",
// Users_id_tech: 3,
// Users_id: 2,
// Groups_id_tech: 1,
// Groups_id: 2,
// Cartridgeitemtypes_id: 0,
Manufacturers_id: manufacturersID,
Alarm_threshold: 5,
}
cartridgeItemsID := glpiSession.ItemOperation("add", "CartridgeItem", requestDataCartridgeItem)
fmt.Println("Add a cartridge item:", cartridgeItemsID)
Затем указываем соответсвие (добавляем запись) между моделью принтера и моделью картриджа
// Linked printer model and cartridge
// printerModelID := glpiSession.SearchItem("PrinterModel", "Sys 1200")
requestData := glpi.GlpiCartridgeItemPrinterModel{
// Id: "11",
Cartridgeitems_id: cartridgeItemsID,
Printermodels_id: printerModelID,
}
cartridgeItemsPrinterModel := glpiSession.ItemOperation("add", "cartridgeitem_printermodel", requestData)
fmt.Println("Added a link between Cartridge model and Printer model:", cartridgeItemsPrinterModel)
Затем добавляем картридж уже как еденицу хранения, привязывая к модели:
// Get the ID of an existing printer, or add it
// printerID = glpiSession.SearchItem("Printer", "acc-prn1")
requestDataCartridge := glpi.GlpiCartridge{
// Id: "11",
Cartridgeitems_id: cartridgeItemsID,
}
cartridgeID := glpiSession.ItemOperation("add", "Cartridge", requestDataCartridge)
fmt.Println("Added a cartridge:", cartridgeID)
"Установим" картридж в принтер, т.е. привяжем конкретный картридж к конкретному принтеру:
// Getting a new cartridge info
res := glpiSession.GetItem("Cartridge", cartridgeID, "")
fmt.Println("Cartridge:", string(res))
var cartridgeInfo glpi.GlpiCartridge
err = json.Unmarshal(res, &cartridgeInfo)
if err != nil {
fmt.Println(err)
}
// Installing the cartridge in the printer
cartridgeInfo.Printers_id = printerID
cartridgeInfo.Date_use = "2021-08-20"
res1 := glpiSession.ItemOperation("update", "Cartridge", cartridgeInfo)
fmt.Println("Add a link between cartridge and printer:", res1)
Перед обновлением конкретной записи требуется выбрать все данные и сохранить в переменную соответствующего типа, если этого не сделать то при выполнении операции "update" не инициализированные поля будут пустыми (т.е. данные затрутся).
Для получения информации о конкретном оборудовании в модуле есть функция GetItem(itemType string, itemID int, otherParam string)
. Она принимает на вход тип оборудования и его ID, возвращает JSON ввиде массива байт ([]byte). Так как функция GetItem() возвращает JSON, то его можно увидеть просто преобразовав байты в строку при помощи string()
Получим информацию о добавленном картридже и выведем в косоль:
// Getting a new cartridge info
res := glpiSession.GetItem("Cartridge", cartridgeID, "")
fmt.Println("Cartridge:", string(res))
Для преобразования данных из json в структуру требуемого нам типа применим функцию json.Unmarshal():
var cartridgeInfo glpi.GlpiCartridge
err = json.Unmarshal(res, &cartridgeInfo)
if err != nil {
fmt.Println(err)
}
fmt.Println("Cartridge:", cartridgeInfo)
Доступ к полям структуры можно получить штатным образом:
- cartridgeInfo.Id - уникальный идентификатор
- cartridgeInfo.Printers_id - идентификатор принтера
- cartridgeInfo.Date_in - дата установкии
- и т.д.
После выполнения всех операция удаляем нашу сессию. GLPI периодически сессии очищает сам, но лучше удалить:
_, err = glpiSession.KillSession()
if err != nil {
fmt.Println(err)
return
}
Код модуля (а также пара утилит с его применением) доступен тут.