build / Garde-fou (pull_request) Failing after 7s
build / Images Harbor (catalog-sync, Dockerfile.catalog-sync) (pull_request) Skipped
build / Images Harbor (web, Dockerfile) (pull_request) Skipped
build / Garde-fou (push) Failing after 8s
build / Images Harbor (catalog-sync, Dockerfile.catalog-sync) (push) Skipped
build / Images Harbor (web, Dockerfile) (push) Skipped
958 lines
38 KiB
JavaScript
958 lines
38 KiB
JavaScript
/* Editeur de projet Enclume.
|
|
Aucune dependance : le projet vit dans le navigateur, le serveur ne fait que
|
|
valider et generer. */
|
|
|
|
'use strict';
|
|
|
|
var CLE = 'enclume.projet';
|
|
var TYPES = JSON.parse(document.getElementById('donnees-types').textContent);
|
|
|
|
var projet = charger();
|
|
var selection = { type: 'reglages', index: 0 };
|
|
var packsCache = null;
|
|
|
|
// ------------------------------------------------------------------ modele
|
|
|
|
function projetVide() {
|
|
return {
|
|
name: 'Nouveau projet',
|
|
centreon_version: '25.10',
|
|
prefix: '',
|
|
plugins_dir: '/usr/lib/centreon/plugins',
|
|
poller: 'Central',
|
|
apply_config: true,
|
|
commands: [],
|
|
service_templates: [],
|
|
host_templates: [],
|
|
hosts: [],
|
|
prerequis: { paquets_debian: [], paquets_rhel: [], verifications: [], notes: '' }
|
|
};
|
|
}
|
|
|
|
function charger() {
|
|
try {
|
|
var brut = localStorage.getItem(CLE);
|
|
if (!brut) return projetVide();
|
|
var p = JSON.parse(brut);
|
|
var vide = projetVide();
|
|
Object.keys(vide).forEach(function (k) { if (p[k] === undefined) p[k] = vide[k]; });
|
|
return p;
|
|
} catch (e) {
|
|
return projetVide();
|
|
}
|
|
}
|
|
|
|
var minuteurSauvegarde = null;
|
|
function sauver() {
|
|
clearTimeout(minuteurSauvegarde);
|
|
minuteurSauvegarde = setTimeout(function () {
|
|
try {
|
|
localStorage.setItem(CLE, JSON.stringify(projet));
|
|
etat('Enregistre dans ce navigateur');
|
|
} catch (e) {
|
|
etat('Enregistrement impossible : espace du navigateur sature');
|
|
}
|
|
}, 250);
|
|
}
|
|
|
|
function etat(message) {
|
|
var cible = document.getElementById('etat-sauvegarde');
|
|
cible.textContent = message;
|
|
clearTimeout(etat.minuteur);
|
|
etat.minuteur = setTimeout(function () { cible.textContent = ''; }, 2500);
|
|
}
|
|
|
|
var LISTES = {
|
|
command: { cle: 'commands', titre: 'Commandes', singulier: 'commande' },
|
|
stpl: { cle: 'service_templates', titre: 'Modeles de service', singulier: 'modele de service' },
|
|
htpl: { cle: 'host_templates', titre: "Modeles d'hote", singulier: "modele d'hote" },
|
|
host: { cle: 'hosts', titre: 'Hotes', singulier: 'hote' }
|
|
};
|
|
|
|
function liste(type) { return projet[LISTES[type].cle]; }
|
|
|
|
function nouvelObjet(type) {
|
|
if (type === 'command') return { name: '', type: 'check', line: '', comment: '' };
|
|
if (type === 'stpl') return {
|
|
name: '', alias: '', parent: 'generic-active-service-custom', command: '', command_args: '',
|
|
check_interval: 5, retry_interval: 1, max_attempts: 3, active_checks: 1, passive_checks: 0,
|
|
host_templates: [], macros: []
|
|
};
|
|
if (type === 'htpl') return {
|
|
name: '', alias: '', parents: ['generic-active-host-custom'], check_command: '',
|
|
check_command_args: '', snmp_version: '', snmp_community: '', timezone: '', macros: []
|
|
};
|
|
return { name: '', alias: '', address: '', templates: [], poller: projet.poller, hostgroups: '', macros: [] };
|
|
}
|
|
|
|
// -------------------------------------------------------------------- rail
|
|
|
|
function rendreRail() {
|
|
var rail = document.getElementById('rail');
|
|
rail.innerHTML = '';
|
|
|
|
rail.appendChild(elt('button', {
|
|
className: 'rail-reglages' + (selection.type === 'reglages' ? ' actif' : ''),
|
|
type: 'button',
|
|
textContent: 'Reglages du projet',
|
|
onclick: function () { selection = { type: 'reglages', index: 0 }; rendre(); }
|
|
}));
|
|
|
|
Object.keys(LISTES).forEach(function (type) {
|
|
var section = elt('section', { className: 'rail-groupe' });
|
|
var head = elt('div', { className: 'rail-head' });
|
|
head.appendChild(elt('h2', { textContent: LISTES[type].titre }));
|
|
head.appendChild(elt('span', { className: 'count', textContent: liste(type).length }));
|
|
head.appendChild(elt('button', {
|
|
className: 'small', type: 'button', textContent: '+', title: 'Ajouter un ' + LISTES[type].singulier,
|
|
onclick: function () {
|
|
liste(type).push(nouvelObjet(type));
|
|
selection = { type: type, index: liste(type).length - 1 };
|
|
sauver(); rendre();
|
|
}
|
|
}));
|
|
section.appendChild(head);
|
|
|
|
liste(type).forEach(function (objet, index) {
|
|
var actif = selection.type === type && selection.index === index;
|
|
var item = elt('button', {
|
|
className: 'rail-item' + (actif ? ' actif' : ''),
|
|
type: 'button',
|
|
onclick: function () { selection = { type: type, index: index }; rendre(); }
|
|
});
|
|
item.appendChild(elt('span', {
|
|
className: 'nom', textContent: objet.name || '(sans nom)',
|
|
dataset: { railNom: type + ':' + index }
|
|
}));
|
|
if (type === 'stpl' && objet.host_templates.length === 0) {
|
|
item.appendChild(elt('span', { className: 'tag warn', textContent: 'non rattache' }));
|
|
}
|
|
section.appendChild(item);
|
|
});
|
|
rail.appendChild(section);
|
|
});
|
|
}
|
|
|
|
// -------------------------------------------------------------------- main
|
|
|
|
function rendreMain() {
|
|
var main = document.getElementById('main');
|
|
main.innerHTML = '';
|
|
|
|
if (selection.type === 'generation') return rendreGeneration(main);
|
|
if (selection.type === 'reglages') return rendreReglages(main);
|
|
|
|
var items = liste(selection.type);
|
|
if (!items.length) {
|
|
if (projetTotal() === 0) {
|
|
main.appendChild(document.getElementById('tpl-vide').content.cloneNode(true));
|
|
} else {
|
|
main.appendChild(elt('p', { className: 'lede', textContent: 'Aucun objet dans cette liste.' }));
|
|
}
|
|
return;
|
|
}
|
|
if (selection.index >= items.length) selection.index = items.length - 1;
|
|
var objet = items[selection.index];
|
|
|
|
var entete = elt('div', { className: 'objet-head' });
|
|
entete.appendChild(elt('h2', { textContent: LISTES[selection.type].titre }));
|
|
entete.appendChild(elt('span', { className: 'grow' }));
|
|
entete.appendChild(elt('button', {
|
|
className: 'small', type: 'button', textContent: 'Dupliquer',
|
|
onclick: function () {
|
|
var copie = JSON.parse(JSON.stringify(objet));
|
|
copie.name = copie.name + '-copie';
|
|
items.splice(selection.index + 1, 0, copie);
|
|
selection.index += 1; sauver(); rendre();
|
|
}
|
|
}));
|
|
entete.appendChild(elt('button', {
|
|
className: 'small danger', type: 'button', textContent: 'Supprimer',
|
|
onclick: function () {
|
|
if (!confirm('Supprimer ' + (objet.name || 'cet objet') + ' ?')) return;
|
|
items.splice(selection.index, 1);
|
|
selection.index = Math.max(0, selection.index - 1);
|
|
sauver(); rendre();
|
|
}
|
|
}));
|
|
main.appendChild(entete);
|
|
|
|
if (selection.type === 'command') rendreCommande(main, objet);
|
|
if (selection.type === 'stpl') rendreStpl(main, objet);
|
|
if (selection.type === 'htpl') rendreHtpl(main, objet);
|
|
if (selection.type === 'host') rendreHost(main, objet);
|
|
}
|
|
|
|
function projetTotal() {
|
|
return projet.commands.length + projet.service_templates.length +
|
|
projet.host_templates.length + projet.hosts.length;
|
|
}
|
|
|
|
function rendreReglages(main) {
|
|
main.appendChild(elt('h2', { textContent: 'Reglages du projet' }));
|
|
main.appendChild(elt('p', {
|
|
className: 'lede',
|
|
textContent: "Ces valeurs s'appliquent a l'ensemble du code genere. Le prefixe evite les collisions " +
|
|
'avec les packs officiels deja installes sur votre central.'
|
|
}));
|
|
var grille = elt('div', { className: 'grille' });
|
|
grille.appendChild(champ('Prefixe applique aux objets crees', projet, 'prefix', { placeholder: 'ex. ACME-' }));
|
|
grille.appendChild(champ('Repertoire des plugins', projet, 'plugins_dir'));
|
|
grille.appendChild(champ('Collecteur cible', projet, 'poller'));
|
|
main.appendChild(grille);
|
|
main.appendChild(caseACocher("Ajouter APPLYCFG a la fin des scripts", projet, 'apply_config'));
|
|
|
|
main.appendChild(sousTitre('Prerequis des sondes',
|
|
"Ce qui doit etre installe sur le collecteur ; genere un fichier distinct du CLAPI."));
|
|
var pre = elt('div', { className: 'prerequis' });
|
|
if (!projet.prerequis) projet.prerequis = { paquets_debian: [], paquets_rhel: [], verifications: [], notes: '' };
|
|
pre.appendChild(champListe('Paquets Debian et Ubuntu', projet.prerequis, 'paquets_debian'));
|
|
pre.appendChild(champListe('Paquets RHEL, Alma, Rocky', projet.prerequis, 'paquets_rhel'));
|
|
main.appendChild(pre);
|
|
main.appendChild(champZone('Notes a l\'attention de l\'installateur', projet.prerequis, 'notes'));
|
|
main.appendChild(champListe('Commandes de verification', projet.prerequis, 'verifications'));
|
|
|
|
var info = elt('div', { className: 'notice' });
|
|
info.appendChild(elt('p', {
|
|
textContent: 'Le projet est conserve dans ce navigateur uniquement. Rien n\u2019est envoye au serveur ' +
|
|
'tant que vous ne demandez pas la generation, et aucune macro de type mot de passe n\u2019est stockee ' +
|
|
'ni partagee.'
|
|
}));
|
|
main.appendChild(info);
|
|
}
|
|
|
|
function rendreCommande(main, cmd) {
|
|
var grille = elt('div', { className: 'grille' });
|
|
grille.appendChild(champ('Nom de la commande', cmd, 'name', { rail: true, placeholder: 'OS-Linux-SNMP-Cpu' }));
|
|
grille.appendChild(champSelect('Type', cmd, 'type', TYPES));
|
|
main.appendChild(grille);
|
|
main.appendChild(champ('Commentaire', cmd, 'comment'));
|
|
main.appendChild(champZone('Ligne de commande', cmd, 'line', {
|
|
aide: '$CENTREONPLUGINS$ sera remplace par le repertoire des plugins. ' +
|
|
'Utilisez $HOSTADDRESS$, $_HOSTMACRO$ et $_SERVICEMACRO$ comme dans Centreon.'
|
|
}));
|
|
|
|
var barre = elt('div', { className: 'barre' });
|
|
barre.appendChild(elt('button', {
|
|
type: 'button', className: 'small', textContent: 'Creer un modele de service depuis cette commande',
|
|
onclick: function () {
|
|
var stpl = nouvelObjet('stpl');
|
|
stpl.name = cmd.name;
|
|
stpl.alias = cmd.name.split('-').pop();
|
|
stpl.command = cmd.name;
|
|
stpl.macros = macrosDepuisLigne(cmd.line).service;
|
|
projet.service_templates.push(stpl);
|
|
selection = { type: 'stpl', index: projet.service_templates.length - 1 };
|
|
sauver(); rendre();
|
|
}
|
|
}));
|
|
main.appendChild(barre);
|
|
}
|
|
|
|
function macrosDepuisLigne(ligne) {
|
|
var service = (ligne.match(/\$_SERVICE([A-Z0-9_]+)\$/g) || []).map(function (m) {
|
|
return m.replace('$_SERVICE', '').replace('$', '');
|
|
});
|
|
var host = (ligne.match(/\$_HOST([A-Z0-9_]+)\$/g) || []).map(function (m) {
|
|
return m.replace('$_HOST', '').replace('$', '');
|
|
});
|
|
var uniq = function (noms) {
|
|
var vus = {}, sortie = [];
|
|
noms.forEach(function (n) {
|
|
if (vus[n]) return;
|
|
vus[n] = 1;
|
|
sortie.push({ name: n, value: '', description: '', is_password: /(PASSWORD|SECRET|TOKEN|APIKEY)/.test(n) });
|
|
});
|
|
return sortie;
|
|
};
|
|
return { service: uniq(service), host: uniq(host) };
|
|
}
|
|
|
|
function rendreStpl(main, stpl) {
|
|
var grille = elt('div', { className: 'grille' });
|
|
grille.appendChild(champ('Nom du modele', stpl, 'name', { rail: true }));
|
|
grille.appendChild(champ('Alias', stpl, 'alias'));
|
|
grille.appendChild(champ('Modele parent', stpl, 'parent'));
|
|
grille.appendChild(champSelectListe('Commande de controle', stpl, 'command',
|
|
projet.commands.map(function (c) { return c.name; })));
|
|
main.appendChild(grille);
|
|
|
|
var grille2 = elt('div', { className: 'grille quatre' });
|
|
grille2.appendChild(champ('Intervalle', stpl, 'check_interval', { type: 'number' }));
|
|
grille2.appendChild(champ('Intervalle de reprise', stpl, 'retry_interval', { type: 'number' }));
|
|
grille2.appendChild(champ('Tentatives', stpl, 'max_attempts', { type: 'number' }));
|
|
grille2.appendChild(champ('Arguments', stpl, 'command_args', { placeholder: '!80!90' }));
|
|
main.appendChild(grille2);
|
|
|
|
main.appendChild(sousTitre("Rattachement aux modeles d'hote",
|
|
"Un hote heritant d'un de ces modeles recevra ce service."));
|
|
if (!projet.host_templates.length) {
|
|
main.appendChild(elt('p', { className: 'lede', textContent: "Aucun modele d'hote dans le projet." }));
|
|
} else {
|
|
var cases = elt('div', { className: 'cases' });
|
|
projet.host_templates.forEach(function (htpl) {
|
|
cases.appendChild(caseListe(htpl.name || '(sans nom)', stpl.host_templates, htpl.name));
|
|
});
|
|
main.appendChild(cases);
|
|
}
|
|
|
|
main.appendChild(sousTitre('Macros du service', 'Seuils et parametres propres a cette sonde.'));
|
|
main.appendChild(editeurMacros(stpl, function () {
|
|
var cmd = projet.commands.filter(function (c) { return c.name === stpl.command; })[0];
|
|
return cmd ? macrosDepuisLigne(cmd.line).service : [];
|
|
}));
|
|
}
|
|
|
|
function rendreHtpl(main, htpl) {
|
|
var grille = elt('div', { className: 'grille' });
|
|
grille.appendChild(champ('Nom du modele', htpl, 'name', { rail: true, placeholder: 'OS-Linux-SNMP-custom' }));
|
|
grille.appendChild(champ('Alias', htpl, 'alias'));
|
|
grille.appendChild(champListe('Modeles parents', htpl, 'parents'));
|
|
grille.appendChild(champ('Fuseau horaire', htpl, 'timezone', { placeholder: ':Europe/Paris' }));
|
|
main.appendChild(grille);
|
|
|
|
var grille2 = elt('div', { className: 'grille' });
|
|
grille2.appendChild(champ('Commande de verification', htpl, 'check_command', { placeholder: 'check_centreon_ping' }));
|
|
grille2.appendChild(champ('Arguments', htpl, 'check_command_args', { placeholder: '!3!200,20%!400,50%' }));
|
|
grille2.appendChild(champ('Version SNMP', htpl, 'snmp_version', { placeholder: '2c' }));
|
|
grille2.appendChild(champ('Communaute SNMP', htpl, 'snmp_community'));
|
|
main.appendChild(grille2);
|
|
|
|
main.appendChild(sousTitre("Macros du modele d'hote",
|
|
'Heritees par tous les hotes rattaches : identifiants, ports, options du plugin.'));
|
|
main.appendChild(editeurMacros(htpl, function () { return []; }));
|
|
|
|
var services = projet.service_templates.filter(function (s) {
|
|
return s.host_templates.indexOf(htpl.name) !== -1;
|
|
});
|
|
main.appendChild(sousTitre('Services deployes par ce modele', services.length + ' modele(s) de service'));
|
|
var liste_ = elt('div', { className: 'puces' });
|
|
services.forEach(function (s) { liste_.appendChild(elt('span', { className: 'puce', textContent: s.name })); });
|
|
main.appendChild(liste_);
|
|
}
|
|
|
|
function rendreHost(main, host) {
|
|
var grille = elt('div', { className: 'grille' });
|
|
grille.appendChild(champ("Nom de l'hote", host, 'name', { rail: true }));
|
|
grille.appendChild(champ('Alias', host, 'alias'));
|
|
grille.appendChild(champ('Adresse IP ou DNS', host, 'address'));
|
|
grille.appendChild(champ('Collecteur', host, 'poller'));
|
|
grille.appendChild(champ("Groupes d'hotes", host, 'hostgroups'));
|
|
main.appendChild(grille);
|
|
|
|
main.appendChild(sousTitre("Modeles d'hote appliques", ''));
|
|
var cases = elt('div', { className: 'cases' });
|
|
projet.host_templates.forEach(function (htpl) {
|
|
cases.appendChild(caseListe(htpl.name || '(sans nom)', host.templates, htpl.name));
|
|
});
|
|
main.appendChild(cases);
|
|
|
|
main.appendChild(sousTitre("Macros propres a l'hote", 'Elles surchargent celles du modele.'));
|
|
main.appendChild(editeurMacros(host, function () { return []; }));
|
|
}
|
|
|
|
// --------------------------------------------------------------- generation
|
|
|
|
function rendreGeneration(main) {
|
|
main.appendChild(elt('h2', { textContent: 'Code CLAPI' }));
|
|
var zone = elt('div', { id: 'zone-generation' });
|
|
zone.appendChild(elt('p', { className: 'lede', textContent: 'Generation en cours...' }));
|
|
main.appendChild(zone);
|
|
|
|
fetch('/api/generer', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ project: projet })
|
|
}).then(function (r) { return r.json(); }).then(function (data) {
|
|
zone.innerHTML = '';
|
|
|
|
var resume = elt('div', { className: 'resume' });
|
|
Object.keys(data.summary).forEach(function (cle) {
|
|
var bloc = elt('div');
|
|
bloc.appendChild(elt('b', { textContent: data.summary[cle] }));
|
|
bloc.appendChild(elt('span', { textContent: cle }));
|
|
resume.appendChild(bloc);
|
|
});
|
|
zone.appendChild(resume);
|
|
|
|
if (data.issues.length) {
|
|
var erreurs = data.issues.filter(function (i) { return i.level === 'error'; });
|
|
var note = elt('div', { className: 'notice' + (erreurs.length ? ' bad' : '') });
|
|
note.appendChild(elt('b', {
|
|
textContent: erreurs.length
|
|
? erreurs.length + ' erreur(s) a corriger avant de deployer'
|
|
: data.issues.length + ' point(s) de vigilance'
|
|
}));
|
|
var ul = elt('ul');
|
|
data.issues.forEach(function (i) {
|
|
ul.appendChild(elt('li', { textContent: i.message, className: i.level }));
|
|
});
|
|
note.appendChild(ul);
|
|
zone.appendChild(note);
|
|
}
|
|
|
|
var noms = Object.keys(data.files);
|
|
var onglets = elt('div', { className: 'tabs' });
|
|
var panneaux = {};
|
|
noms.forEach(function (nom, i) {
|
|
var bouton = elt('button', {
|
|
type: 'button', textContent: nom,
|
|
onclick: function () {
|
|
noms.forEach(function (n) {
|
|
panneaux[n].hidden = n !== nom;
|
|
onglets.querySelectorAll('button').forEach(function (b) {
|
|
b.setAttribute('aria-selected', String(b.textContent === nom));
|
|
});
|
|
});
|
|
}
|
|
});
|
|
bouton.setAttribute('aria-selected', String(i === 0));
|
|
onglets.appendChild(bouton);
|
|
});
|
|
zone.appendChild(onglets);
|
|
noms.forEach(function (nom, i) {
|
|
var pre = elt('pre', { className: 'code', textContent: data.files[nom] });
|
|
pre.hidden = i !== 0;
|
|
panneaux[nom] = pre;
|
|
zone.appendChild(pre);
|
|
});
|
|
|
|
var barre = elt('div', { className: 'barre' });
|
|
noms.forEach(function (nom) {
|
|
barre.appendChild(elt('button', {
|
|
type: 'button', textContent: nom,
|
|
onclick: function () { telecharger(nom, data.files[nom]); }
|
|
}));
|
|
});
|
|
barre.appendChild(elt('button', {
|
|
type: 'button', className: 'primary auto', textContent: 'Archive complete (.zip)',
|
|
onclick: function () {
|
|
document.getElementById('form-zip-project').value = JSON.stringify(projet);
|
|
document.getElementById('form-zip').submit();
|
|
}
|
|
}));
|
|
zone.appendChild(barre);
|
|
}).catch(function () {
|
|
zone.innerHTML = '';
|
|
zone.appendChild(elt('div', { className: 'notice bad', textContent: 'La generation a echoue. Reessayez.' }));
|
|
});
|
|
}
|
|
|
|
function telecharger(nom, contenu) {
|
|
var blob = new Blob([contenu], { type: 'text/plain;charset=utf-8' });
|
|
var url = URL.createObjectURL(blob);
|
|
var a = elt('a', { href: url, download: nom });
|
|
document.body.appendChild(a); a.click(); a.remove();
|
|
setTimeout(function () { URL.revokeObjectURL(url); }, 1000);
|
|
}
|
|
|
|
// ------------------------------------------------------------- composants
|
|
|
|
function elt(tag, props) {
|
|
var noeud = document.createElement(tag);
|
|
Object.keys(props || {}).forEach(function (cle) {
|
|
if (cle === 'dataset') Object.assign(noeud.dataset, props[cle]);
|
|
else if (cle === 'onclick') noeud.addEventListener('click', props[cle]);
|
|
else noeud[cle] = props[cle];
|
|
});
|
|
return noeud;
|
|
}
|
|
|
|
function champ(libelle, objet, cle, options) {
|
|
options = options || {};
|
|
var label = elt('label', { className: 'field' });
|
|
label.appendChild(elt('span', { textContent: libelle }));
|
|
var input = elt('input', { type: options.type || 'text', value: objet[cle] });
|
|
if (options.placeholder) input.placeholder = options.placeholder;
|
|
input.addEventListener('input', function () {
|
|
objet[cle] = options.type === 'number' ? parseInt(input.value || '0', 10) : input.value;
|
|
if (options.rail) majNomRail();
|
|
sauver();
|
|
});
|
|
label.appendChild(input);
|
|
return label;
|
|
}
|
|
|
|
function champZone(libelle, objet, cle, options) {
|
|
options = options || {};
|
|
var label = elt('label', { className: 'field' });
|
|
label.appendChild(elt('span', { textContent: libelle }));
|
|
var zone = elt('textarea', { rows: 5, value: objet[cle] });
|
|
zone.addEventListener('input', function () { objet[cle] = zone.value; sauver(); });
|
|
label.appendChild(zone);
|
|
if (options.aide) label.appendChild(elt('small', { textContent: options.aide }));
|
|
return label;
|
|
}
|
|
|
|
function champSelect(libelle, objet, cle, choix) {
|
|
var label = elt('label', { className: 'field' });
|
|
label.appendChild(elt('span', { textContent: libelle }));
|
|
var select = elt('select');
|
|
Object.keys(choix).forEach(function (valeur) {
|
|
var option = elt('option', { value: valeur, textContent: choix[valeur] });
|
|
if (objet[cle] === valeur) option.selected = true;
|
|
select.appendChild(option);
|
|
});
|
|
select.addEventListener('change', function () { objet[cle] = select.value; sauver(); });
|
|
label.appendChild(select);
|
|
return label;
|
|
}
|
|
|
|
function champSelectListe(libelle, objet, cle, valeurs) {
|
|
var label = elt('label', { className: 'field' });
|
|
label.appendChild(elt('span', { textContent: libelle }));
|
|
var input = elt('input', { type: 'text', value: objet[cle], list: 'liste-' + cle });
|
|
var datalist = elt('datalist', { id: 'liste-' + cle });
|
|
valeurs.forEach(function (v) { datalist.appendChild(elt('option', { value: v })); });
|
|
input.addEventListener('input', function () { objet[cle] = input.value; sauver(); });
|
|
label.appendChild(input);
|
|
label.appendChild(datalist);
|
|
return label;
|
|
}
|
|
|
|
function champListe(libelle, objet, cle) {
|
|
var label = elt('label', { className: 'field' });
|
|
label.appendChild(elt('span', { textContent: libelle + ' (separes par une virgule)' }));
|
|
var input = elt('input', { type: 'text', value: (objet[cle] || []).join(', ') });
|
|
input.addEventListener('input', function () {
|
|
objet[cle] = input.value.split(',').map(function (v) { return v.trim(); }).filter(Boolean);
|
|
sauver();
|
|
});
|
|
label.appendChild(input);
|
|
return label;
|
|
}
|
|
|
|
function caseACocher(libelle, objet, cle) {
|
|
var label = elt('label', { className: 'check' });
|
|
var input = elt('input', { type: 'checkbox', checked: !!objet[cle] });
|
|
input.addEventListener('change', function () { objet[cle] = input.checked; sauver(); });
|
|
label.appendChild(input);
|
|
label.appendChild(elt('span', { textContent: libelle }));
|
|
return label;
|
|
}
|
|
|
|
function caseListe(libelle, tableau, valeur) {
|
|
var label = elt('label', { className: 'check' });
|
|
var input = elt('input', { type: 'checkbox', checked: tableau.indexOf(valeur) !== -1 });
|
|
input.addEventListener('change', function () {
|
|
var i = tableau.indexOf(valeur);
|
|
if (input.checked && i === -1) tableau.push(valeur);
|
|
if (!input.checked && i !== -1) tableau.splice(i, 1);
|
|
sauver(); rendreRail();
|
|
});
|
|
label.appendChild(input);
|
|
label.appendChild(elt('span', { textContent: libelle }));
|
|
return label;
|
|
}
|
|
|
|
function sousTitre(titre, aide) {
|
|
var bloc = elt('div', { className: 'sous-titre' });
|
|
bloc.appendChild(elt('h3', { textContent: titre }));
|
|
if (aide) bloc.appendChild(elt('span', { textContent: aide }));
|
|
return bloc;
|
|
}
|
|
|
|
function editeurMacros(objet, suggestions) {
|
|
var bloc = elt('div');
|
|
var table = elt('div', { className: 'macros-table' });
|
|
objet.macros.forEach(function (macro, index) {
|
|
var ligne = elt('div', { className: 'macro-ligne' });
|
|
var nom = elt('input', { type: 'text', value: macro.name, placeholder: 'NOM' });
|
|
nom.addEventListener('input', function () { macro.name = nom.value.toUpperCase(); nom.value = macro.name; sauver(); });
|
|
var valeur = elt('input', { type: 'text', value: macro.value, placeholder: 'valeur' });
|
|
valeur.addEventListener('input', function () { macro.value = valeur.value; sauver(); });
|
|
var desc = elt('input', { type: 'text', value: macro.description, placeholder: 'description' });
|
|
desc.addEventListener('input', function () { macro.description = desc.value; sauver(); });
|
|
var mdp = elt('label', { className: 'check', title: 'Macro de type mot de passe' });
|
|
var coche = elt('input', { type: 'checkbox', checked: !!macro.is_password });
|
|
coche.addEventListener('change', function () { macro.is_password = coche.checked; sauver(); rendreMain(); });
|
|
mdp.appendChild(coche);
|
|
mdp.appendChild(elt('span', { textContent: 'mdp' }));
|
|
var sup = elt('button', {
|
|
type: 'button', className: 'small danger', textContent: 'x',
|
|
onclick: function () { objet.macros.splice(index, 1); sauver(); rendreMain(); }
|
|
});
|
|
ligne.appendChild(nom); ligne.appendChild(valeur); ligne.appendChild(desc);
|
|
ligne.appendChild(mdp); ligne.appendChild(sup);
|
|
if (macro.is_password && macro.value) {
|
|
ligne.appendChild(elt('small', { className: 'avert', textContent: 'sortira en clair dans les fichiers' }));
|
|
}
|
|
table.appendChild(ligne);
|
|
});
|
|
bloc.appendChild(table);
|
|
|
|
var barre = elt('div', { className: 'barre' });
|
|
barre.appendChild(elt('button', {
|
|
type: 'button', className: 'small', textContent: 'Ajouter une macro',
|
|
onclick: function () {
|
|
objet.macros.push({ name: '', value: '', description: '', is_password: false });
|
|
sauver(); rendreMain();
|
|
}
|
|
}));
|
|
var manquantes = suggestions().filter(function (m) {
|
|
return !objet.macros.some(function (existante) { return existante.name === m.name; });
|
|
});
|
|
if (manquantes.length) {
|
|
barre.appendChild(elt('button', {
|
|
type: 'button', className: 'small', textContent: 'Creer les macros de la commande (' + manquantes.length + ')',
|
|
onclick: function () { manquantes.forEach(function (m) { objet.macros.push(m); }); sauver(); rendreMain(); }
|
|
}));
|
|
}
|
|
bloc.appendChild(barre);
|
|
return bloc;
|
|
}
|
|
|
|
function majNomRail() {
|
|
var cible = document.querySelector('[data-rail-nom="' + selection.type + ':' + selection.index + '"]');
|
|
if (cible) cible.textContent = liste(selection.type)[selection.index].name || '(sans nom)';
|
|
}
|
|
|
|
// ------------------------------------------------------------------ modale
|
|
|
|
function ouvrirModale(titre, construire) {
|
|
document.getElementById('modal-titre').textContent = titre;
|
|
var corps = document.getElementById('modal-corps');
|
|
corps.innerHTML = '';
|
|
construire(corps);
|
|
document.getElementById('modal').hidden = false;
|
|
}
|
|
|
|
function fermerModale() { document.getElementById('modal').hidden = true; }
|
|
|
|
function ouvrirCataloguePacks() {
|
|
ouvrirModale('Importer un pack', function (corps) {
|
|
var recherche = elt('input', { type: 'text', placeholder: 'Filtrer par nom, technologie, plugin...' });
|
|
corps.appendChild(recherche);
|
|
var resultats = elt('div', { className: 'packs' });
|
|
corps.appendChild(resultats);
|
|
|
|
function afficher(packs) {
|
|
resultats.innerHTML = '';
|
|
packs.forEach(function (pack) {
|
|
var ligne = elt('div', { className: 'pack-ligne' });
|
|
var texte = elt('div');
|
|
texte.appendChild(elt('b', { textContent: pack.name }));
|
|
if (pack.status !== 'verified') {
|
|
texte.appendChild(elt('span', { className: 'tag warn', textContent: 'a valider' }));
|
|
}
|
|
texte.appendChild(elt('div', { className: 'desc', textContent: pack.description }));
|
|
texte.appendChild(elt('div', { className: 'plugin', textContent: pack.plugin + ' | ' + pack.services.length + ' sondes' }));
|
|
ligne.appendChild(texte);
|
|
var actions = elt('div', { className: 'barre' });
|
|
actions.appendChild(elt('button', {
|
|
type: 'button', className: 'small', textContent: 'Sondes par defaut',
|
|
onclick: function () { importerPack(pack.id, false); }
|
|
}));
|
|
actions.appendChild(elt('button', {
|
|
type: 'button', className: 'small', textContent: 'Toutes les sondes',
|
|
onclick: function () { importerPack(pack.id, true); }
|
|
}));
|
|
ligne.appendChild(actions);
|
|
resultats.appendChild(ligne);
|
|
});
|
|
if (!packs.length) resultats.appendChild(elt('p', { className: 'lede', textContent: 'Aucun pack ne correspond.' }));
|
|
}
|
|
|
|
function filtrer() {
|
|
var q = recherche.value.toLowerCase();
|
|
afficher(packsCache.filter(function (p) {
|
|
return (p.name + ' ' + p.description + ' ' + p.plugin + ' ' + p.category_label).toLowerCase().indexOf(q) !== -1;
|
|
}));
|
|
}
|
|
recherche.addEventListener('input', filtrer);
|
|
|
|
if (packsCache) { afficher(packsCache); }
|
|
else {
|
|
resultats.appendChild(elt('p', { className: 'lede', textContent: 'Chargement du catalogue...' }));
|
|
fetch('/api/packs').then(function (r) { return r.json(); }).then(function (packs) {
|
|
packsCache = packs; afficher(packs);
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
function importerPack(id, tout) {
|
|
fetch('/api/packs/' + id + '/projet' + (tout ? '?tout=1' : ''))
|
|
.then(function (r) { return r.json(); })
|
|
.then(function (fragment) {
|
|
var ignores = [];
|
|
['commands', 'service_templates', 'host_templates'].forEach(function (cle) {
|
|
var existants = {};
|
|
projet[cle].forEach(function (o) { existants[o.name] = true; });
|
|
fragment[cle].forEach(function (o) {
|
|
if (existants[o.name]) { ignores.push(o.name); return; }
|
|
projet[cle].push(o);
|
|
});
|
|
});
|
|
fermerModale();
|
|
selection = { type: 'htpl', index: Math.max(0, projet.host_templates.length - 1) };
|
|
sauver(); rendre();
|
|
etat(ignores.length ? ignores.length + ' objet(s) deja present(s), ignore(s)' : 'Pack importe');
|
|
});
|
|
}
|
|
|
|
// ------------------------------------------------------------------ actions
|
|
|
|
function rendre() {
|
|
rendreRail();
|
|
rendreMain();
|
|
document.getElementById('projet-nom').value = projet.name;
|
|
document.getElementById('projet-version').value = projet.centreon_version;
|
|
}
|
|
|
|
document.getElementById('projet-nom').addEventListener('input', function (e) {
|
|
projet.name = e.target.value; sauver();
|
|
});
|
|
document.getElementById('projet-version').addEventListener('change', function (e) {
|
|
projet.centreon_version = e.target.value; sauver();
|
|
});
|
|
document.getElementById('btn-pack').addEventListener('click', ouvrirCataloguePacks);
|
|
document.getElementById('btn-generer').addEventListener('click', function () {
|
|
selection = { type: 'generation', index: 0 }; rendre();
|
|
});
|
|
document.getElementById('btn-export').addEventListener('click', function () {
|
|
telecharger((projet.name || 'projet').replace(/[^\w-]+/g, '-') + '.json', JSON.stringify(projet, null, 2));
|
|
});
|
|
document.getElementById('btn-import').addEventListener('click', function () {
|
|
var input = elt('input', { type: 'file', accept: '.json,application/json' });
|
|
input.addEventListener('change', function () {
|
|
var fichier = input.files[0];
|
|
if (!fichier) return;
|
|
var lecteur = new FileReader();
|
|
lecteur.onload = function () {
|
|
try {
|
|
var charge = JSON.parse(lecteur.result);
|
|
var vide = projetVide();
|
|
Object.keys(vide).forEach(function (k) { if (charge[k] === undefined) charge[k] = vide[k]; });
|
|
projet = charge;
|
|
selection = { type: 'reglages', index: 0 };
|
|
sauver(); rendre();
|
|
etat('Projet ouvert');
|
|
} catch (e) {
|
|
alert("Ce fichier n'est pas un projet valide.");
|
|
}
|
|
};
|
|
lecteur.readAsText(fichier);
|
|
});
|
|
input.click();
|
|
});
|
|
document.getElementById('btn-vider').addEventListener('click', function () {
|
|
if (!confirm('Vider le projet en cours ? Cette action est definitive.')) return;
|
|
projet = projetVide();
|
|
selection = { type: 'reglages', index: 0 };
|
|
sauver(); rendre();
|
|
});
|
|
document.querySelectorAll('[data-fermer-modal]').forEach(function (b) {
|
|
b.addEventListener('click', fermerModale);
|
|
});
|
|
document.getElementById('modal').addEventListener('click', function (e) {
|
|
if (e.target.id === 'modal') fermerModale();
|
|
});
|
|
document.addEventListener('keydown', function (e) {
|
|
if (e.key === 'Escape') fermerModale();
|
|
});
|
|
|
|
rendre();
|
|
|
|
// ==========================================================================
|
|
// Chantiers 3 a 5 : partage, projets enregistres, soumission de packs.
|
|
// Tout ce bloc est inerte si le serveur n'annonce pas la fonctionnalite.
|
|
// ==========================================================================
|
|
|
|
var CONTEXTE = (function () {
|
|
var noeud = document.getElementById('donnees-contexte');
|
|
try { return noeud ? JSON.parse(noeud.textContent) : {}; } catch (e) { return {}; }
|
|
})();
|
|
var FONCTIONS = CONTEXTE.fonctions || {};
|
|
|
|
function appelJSON(url, options) {
|
|
options = options || {};
|
|
options.headers = Object.assign({ 'Content-Type': 'application/json' }, options.headers || {});
|
|
return fetch(url, options).then(function (reponse) {
|
|
return reponse.json().catch(function () { return {}; }).then(function (corps) {
|
|
if (!reponse.ok) throw new Error(corps.erreur || 'echec de la requete');
|
|
return corps;
|
|
});
|
|
});
|
|
}
|
|
|
|
function brancher(id, action) {
|
|
var bouton = document.getElementById(id);
|
|
if (bouton) bouton.addEventListener('click', action);
|
|
}
|
|
|
|
// --------------------------------------------------------------- partage
|
|
|
|
brancher('btn-partager', function () {
|
|
appelJSON('/api/partages', { method: 'POST', body: JSON.stringify({ project: projet }) })
|
|
.then(function (donnees) {
|
|
ouvrirModale('Lien de partage', function (corps) {
|
|
corps.appendChild(elt('p', {
|
|
className: 'lede',
|
|
textContent: 'Toute personne disposant de ce lien peut ouvrir une copie de ce projet. ' +
|
|
donnees.avertissement
|
|
}));
|
|
var champ = elt('input', { type: 'text', value: donnees.url, readOnly: true });
|
|
corps.appendChild(champ);
|
|
champ.focus(); champ.select();
|
|
var barre = elt('div', { className: 'barre' });
|
|
barre.appendChild(elt('button', {
|
|
type: 'button', textContent: 'Copier le lien',
|
|
onclick: function () {
|
|
champ.select();
|
|
if (navigator.clipboard) navigator.clipboard.writeText(donnees.url);
|
|
else document.execCommand('copy');
|
|
etat('Lien copie');
|
|
}
|
|
}));
|
|
corps.appendChild(barre);
|
|
corps.appendChild(elt('small', { textContent: 'Ce lien expire le ' + (donnees.expire_le || '').slice(0, 10) + '.' }));
|
|
});
|
|
})
|
|
.catch(function (erreur) { alert('Partage impossible : ' + erreur.message); });
|
|
});
|
|
|
|
// Ouverture d'un projet partage : la page /p/<id> passe l'identifiant.
|
|
if (CONTEXTE.partage) {
|
|
appelJSON('/api/partages/' + encodeURIComponent(CONTEXTE.partage))
|
|
.then(function (donnees) {
|
|
var vide = projetVide();
|
|
Object.keys(vide).forEach(function (k) {
|
|
if (donnees.project[k] === undefined) donnees.project[k] = vide[k];
|
|
});
|
|
projet = donnees.project;
|
|
selection = { type: 'reglages', index: 0 };
|
|
sauver(); rendre();
|
|
etat('Projet partage ouvert');
|
|
})
|
|
.catch(function () { alert("Ce lien de partage n'existe pas ou a expire."); });
|
|
}
|
|
|
|
// ----------------------------------------------------- projets en ligne
|
|
|
|
brancher('btn-serveur-enregistrer', function () {
|
|
appelJSON('/api/projets', {
|
|
method: 'POST',
|
|
body: JSON.stringify({ project: projet, id: projet.__id_serveur || null })
|
|
}).then(function (donnees) {
|
|
projet.__id_serveur = donnees.id;
|
|
sauver();
|
|
etat('Projet enregistre en ligne');
|
|
}).catch(function (erreur) { alert('Enregistrement impossible : ' + erreur.message); });
|
|
});
|
|
|
|
brancher('btn-serveur-ouvrir', function () {
|
|
appelJSON('/api/projets').then(function (projets) {
|
|
ouvrirModale('Mes projets', function (corps) {
|
|
if (!projets.length) {
|
|
corps.appendChild(elt('p', { className: 'lede', textContent: 'Aucun projet enregistre pour le moment.' }));
|
|
return;
|
|
}
|
|
var liste = elt('div', { className: 'packs' });
|
|
projets.forEach(function (entree) {
|
|
var ligne = elt('div', { className: 'pack-ligne' });
|
|
var texte = elt('div');
|
|
texte.appendChild(elt('b', { textContent: entree.nom }));
|
|
texte.appendChild(elt('div', {
|
|
className: 'desc',
|
|
textContent: 'modifie le ' + (entree.maj_le || '').slice(0, 16).replace('T', ' a ')
|
|
}));
|
|
ligne.appendChild(texte);
|
|
var actions = elt('div', { className: 'barre' });
|
|
actions.appendChild(elt('button', {
|
|
type: 'button', className: 'small', textContent: 'Ouvrir',
|
|
onclick: function () {
|
|
appelJSON('/api/projets/' + entree.id).then(function (donnees) {
|
|
projet = donnees.project;
|
|
projet.__id_serveur = donnees.id;
|
|
selection = { type: 'reglages', index: 0 };
|
|
fermerModale(); sauver(); rendre();
|
|
etat('Projet ouvert');
|
|
});
|
|
}
|
|
}));
|
|
actions.appendChild(elt('button', {
|
|
type: 'button', className: 'small danger', textContent: 'Supprimer',
|
|
onclick: function () {
|
|
if (!confirm('Supprimer ' + entree.nom + ' ?')) return;
|
|
appelJSON('/api/projets/' + entree.id, { method: 'DELETE' }).then(function () {
|
|
ligne.remove(); etat('Projet supprime');
|
|
});
|
|
}
|
|
}));
|
|
ligne.appendChild(actions);
|
|
liste.appendChild(ligne);
|
|
});
|
|
corps.appendChild(liste);
|
|
});
|
|
}).catch(function (erreur) { alert('Lecture impossible : ' + erreur.message); });
|
|
});
|
|
|
|
brancher('btn-deconnexion', function () {
|
|
fetch('/deconnexion', { method: 'POST' }).then(function () { window.location.reload(); });
|
|
});
|
|
|
|
// ------------------------------------------------------ soumission de pack
|
|
|
|
brancher('btn-soumettre', function () {
|
|
ouvrirModale('Proposer un pack au catalogue', function (corps) {
|
|
corps.appendChild(elt('p', {
|
|
className: 'lede',
|
|
textContent: "Collez le YAML de votre pack. Il sera verifie ici, puis relu dans une " +
|
|
'demande de fusion avant d\u2019entrer au catalogue. Rien n\u2019est execute par le site.'
|
|
}));
|
|
|
|
var zone = elt('textarea', { rows: 16, placeholder: 'id: app-rabbitmq\nname: RabbitMQ\ncategory: application\n...' });
|
|
corps.appendChild(zone);
|
|
|
|
var mot = elt('input', { type: 'text', placeholder: 'Un mot pour le relecteur (facultatif)' });
|
|
corps.appendChild(mot);
|
|
|
|
var retour = elt('div');
|
|
corps.appendChild(retour);
|
|
|
|
var barre = elt('div', { className: 'barre' });
|
|
var envoi = elt('button', { type: 'button', className: 'primary auto', textContent: 'Soumettre', disabled: true });
|
|
|
|
barre.appendChild(elt('button', {
|
|
type: 'button', textContent: 'Verifier',
|
|
onclick: function () {
|
|
appelJSON('/api/soumissions/verifier', {
|
|
method: 'POST', body: JSON.stringify({ yaml: zone.value })
|
|
}).then(function (donnees) {
|
|
retour.innerHTML = '';
|
|
if (donnees.valide) {
|
|
var note = elt('div', { className: 'notice' });
|
|
note.appendChild(elt('b', { textContent: 'Pack valide' }));
|
|
note.appendChild(elt('p', {
|
|
textContent: donnees.pack.nom + ' — ' + donnees.pack.sondes + ' sonde(s), modele ' + donnees.pack.modele_hote
|
|
}));
|
|
retour.appendChild(note);
|
|
envoi.disabled = false;
|
|
} else {
|
|
var mauvais = elt('div', { className: 'notice bad' });
|
|
mauvais.appendChild(elt('b', { textContent: 'A corriger' }));
|
|
var ul = elt('ul');
|
|
donnees.problemes.forEach(function (p) { ul.appendChild(elt('li', { textContent: p })); });
|
|
mauvais.appendChild(ul);
|
|
retour.appendChild(mauvais);
|
|
envoi.disabled = true;
|
|
}
|
|
}).catch(function (erreur) { alert(erreur.message); });
|
|
}
|
|
}));
|
|
|
|
envoi.addEventListener('click', function () {
|
|
appelJSON('/api/soumissions', {
|
|
method: 'POST', body: JSON.stringify({ yaml: zone.value, message: mot.value })
|
|
}).then(function () {
|
|
fermerModale();
|
|
etat('Pack soumis, en attente de relecture');
|
|
}).catch(function (erreur) { alert('Soumission refusee : ' + erreur.message); });
|
|
});
|
|
|
|
barre.appendChild(envoi);
|
|
corps.appendChild(barre);
|
|
});
|
|
});
|