Refactored Alert schedule (#9514)

* add AlertSchedule model and relationships
change table structure to match the expected layout

* Update maint schedule map ui

* better index name

* Laravel queries
fix some issues with the ui: restricting start incorrectly and loading empty days error

* handle date limiting properly

* Another attempt add schedule constraints

* use Auth

* Update WorldMap widget to use check isUnderMaintenance

* Rename 275.sql to 276.sql

* Rename 276.sql to 277.sql
This commit is contained in:
Tony Murray
2019-01-03 22:42:12 +00:00
committed by Neil Lathwood
parent 0cc599c576
commit f10cbddacc
10 changed files with 249 additions and 141 deletions
@@ -62,6 +62,7 @@ class WorldMapController extends WidgetController
->whereIn('status', $status)
->get()
->filter(function ($device) use ($status) {
/** @var Device $device */
if (!($device->location_id && $device->location->coordinatesValid())) {
return false;
}
@@ -74,8 +75,7 @@ class WorldMapController extends WidgetController
$device->markerIcon = 'redMarker';
$device->zOffset = 10000;
// TODO if ($device->isUnderMaintenance())
if (false) {
if ($device->isUnderMaintenance()) {
if ($status == 0) {
return false;
}
+86
View File
@@ -0,0 +1,86 @@
<?php
/**
* AlertSchedule.php
*
* -Description-
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @package LibreNMS
* @link http://librenms.org
* @copyright 2018 Tony Murray
* @author Tony Murray <[email protected]>
*/
namespace App\Models;
use DB;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
class AlertSchedule extends Model
{
public $timestamps = false;
protected $table = 'alert_schedule';
protected $primaryKey = 'schedule_id';
// ---- Query scopes ----
public function scopeIsActive($query)
{
// TODO use Carbon?
return $query->where(function ($query) {
$query->where(function ($query) {
// Non recurring simply between start and end
$query->where('recurring', 0)
->where('start', '<=', DB::raw('NOW()'))
->where('end', '>=', DB::raw('NOW()'));
})->orWhere(function ($query) {
$query->where('recurring', 1)
// Check the time is after the start date and before the end date, or end date is not set
->where(function ($query) {
$query->where('start_recurring_dt', '<=', DB::raw("date_format(NOW(), '%Y-%m-%d')"))
->where(function ($query) {
$query->where('end_recurring_dt', '>=', DB::raw("date_format(NOW(), '%Y-%m-%d')"))
->orWhereNull('end_recurring_dt')
->orWhere('end_recurring_dt', '0000-00-00')
->orWhere('end_recurring_dt', '');
});
})
// Check the time is between the start and end hour/minutes/seconds
->where('start_recurring_hr', '<=', DB::raw("date_format(NOW(), '%H:%i:%s')"))
->where('end_recurring_hr', '>=', DB::raw("date_format(NOW(), '%H:%i:%s')"))
// Check we are on the correct day of the week
->where(function ($query) {
/** @var Builder $query */
$query->where('recurring_day', 'like', DB::raw("CONCAT('%', date_format(NOW(), '%w'), '%')"))
->orWhereNull('recurring_day')
->orWhere('recurring_day', '');
});
});
});
}
// ---- Define Relationships ----
public function devices()
{
return $this->morphedByMany('App\Models\Device', 'alert_schedulable', 'alert_schedulables', 'schedule_id', 'schedule_id');
}
public function deviceGroups()
{
return $this->morphedByMany('App\Models\DeviceGroup', 'alert_schedulable');
}
}
+26
View File
@@ -151,6 +151,27 @@ class Device extends BaseModel
return $this->hostname;
}
public function isUnderMaintenance()
{
$query = AlertSchedule::isActive()
->join('alert_schedulables', 'alert_schedule.schedule_id', 'alert_schedulables.schedule_id')
->where(function ($query) {
$query->where(function ($query) {
$query->where('alert_schedulable_type', 'device')
->where('alert_schedulable_id', $this->device_id);
});
if ($this->groups) {
$query->orWhere(function ($query) {
$query->where('alert_schedulable_type', 'device_group')
->whereIn('alert_schedulable_id', $this->groups->pluck('id'));
});
}
});
return $query->exists();
}
/**
* Get the shortened display name of this device.
* Length is always overridden by shorthost_target_length.
@@ -434,6 +455,11 @@ class Device extends BaseModel
return $this->hasMany('App\Models\Alert', 'device_id');
}
public function alertSchedules()
{
return $this->morphToMany('App\Models\AlertSchedule', 'alert_schedulable', 'alert_schedulables', 'schedule_id', 'schedule_id');
}
public function applications()
{
return $this->hasMany('App\Models\Application', 'device_id');
+6
View File
@@ -302,6 +302,12 @@ class DeviceGroup extends BaseModel
// ---- Define Relationships ----
public function alertSchedules()
{
return $this->morphToMany('App\Models\AlertSchedule', 'alert_schedulable', 'alert_schedulables', 'schedule_id', 'schedule_id');
}
public function rules()
{
return $this->belongsToMany('App\Models\AlertRule', 'alert_group_map', 'group_id', 'rule_id');
+2
View File
@@ -80,6 +80,8 @@ class AppServiceProvider extends ServiceProvider
Relation::morphMap([
'interface' => \App\Models\Port::class,
'sensor' => \App\Models\Sensor::class,
'device' => \App\Models\Device::class,
'device_group' => \App\Models\DeviceGroup::class,
]);
}
@@ -123,16 +123,21 @@ if ($sub_type == 'new-maintenance') {
$fail = 0;
if ($update == 1) {
dbDelete('alert_schedule_items', '`schedule_id`=?', array($schedule_id));
dbDelete('alert_schedulables', '`schedule_id`=?', array($schedule_id));
}
foreach ($_POST['maps'] as $target) {
$target = target_to_id($target);
$item = dbInsert(array('schedule_id' => $schedule_id, 'target' => $target), 'alert_schedule_items');
if ($notes && get_user_pref('add_schedule_note_to_device', false)) {
$device_notes = dbFetchCell('SELECT `notes` FROM `devices` WHERE `device_id` = ?;', array($target));
$type = 'device';
if (starts_with($target, 'g')) {
$type = 'device_group';
$target = substr($target, 1);
}
$item = dbInsert(['schedule_id' => $schedule_id, 'alert_schedulable_type' => $type, 'alert_schedulable_id' => $target], 'alert_schedulables');
if ($notes && $type = 'device' && get_user_pref('add_schedule_note_to_device', false)) {
$device_notes = dbFetchCell('SELECT `notes` FROM `devices` WHERE `device_id` = ?;', [$target]);
$device_notes.= ((empty($device_notes)) ? '' : PHP_EOL) . date("Y-m-d H:i") . ' Alerts delayed: ' . $notes;
dbUpdate(array('notes' => $device_notes), 'devices', '`device_id` = ?', array($target));
dbUpdate(['notes' => $device_notes], 'devices', '`device_id` = ?', [$target]);
}
if ($item > 0) {
array_push($items, $item);
@@ -143,7 +148,7 @@ if ($sub_type == 'new-maintenance') {
if ($fail == 1 && $update == 0) {
foreach ($items as $item) {
dbDelete('alert_schedule_items', '`item_id`=?', array($item));
dbDelete('alert_schedulables', '`item_id`=?', array($item));
}
dbDelete('alert_schedule', '`schedule_id`=?', array($schedule_id));
@@ -164,10 +169,19 @@ if ($sub_type == 'new-maintenance') {
} elseif ($sub_type == 'parse-maintenance') {
$schedule_id = mres($_POST['schedule_id']);
$schedule = dbFetchRow('SELECT * FROM `alert_schedule` WHERE `schedule_id`=?', array($schedule_id));
$items = array();
foreach (dbFetchRows('SELECT `target` FROM `alert_schedule_items` WHERE `schedule_id`=?', array($schedule_id)) as $targets) {
$targets = id_to_target($targets['target']);
array_push($items, $targets);
$items = [];
foreach (dbFetchRows('SELECT `alert_schedulable_type`, `alert_schedulable_id` FROM `alert_schedulables` WHERE `schedule_id`=?', [$schedule_id]) as $target) {
$id = $target['alert_schedulable_id'];
if ($target['alert_schedulable_type'] == 'device_group') {
$text = dbFetchCell('SELECT name FROM device_groups WHERE id = ?', [$id]);
$id = 'g' . $id;
} else {
$text = dbFetchCell('SELECT hostname FROM devices WHERE device_id = ?', [$id]);
}
$items[] = [
'id' => $id,
'text' => $text,
];
}
$response = array(
+81 -109
View File
@@ -12,9 +12,7 @@
* the source code distribution for details.
*/
use LibreNMS\Authentication\LegacyAuth;
if (LegacyAuth::user()->hasGlobalAdmin()) {
if (\Auth::user()->hasGlobalAdmin()) {
?>
<div class="modal fade bs-example-modal-sm" id="schedule-maintenance" tabindex="-1" role="dialog" aria-labelledby="Create" aria-hidden="true">
@@ -61,13 +59,13 @@ if (LegacyAuth::user()->hasGlobalAdmin()) {
<div class="form-group">
<label for="start" class="col-sm-4 control-label">Start <exp>*</exp>: </label>
<div class="col-sm-8">
<input type="text" class="form-control date" id="start" name="start" value="<?php echo date($config['dateformat']['byminute']); ?>" data-date-format="YYYY-MM-DD HH:mm">
<input type="text" class="form-control date" id="start" name="start" value="" data-date-format="YYYY-MM-DD HH:mm">
</div>
</div>
<div class="form-group">
<label for="end" class="col-sm-4 control-label">End <exp>*</exp>: </label>
<div class="col-sm-8">
<input type="text" class="form-control date" id="end" name="end" value="<?php echo date($config['dateformat']['byminute'], strtotime('+1 hour')); ?>" data-date-format="YYYY-MM-DD HH:mm">
<input type="text" class="form-control date" id="end" name="end" value="" data-date-format="YYYY-MM-DD HH:mm">
</div>
</div>
</div>
@@ -75,25 +73,25 @@ if (LegacyAuth::user()->hasGlobalAdmin()) {
<div class="form-group">
<label for="start_recurring_dt" class="col-sm-4 control-label">Start date <exp>*</exp>: </label>
<div class="col-sm-8">
<input type="text" class="form-control date" id="start_recurring_dt" name="start_recurring_dt" value="<?php echo date($config['dateformat']['start_recurring_dt']); ?>" data-date-format="YYYY-MM-DD">
<input type="text" class="form-control date" id="start_recurring_dt" name="start_recurring_dt" value="" data-date-format="YYYY-MM-DD">
</div>
</div>
<div class="form-group">
<label for="end_recurring_dt" class="col-sm-4 control-label">End date: </label>
<div class="col-sm-8">
<input type="text" class="form-control date" id="end_recurring_dt" name="end_recurring_dt" value="<?php echo date($config['dateformat']['end_recurring_dt'], strtotime('+1 hour')); ?>" data-date-format="YYYY-MM-DD">
<input type="text" class="form-control date" id="end_recurring_dt" name="end_recurring_dt" value="" data-date-format="YYYY-MM-DD">
</div>
</div>
<div class="form-group">
<label for="start_recurring_hr" class="col-sm-4 control-label">Start hour <exp>*</exp>: </label>
<div class="col-sm-8">
<input type="text" class="form-control date" id="start_recurring_hr" name="start_recurring_hr" value="<?php echo date($config['dateformat']['start_recurring_hr']); ?>" data-date-format="HH:mm">
<input type="text" class="form-control date" id="start_recurring_hr" name="start_recurring_hr" value="" data-date-format="HH:mm">
</div>
</div>
<div class="form-group">
<label for="end_recurring_hr" class="col-sm-4 control-label">End hour <exp>*</exp>: </label>
<div class="col-sm-8">
<input type="text" class="form-control date" id="end_recurring_hr" name="end_recurring_hr" value="<?php echo date($config['dateformat']['end_recurring_hr'], strtotime('+1 hour')); ?>" data-date-format="HH:mm">
<input type="text" class="form-control date" id="end_recurring_hr" name="end_recurring_hr" value="" data-date-format="HH:mm">
</div>
</div>
<div class="form-group">
@@ -110,17 +108,9 @@ if (LegacyAuth::user()->hasGlobalAdmin()) {
</div>
</div>
<div class="form-group">
<label for='map-stub' class='col-sm-4 control-label'>Map To <exp>*</exp>: </label>
<div class="col-sm-5">
<input type='text' id='map-stub' name='map-stub' class='form-control'/>
</div>
<div class="col-sm-3">
<button class="btn btn-primary" type="button" name="add-map" id="add-map" value="Add">Add</button>
</div>
</div>
<div class="form-group">
<div class="col-md-12">
<span id="map-tags"></span>
<label for='maps' class='col-sm-4 control-label'>Map To <exp>*</exp>: </label>
<div class="col-sm-8">
<select id="maps" name="maps[]" class="form-control" multiple="multiple"></select>
</div>
</div>
<div class="form-group">
@@ -135,32 +125,31 @@ if (LegacyAuth::user()->hasGlobalAdmin()) {
</div>
<script>
$('#schedule-maintenance').on('hide.bs.modal', function (event) {
$('#map-tags').data('tagmanager').empty();
$('#maps').val(null).trigger('change');
$('#schedule_id').val('');
$('#title').val('');
$('#notes').val('');
$('#recurring').val('');
$('#start').val('');
$('#end').val('');
$('#start_recurring_dt').val('');
$('#end_recurring_dt').val('');
$('#start_recurring_hr').val('');
$('#end_recurring_hr').val('');
$('#start').val(moment().format('YYYY-MM-DD HH:mm')).data("DateTimePicker").maxDate(false).minDate(moment());
$('#end').val(moment().add(1, 'hour').format('YYYY-MM-DD HH:mm')).data("DateTimePicker").maxDate(false).minDate(moment());
var $startRecurringDt = $('#start_recurring_dt');
$startRecurringDt.val('').data("DateTimePicker").maxDate(false).minDate(moment());
var $endRecurringDt = $('#end_recurring_dt');
$endRecurringDt.data("DateTimePicker").date(moment()).maxDate(false).minDate(moment());
$endRecurringDt.val('');
$startRecurringDt.data("DateTimePicker").maxDate(false);
$('#start_recurring_hr').val('').data("DateTimePicker").minDate(false).maxDate(false);
$('#end_recurring_hr').val('').data("DateTimePicker").minDate(false).maxDate(false);
$("#recurring0").prop("checked", true);
$('#recurring_day').prop('checked', false);
$('#norecurringgroup').show();
$('#recurringgroup').hide();
$('#schedulemodal-alert').remove('');
$('#schedulemodal-alert').remove();
});
$('#schedule-maintenance').on('show.bs.modal', function (event) {
$('#tagmanager').tagmanager();
var schedule_id = $('#schedule_id').val();
$('#map-tags').tagmanager({
strategy: 'array',
tagFieldName: 'maps[]',
initialCap: false
});
if (schedule_id > 0) {
$.ajax({
type: "POST",
@@ -168,16 +157,27 @@ $('#schedule-maintenance').on('show.bs.modal', function (event) {
data: { type: "schedule-maintenance", sub_type: "parse-maintenance", schedule_id: schedule_id },
dataType: "json",
success: function(output) {
var arr = [];
$.each ( output['targets'], function( key, value ) {
arr.push(value);
var maps = $('#maps');
var selected = [];
$.each ( output['targets'], function( key, item ) {
// create options if they don't exist
if (maps.find("option[value='" + item.id + "']").length === 0) {
var newOption = new Option(item.text, item.id, true, true);
maps.append(newOption);
}
selected.push(item.id);
});
$('#map-tags').data('tagmanager').populate(arr);
maps.val(selected).trigger('change');
$('#title').val(output['title']);
$('#notes').val(output['notes']);
if (output['recurring'] == 0){
$('#start').val(output['start']);
$('#end').val(output['end']);
var start = $('#start').data("DateTimePicker");
if (output['start']) {
start.minDate(output['start']);
}
start.date(output['start']);
$('#end').data("DateTimePicker").date(output['end']);
$('#norecurringgroup').show();
$('#recurringgroup').hide();
@@ -189,15 +189,24 @@ $('#schedule-maintenance').on('show.bs.modal', function (event) {
$("#recurring0").prop("checked", true);
$('#recurring_day').prop('checked', false);
}else{
var start_recurring_dt = $('#start_recurring_dt').data("DateTimePicker");
if (output['start_recurring_dt']) {
start_recurring_dt.minDate(output['start_recurring_dt']);
}
start_recurring_dt.date(output['start_recurring_dt']);
$('#end_recurring_dt').data("DateTimePicker").date(output['end_recurring_dt']);
var start_recurring_hr = $('#start_recurring_hr').data("DateTimePicker");
if (output['start_recurring_dt']) {
start_recurring_dt.minDate(output['start_recurring_dt']);
}
start_recurring_hr.date(output['start_recurring_hr']);
$('#end_recurring_hr').data("DateTimePicker").date(output['end_recurring_hr']);
$('#start_recurring_dt').val(output['start_recurring_dt']);
$('#end_recurring_dt').val(output['end_recurring_dt']);
$('#start_recurring_hr').val(output['start_recurring_hr']);
$('#end_recurring_hr').val(output['end_recurring_hr']);
$("#recurring1").prop("checked", true);
var recdayupd = output['recurring_day'];
if (recdayupd != ''){
if (recdayupd){
var arrayrecdayupd = recdayupd.split(',');
$.each(arrayrecdayupd, function(indexcheckedday, checkedday){
$("input[name='recurring_day[]'][value="+checkedday+"]").prop('checked', true);
@@ -254,73 +263,25 @@ $('#sched-submit').click('', function(e) {
});
});
$('#add-map').click('',function (event) {
$('#map-tags').data('tagmanager').populate([ $('#map-stub').val() ]);
$('#map-stub').val('');
});
var map_devices = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url: "ajax_search.php?search=%QUERY&type=device&map=1",
filter: function (output) {
return $.map(output, function (item) {
return {
name: item.name,
};
});
},
wildcard: "%QUERY"
}
});
var map_groups = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url: "ajax_search.php?search=%QUERY&type=group&map=1",
filter: function (output) {
return $.map(output, function (item) {
return {
name: item.name,
};
});
},
wildcard: "%QUERY"
}
});
map_devices.initialize();
map_groups.initialize();
$('#map-stub').typeahead({
hint: true,
highlight: true,
minLength: 1,
classNames: {
menu: 'typeahead-left'
}
},
{
source: map_devices.ttAdapter(),
async: true,
displayKey: 'name',
valueKey: name,
templates: {
suggestion: Handlebars.compile('<p>&nbsp;{{name}}</p>')
}
},
{
source: map_groups.ttAdapter(),
async: true,
displayKey: 'name',
valueKey: name,
templates: {
suggestion: Handlebars.compile('<p>&nbsp;{{name}}</p>')
$("#maps").select2({
width: '100%',
placeholder: "Devices or Groups",
ajax: {
url: 'ajax_list.php',
delay: 250,
data: function (params) {
return {
type: 'devices_groups',
search: params.term
};
}
}
});
$(function () {
$("#start").datetimepicker({
minDate: '<?php echo date($config['dateformat']['byminute']); ?>',
defaultDate: moment(),
minDate: moment().format('YYYY-MM-DD'),
icons: {
time: 'fa fa-clock-o',
date: 'fa fa-calendar',
@@ -334,6 +295,8 @@ $(function () {
}
});
$("#end").datetimepicker({
defaultDate: moment().add(1, 'hour'),
minDate: moment().format('YYYY-MM-DD'),
icons: {
time: 'fa fa-clock-o',
date: 'fa fa-calendar',
@@ -353,7 +316,8 @@ $(function () {
$("#start").data("DateTimePicker").maxDate(e.date);
});
$("#start_recurring_dt").datetimepicker({
minDate: '<?php echo date($config['dateformat']['byminute']); ?>',
defaultDate: moment(),
minDate: moment().format('YYYY-MM-DD'),
icons: {
time: 'fa fa-clock-o',
date: 'fa fa-calendar',
@@ -367,6 +331,7 @@ $(function () {
}
});
$("#end_recurring_dt").datetimepicker({
minDate: moment().format('YYYY-MM-DD'),
icons: {
time: 'fa fa-clock-o',
date: 'fa fa-calendar',
@@ -380,7 +345,14 @@ $(function () {
}
});
$("#start_recurring_dt").on("dp.change", function (e) {
$("#end_recurring_dt").data("DateTimePicker").minDate(e.date);
var $endRecurringDt = $("#end_recurring_dt");
var val = $endRecurringDt.val();
$endRecurringDt.data("DateTimePicker").minDate(e.date);
// work around annoying event interaction
if (!val) {
$endRecurringDt.val('');
$("#start_recurring_dt").data("DateTimePicker").maxDate(false);
}
});
$("#end_recurring_dt").on("dp.change", function (e) {
$("#start_recurring_dt").data("DateTimePicker").maxDate(e.date);
+4 -11
View File
@@ -150,19 +150,12 @@ function GetRules($device_id)
/**
* Check if device is under maintenance
* @param int $device Device-ID
* @return int
* @param int $device_id Device-ID
* @return bool
*/
function IsMaintenance($device)
function IsMaintenance($device_id)
{
$groups = GetGroupsFromDevice($device);
$params = array($device);
$where = "";
foreach ($groups as $group) {
$where .= " || alert_schedule_items.target = ?";
$params[] = 'g'.$group;
}
return dbFetchCell('SELECT alert_schedule.schedule_id FROM alert_schedule LEFT JOIN alert_schedule_items ON alert_schedule.schedule_id=alert_schedule_items.schedule_id WHERE ( alert_schedule_items.target = ?'.$where.' ) && ((alert_schedule.recurring = 0 AND (NOW() BETWEEN alert_schedule.start AND alert_schedule.end)) OR (alert_schedule.recurring = 1 AND (alert_schedule.start_recurring_dt <= date_format(NOW(), \'%Y-%m-%d\') AND (end_recurring_dt >= date_format(NOW(), \'%Y-%m-%d\') OR end_recurring_dt is NULL OR end_recurring_dt = \'0000-00-00\' OR end_recurring_dt = \'\')) AND (date_format(now(), \'%H:%i:%s\') BETWEEN `start_recurring_hr` AND end_recurring_hr) AND (recurring_day LIKE CONCAT(\'%\',date_format(now(), \'%w\'),\'%\') OR recurring_day is null or recurring_day = \'\'))) LIMIT 1', $params);
return \App\Models\Device::find($device_id)->isUnderMaintenance();
}
/**
* Run all rules for a device
+10 -8
View File
@@ -78,6 +78,16 @@ alert_rules:
Indexes:
PRIMARY: { Name: PRIMARY, Columns: [id], Unique: true, Type: BTREE }
name: { Name: name, Columns: [name], Unique: true, Type: BTREE }
alert_schedulables:
Columns:
- { Field: item_id, Type: int(11), 'Null': false, Extra: auto_increment }
- { Field: schedule_id, Type: int(11), 'Null': false, Extra: '' }
- { Field: alert_schedulable_id, Type: 'int(11) unsigned', 'Null': false, Extra: '' }
- { Field: alert_schedulable_type, Type: varchar(255), 'Null': false, Extra: '' }
Indexes:
PRIMARY: { Name: PRIMARY, Columns: [item_id], Unique: true, Type: BTREE }
schedule_id: { Name: schedule_id, Columns: [schedule_id], Unique: false, Type: BTREE }
schedulable_morph_index: { Name: schedulable_morph_index, Columns: [alert_schedulable_type, alert_schedulable_id], Unique: false, Type: BTREE }
alert_schedule:
Columns:
- { Field: schedule_id, Type: int(11), 'Null': false, Extra: auto_increment }
@@ -93,14 +103,6 @@ alert_schedule:
- { Field: notes, Type: text, 'Null': false, Extra: '' }
Indexes:
PRIMARY: { Name: PRIMARY, Columns: [schedule_id], Unique: true, Type: BTREE }
alert_schedule_items:
Columns:
- { Field: item_id, Type: int(11), 'Null': false, Extra: auto_increment }
- { Field: schedule_id, Type: int(11), 'Null': false, Extra: '' }
- { Field: target, Type: varchar(255), 'Null': false, Extra: '' }
Indexes:
PRIMARY: { Name: PRIMARY, Columns: [item_id], Unique: true, Type: BTREE }
schedule_id: { Name: schedule_id, Columns: [schedule_id], Unique: false, Type: BTREE }
alert_templates:
Columns:
- { Field: id, Type: int(11), 'Null': false, Extra: auto_increment }
+7
View File
@@ -0,0 +1,7 @@
ALTER TABLE alert_schedule_items ADD alert_schedulable_type varchar(255) NOT NULL;
UPDATE alert_schedule_items SET alert_schedulable_type = 'device_group' WHERE target LIKE 'g%';
UPDATE alert_schedule_items SET alert_schedulable_type = 'device' WHERE alert_schedulable_type='';
UPDATE alert_schedule_items SET target = SUBSTRING(target, 2) WHERE target LIKE 'g%';
ALTER TABLE alert_schedule_items CHANGE target alert_schedulable_id int(11) unsigned NOT NULL;
ALTER TABLE alert_schedule_items RENAME TO alert_schedulables;
CREATE INDEX `schedulable_morph_index` ON alert_schedulables (alert_schedulable_type, alert_schedulable_id);