mirror of
https://github.com/ntop/ntopng.git
synced 2026-05-24 21:56:15 +00:00
473 lines
17 KiB
JavaScript
473 lines
17 KiB
JavaScript
$(document).ready(function () {
|
|
|
|
class TimeserieSourceBuilder {
|
|
|
|
constructor() {
|
|
this.counter = 0;
|
|
this.currentSources = [];
|
|
this.MIN_SOURCE_COUNTER = 1;
|
|
this.MAX_SOURCE_COUNTER = 4;
|
|
}
|
|
|
|
canCreateSource() {
|
|
return (this.currentSources.length <= this.MAX_SOURCE_COUNTER);
|
|
}
|
|
|
|
canRemoveSource() {
|
|
return (this.currentSources.length > this.MIN_SOURCE_COUNTER);
|
|
}
|
|
|
|
buildNewSource(accordionName, name = `source-${this.counter++}`) {
|
|
|
|
const timeserieSourceTemplate = $(`template#ds-source`).html();
|
|
const tagTemplate = $(`template#ds-source-tag`).html();
|
|
const timeseriesSourceInstance = new TimeserieSource({
|
|
name: name,
|
|
tagTemplate: tagTemplate,
|
|
timeseriesFamilies: timeseriesFamilies,
|
|
builder: this,
|
|
accordionName: accordionName,
|
|
$domElement: $(timeserieSourceTemplate),
|
|
$tagElement: $(tagTemplate),
|
|
});
|
|
|
|
this.currentSources.push(timeseriesSourceInstance);
|
|
|
|
return timeseriesSourceInstance;
|
|
}
|
|
|
|
emptyCurrentSources() {
|
|
this.currentSources = [];
|
|
}
|
|
}
|
|
|
|
class TimeserieSource {
|
|
|
|
constructor(args) {
|
|
|
|
const { $domElement, $tagElement, name, timeseriesFamilies, builder, accordionName } = args;
|
|
this.$domElement = $domElement;
|
|
this.$cardTitle = $domElement.find(`a[data-toggle='collapse']`);
|
|
this.$btnRemoveSource = $domElement.find(`.btn-remove-source`);
|
|
this.$familiesSelect = $domElement.find(`select.family`);
|
|
this.$schemasSelect = $domElement.find(`select.schema`);
|
|
this.$metricsSelect = $domElement.find(`select.metric`);
|
|
this.$tagsContainer = $domElement.find('.tags-container');
|
|
this.$tagElement = $tagElement;
|
|
|
|
this.parentName = accordionName;
|
|
this.steps = [$domElement.find('.step-1'), $domElement.find('.step-2')];
|
|
this.stepsCompleted = [false, false];
|
|
this.timeseriesFamilies = timeseriesFamilies;
|
|
this.builder = builder;
|
|
|
|
this.setSourceId(name);
|
|
this.setAccordionParent(this.parentName);
|
|
this.bindEventListeners();
|
|
}
|
|
|
|
get getDomElement() {
|
|
return this.$domElement;
|
|
}
|
|
|
|
get identifierSource() {
|
|
return `${this.$familiesSelect.val()}-${this.$schemasSelect.val()}-${this.$metricsSelect.val()}`;
|
|
}
|
|
|
|
setAccordionParent(parentName) {
|
|
this.$domElement.find('.collapse').attr('data-parent', parentName);
|
|
}
|
|
|
|
bindEventListeners() {
|
|
const self = this;
|
|
this.$familiesSelect.change(function (e) {
|
|
self.onFamilySelect(e);
|
|
});
|
|
this.$schemasSelect.change(function (e) {
|
|
self.onSchemaSelect(e);
|
|
});
|
|
this.$metricsSelect.change(function (e) {
|
|
self.onMetricSelect(e);
|
|
});
|
|
this.$btnRemoveSource.click(function (e) {
|
|
self.onRemoveSourceClick(e);
|
|
});
|
|
}
|
|
|
|
setSourceId(name) {
|
|
this.$cardTitle.attr('href', `#source-${name}`);
|
|
this.$domElement.find(`div.collapse.show`).attr('id', `source-${name}`);
|
|
this.setCardTitle(name);
|
|
}
|
|
|
|
setCardTitle(title) {
|
|
this.$cardTitle.html(`<b>${title}</b>`);
|
|
}
|
|
|
|
generateTag(tagName, tagValue) {
|
|
const $tagElement = this.$tagElement.clone();
|
|
$tagElement.find('span').html(`<b>${tagName}</b>`);
|
|
$tagElement.find('input').val(`${tagValue}`)
|
|
.attr('pattern', `(\\\$${tagName}|[0-9]+)`)
|
|
.attr('minlength', 1)
|
|
.attr(`data-name`, tagName);
|
|
this.$tagsContainer.append($tagElement);
|
|
return $tagElement;
|
|
}
|
|
|
|
generateTags(tags) {
|
|
|
|
this.$tagsContainer.empty();
|
|
|
|
for (const tag of tags) {
|
|
this.$tagsContainer.append(this.generateTag(tag, `$${tag}`));
|
|
}
|
|
}
|
|
|
|
generateFilledTags(tags) {
|
|
this.$tagsContainer.empty();
|
|
for (const [tagName, tagValue] of Object.entries(tags)) {
|
|
this.$tagsContainer.append(this.generateTag(tagName, tagValue));
|
|
}
|
|
}
|
|
|
|
generateSelectOptions($select, values, generateEmpty = true, titleDummy = "Select ...") {
|
|
|
|
const generateOption = (val, label, disabled = false) => {
|
|
const $option = $(`<option value ${disabled ? 'disabled hidden selected' : ''}>${label}</option>`);
|
|
if (val) {
|
|
$option.attr('value', val);
|
|
}
|
|
$select.append($option);
|
|
};
|
|
|
|
/* clean all the options */
|
|
$select.find('option').remove();
|
|
|
|
if (generateEmpty) generateOption(undefined, '', true);
|
|
|
|
// generate dummy option
|
|
generateOption(null, titleDummy, true);
|
|
|
|
if (values.length > 0) {
|
|
for (const val of values) generateOption(val, val);
|
|
return;
|
|
}
|
|
|
|
for (const [key, _] of Object.entries(values).sort((a, b) => a[0].localeCompare(b[0])))
|
|
generateOption(key, key);
|
|
|
|
}
|
|
|
|
onFamilySelect(event) {
|
|
/* render only schema which belongs to the selected family */
|
|
const schemas = this.timeseriesFamilies[this.$familiesSelect.val()];
|
|
this.generateSelectOptions(this.$schemasSelect, schemas, "Select a schema...");
|
|
/* show step one inside the card */
|
|
if (!this.stepsCompleted[0]) {
|
|
this.steps[0].fadeIn();
|
|
this.stepsCompleted[0] = true;
|
|
}
|
|
if (this.stepsCompleted[1]) {
|
|
this.steps[1].fadeOut();
|
|
this.$schemasSelect.prop('selectedIndex', -1).removeClass('is-valid');
|
|
this.stepsCompleted[0] = false;
|
|
}
|
|
}
|
|
|
|
onSchemaSelect(event) {
|
|
/* render only schema which belongs to the selected family */
|
|
const schema = this.timeseriesFamilies[this.$familiesSelect.val()][this.$schemasSelect.val()];
|
|
const { metrics, tags } = schema;
|
|
this.generateSelectOptions(this.$metricsSelect, metrics, false, "Select a metric...");
|
|
if (!this.preCreated) this.generateTags(tags);
|
|
|
|
this.steps[1].fadeIn();
|
|
this.stepsCompleted[1] = true;
|
|
|
|
}
|
|
|
|
onMetricSelect(event) {
|
|
|
|
// check if exists a source with the same id
|
|
const self = this;
|
|
const exists = this.builder.currentSources.some((source) => {
|
|
if (source == self) return false;
|
|
return (source.identifierSource == self.identifierSource);
|
|
});
|
|
|
|
if (exists) {
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
this.$metricsSelect
|
|
.removeClass('is-valid').addClass('is-invalid');
|
|
|
|
this.$metricsSelect.parent().append("<span class='invalid-feedback'>${i18n.source_exists}</span>");
|
|
this.$metricsSelect.parents('form').find(`button[type='submit']`).attr("disabled", "disabled");
|
|
return;
|
|
}
|
|
else {
|
|
this.$metricsSelect.parent().find(`span.invalid-feedback`).remove();
|
|
this.$metricsSelect.parents('form').find(`button[type='submit']`).removeAttr("disabled");
|
|
}
|
|
|
|
this.setCardTitle(`${this.$schemasSelect.val()} / ${this.$metricsSelect.val()}`);
|
|
}
|
|
|
|
onRemoveSourceClick(event) {
|
|
|
|
if (!this.builder.canRemoveSource()) return;
|
|
event.preventDefault();
|
|
this.$domElement.fadeOut(200, function () { $(this).remove(); });
|
|
this.removeSourceFromBuilder();
|
|
}
|
|
|
|
removeSourceFromBuilder() {
|
|
const index = this.builder.currentSources.indexOf(this);
|
|
if (index > -1) {
|
|
this.builder.currentSources.splice(index, 1);
|
|
}
|
|
}
|
|
|
|
fillSource(args) {
|
|
|
|
const {metric, schema, tags} = args;
|
|
const family = schema.split(":")[0];
|
|
|
|
this.$familiesSelect.val(family).trigger('change');
|
|
this.$schemasSelect.val(schema).trigger('change');
|
|
this.$metricsSelect.val(metric);
|
|
|
|
this.generateFilledTags(tags);
|
|
}
|
|
|
|
}
|
|
|
|
const timeseriesSourceBuilder = new TimeserieSourceBuilder();
|
|
|
|
let dtConfig = DataTableUtils.getStdDatatableConfig( [
|
|
{
|
|
text: '<i class="fas fa-plus"></i>',
|
|
action: function (e, dt, node, config) {
|
|
$('#add-datasource-modal').modal('show');
|
|
}
|
|
},
|
|
{
|
|
text: '<i class="fas fa-sync"></i>',
|
|
action: function (e, dt, node, config) {
|
|
$datasourcesTable.ajax.reload();
|
|
}
|
|
}
|
|
]);
|
|
dtConfig = DataTableUtils.setAjaxConfig(dtConfig,`${http_prefix}/lua/get_datasources.lua`);
|
|
dtConfig = DataTableUtils.extendConfig(dtConfig, {
|
|
columns: [
|
|
{ data: 'alias' },
|
|
{
|
|
data: 'hash',
|
|
render: (hash) => `<a target=\"_blank\" href=\"/datasources/${hash}\"'>${hash}</a>`
|
|
},
|
|
{ data: 'scope' },
|
|
{ data: 'origin', render: (origin, type) => {
|
|
if (type === "display") return `<code>${origin}</code>`;
|
|
return origin;
|
|
}},
|
|
{ data: 'data_retention', widht: '5%', render: (retention, type) => {
|
|
if (type === "display") return NtopUtils.fint(retention);
|
|
return retention;
|
|
} },
|
|
{
|
|
targets: -1,
|
|
className: 'text-center',
|
|
data: null,
|
|
width: '10%',
|
|
render: function (data) {
|
|
|
|
const isDeleteDisabled = data.in_use;
|
|
return DataTableUtils.createActionButtons([
|
|
{ class: 'btn-info', icon: 'fa-edit', modal: '#edit-datasource-modal' },
|
|
{
|
|
class: `btn-danger ${isDeleteDisabled ? 'disabled' : ''}`,
|
|
icon: 'fa-trash',
|
|
modal: '#remove-datasource-modal'
|
|
},
|
|
]);
|
|
}
|
|
},
|
|
],
|
|
});
|
|
|
|
const $datasourcesTable = $(`#datasources-list`).DataTable(dtConfig);
|
|
|
|
const prepareFormData = (form) => {
|
|
|
|
const $form = $(form);
|
|
const serialized = NtopUtils.serializeFormArray($form.serializeArray());
|
|
serialized.schemas = {};
|
|
|
|
$form.find('fieldset').each(function(i, fieldset) {
|
|
|
|
const schemaKey = $(this).find(`[name='schema[]']`).val();
|
|
const metric = $(this).find(`[name='metric[]']`).val();
|
|
const tags = {};
|
|
|
|
$(this).find(`.tag`).each(function(i, tag) {
|
|
tags[$(this).attr('data-name')] = $(this).val();
|
|
});
|
|
|
|
serialized.schemas[schemaKey] = { metric: metric, tags: tags };
|
|
});
|
|
|
|
return serialized;
|
|
}
|
|
|
|
const $editDatasourceHandler = $('#edit-datasource-modal form').modalHandler({
|
|
method: 'post',
|
|
endpoint: `${http_prefix}/lua/edit_datasources.lua`,
|
|
csrf: ds_csrf,
|
|
resetAfterSubmit: false,
|
|
beforeSumbit: function (selectedDatasource) {
|
|
|
|
const ds = prepareFormData(`#edit-datasource-modal form`);
|
|
ds.hash = selectedDatasource.hash;
|
|
|
|
return { action: 'edit', JSON: JSON.stringify(ds) };
|
|
},
|
|
onModalInit: function(selectedDatasource) {
|
|
$(`.datasource-name`).text(selectedDatasource.alias);
|
|
|
|
/* fill default datasource values */
|
|
$(`#edit-datasource-modal form`).find('[name]').each(function(e) {
|
|
$(this).val(selectedDatasource[$(this).attr('name')]);
|
|
});
|
|
|
|
const $sourcesContainer = $(`#edit-datasource-modal .ds-source-container`);
|
|
const $btnAddSource = $(`#edit-datasource-modal .btn-add-source`);
|
|
$btnAddSource.hide();
|
|
$sourcesContainer.hide().empty();
|
|
|
|
/* if the origin is of type timeseries then prepare sources */
|
|
if (selectedDatasource.origin != "timeseries.lua") return;
|
|
|
|
const schemas = selectedDatasource.schemas;
|
|
for (const [key, schema] of Object.entries(schemas)) {
|
|
|
|
const source = timeseriesSourceBuilder.buildNewSource(
|
|
`#edit-ds-source-container`,
|
|
`source-${selectedDatasource.hash}-${schema.metric}`
|
|
);
|
|
|
|
source.setCardTitle(`${key} / ${schema.metric}`);
|
|
source.fillSource({ schema: key, metric: schema.metric, tags: schema.tags });
|
|
|
|
$sourcesContainer.append(source.$domElement);
|
|
}
|
|
|
|
$sourcesContainer.show();
|
|
$btnAddSource.show();
|
|
},
|
|
onSubmitSuccess: function(response) {
|
|
if (response.success) {
|
|
$datasourcesTable.ajax.reload();
|
|
timeseriesSourceBuilder.emptyCurrentSources();
|
|
$('#edit-datasource-modal').modal('hide');
|
|
}
|
|
else {
|
|
$(`#edit-datasource-modal .invalid-feedback`).show().text(response.message);
|
|
}
|
|
}
|
|
});
|
|
|
|
/* bind edit datasource event */
|
|
$(`#datasources-list`).on('click', `a[href='#edit-datasource-modal']`, function (e) {
|
|
const selectedDatasource = $datasourcesTable.row($(this).parent().parent()).data();
|
|
$editDatasourceHandler.invokeModalInit(selectedDatasource);
|
|
});
|
|
|
|
/* bind add datasource event */
|
|
$(`#add-datasource-modal form`).modalHandler({
|
|
method: 'post',
|
|
endpoint: `${http_prefix}/lua/edit_datasources.lua`,
|
|
csrf: ds_csrf,
|
|
resetAfterSubmit: false,
|
|
beforeSumbit: function () {
|
|
return {
|
|
action: 'add',
|
|
JSON: JSON.stringify(prepareFormData(`#add-datasource-modal form`))
|
|
};
|
|
},
|
|
onSubmitSuccess: function (response) {
|
|
if (response.success) {
|
|
$('#add-datasource-modal').modal('hide');
|
|
$(`#add-ds-source-container`).fadeOut().empty();
|
|
$(`#btn-add-source`).fadeOut();
|
|
timeseriesSourceBuilder.emptyCurrentSources();
|
|
$datasourcesTable.ajax.reload();
|
|
}
|
|
else {
|
|
$(`#add-datasource-modal .invalid-feedback`).show().text(response.message);
|
|
}
|
|
}
|
|
}).invokeModalInit();
|
|
|
|
const $removeDatasourceHandler = $(`#remove-datasource-modal form`).modalHandler({
|
|
method: 'post',
|
|
endpoint: `${http_prefix}/lua/edit_datasources.lua`,
|
|
dontDisableSubmit: true,
|
|
csrf: ds_csrf,
|
|
beforeSumbit: (datasource) => {
|
|
return { action: 'remove', JSON: JSON.stringify({ ds_key: datasource.hash}) }
|
|
},
|
|
onModalInit: (datasource) => {
|
|
$(`.datasource-name`).text(datasource.alias);
|
|
},
|
|
onSubmitSuccess: (response) => {
|
|
if (response.success) {
|
|
$datasourcesTable.ajax.reload();
|
|
$('#remove-datasource-modal').modal('hide');
|
|
}
|
|
else {
|
|
$(`#remove-datasource-modal .invalid-feedback`).show().text(response.message);
|
|
}
|
|
}
|
|
});
|
|
|
|
/* bind remove datasource event */
|
|
$(`#datasources-list`).on('click', `a[href='#remove-datasource-modal']`, function (e) {
|
|
const selectedDatasource = $datasourcesTable.row($(this).parent().parent()).data();
|
|
$removeDatasourceHandler.invokeModalInit(selectedDatasource);
|
|
});
|
|
|
|
/* **************************************************************************************** */
|
|
|
|
$(`.btn-add-source`).click(function (e) {
|
|
|
|
e.preventDefault(); e.stopPropagation();
|
|
|
|
const $sourcesContainer = $(this).parents('form').find(`.ds-source-container`);
|
|
if (timeseriesSourceBuilder.canCreateSource()) {
|
|
$sourcesContainer.find('.collapse').collapse('hide');
|
|
$sourcesContainer.append(
|
|
timeseriesSourceBuilder.buildNewSource(`#add-ds-source-container`).$domElement
|
|
);
|
|
}
|
|
});
|
|
|
|
$(`#add-datasource-modal select[name='origin'], #edit-datasource-modal select[name='origin']`).change(function (e) {
|
|
|
|
const isTimeseries = $(this).val() == "timeseries.lua";
|
|
const $sourcesContainer = $(this).parents('form').find(`.ds-source-container`);
|
|
const $btnAddSource = $(this).parents().find(`.btn-add-source`);
|
|
if (!isTimeseries) {
|
|
$sourcesContainer.fadeOut().empty();
|
|
$btnAddSource.fadeOut();
|
|
return;
|
|
}
|
|
// be sure to create new elements
|
|
timeseriesSourceBuilder.emptyCurrentSources();
|
|
$sourcesContainer.append(timeseriesSourceBuilder.buildNewSource(`#add-ds-source-container`).$domElement);
|
|
$sourcesContainer.fadeIn();
|
|
$btnAddSource.fadeIn();
|
|
});
|
|
|
|
});
|