Commit d81f0708 authored by Maria Arsuaga Rios's avatar Maria Arsuaga Rios
Browse files

FTS-977: Remove fixed

parent 8300eb00
Pipeline #159816 passed with stage
in 14 seconds
......@@ -38,34 +38,6 @@ class OptimizerEvolution(Base):
filesize_stddeve = Column(Float)
class OptimizerActive(Base):
__tablename__ = 't_optimize_active'
source_se = Column(String(150), primary_key=True)
dest_se = Column(String(150), primary_key=True)
active = Column(Integer, default=5)
datetime = Column(DateTime, default=None)
ema = Column(Float, default=0)
fixed = Column(Flag(negative='off', positive='on'), default='off')
min_active = Column(Integer, default=None)
max_active = Column(Integer, default=None)
class OptimizerStreams(Base):
__tablename__ = 't_optimize_streams'
source_se = Column(String(150), primary_key=True)
dest_se = Column(String(150), primary_key=True)
nostreams = Column(Integer, primary_key=True)
datetime = Column(DateTime)
throughput = Column(Float)
tested = Column(Integer, default=0)
__table_args__ = (
ForeignKeyConstraint(['source_se', 'dest_se'],
[OptimizerActive.source_se, OptimizerActive.dest_se]),
)
class Optimize(Base):
__tablename__ = 't_optimize'
......
# Copyright notice:
#
# Copyright CERN 2013-2015
#
# Copyright Members of the EMI Collaboration, 2013.
# See www.eu-emi.eu for details on the copyright holders
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
try:
import simplejson as json
except:
import json
import logging
from pylons import request
from fts3.model import *
from fts3rest.controllers.config import audit_configuration
from fts3rest.lib.api import doc
from fts3rest.lib.base import BaseController, Session
from fts3rest.lib.helpers import jsonify, accept, get_input_as_dict
from fts3rest.lib.http_exceptions import *
from fts3rest.lib.middleware.fts3auth import authorize
from fts3rest.lib.middleware.fts3auth.constants import *
__controller__ = 'FixConfigController'
log = logging.getLogger(__name__)
Preset_Min_Active = 2
Preset_Max_Active = 60
class FixConfigController(BaseController):
"""
Configure fixed limits
"""
@doc.response(403, 'The user is not allowed to modify the configuration')
@authorize(CONFIG)
@jsonify
def fix_active(self):
"""
Fixes the number of actives for a pair
"""
input_dict = get_input_as_dict(request)
source = input_dict.get('source_se')
destination = input_dict.get('dest_se')
try:
min_active = int(input_dict.get('min_active', 0))
max_active = int(input_dict.get('max_active', 0))
except Exception, e:
raise HTTPBadRequest('Active must be an integer (%s)' % str(e))
if not source or not destination:
raise HTTPBadRequest('Missing source and/or destination')
if min_active is None:
raise HTTPBadRequest('Missing min_active')
if max_active is None:
raise HTTPBadRequest('Missing max_active')
if min_active > max_active:
raise HTTPBadRequest('max_active is lower than min_active')
opt_active = Session.query(OptimizerActive).get((source, destination))
if not opt_active:
opt_active = OptimizerActive(
source_se=source,
dest_se=destination
)
else:
Session.expunge(opt_active)
try:
if min_active > 0 and max_active > 0:
opt_active.active = min_active
opt_active.min_active = min_active
opt_active.max_active = max_active
opt_active.fixed = True
audit_configuration('fix-active', '%s => %s actives fixed to range(%s, %s)' % (source, destination, min_active, max_active))
else:
opt_active.min_active = Preset_Min_Active
opt_active.max_active = Preset_Max_Active
opt_active.fixed = False
audit_configuration('fix-active', '%s => %s actives unfixed' % (source, destination))
Session.merge(opt_active)
Session.commit()
except:
Session.rollback()
raise
return opt_active
@doc.response(403, 'The user is not allowed to query the configuration')
@authorize(CONFIG)
@accept(html_template='/config/fixed.html')
def get_fixed_active(self):
"""
Gets the fixed pairs
"""
input_dict = get_input_as_dict(request, from_query=True)
source = input_dict.get('source_se')
destination = input_dict.get('dest_se')
fixed = Session.query(OptimizerActive).filter(OptimizerActive.fixed == True)
if source:
fixed = fixed.filter(OptimizerActive.source_se == source)
if destination:
fixed = fixed.filter(OptimizerActive.dest_se == destination)
return fixed.all()
/*
* Copyright 2015 CERN
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
function refreshFixList()
{
var tbody = $("#fixed-list");
$.ajax({
headers: {
Accept : "application/json",
},
url: "/config/fixed?",
})
.done(function(data, textStatus, jqXHR) {
tbody.empty();
$.each(data, function(i, fix) {
var tr = $("<tr></tr>");
var deleteBtn = $("<button class='btn btn-link' type='button'></button>")
.append("<i class='glyphicon glyphicon-trash'></i>");
deleteBtn.click(function() {
tr.css("background", "#d9534f");
var data = {
vo: fix.vo,
source_se: fix.source_se,
dest_se: fix.dest_se,
min_active: 0,
max_active: 0
};
console.log(data);
$.ajax({
url: "/config/fixed",
type: "POST",
dataType: "json",
contentType: "application/json",
data: JSON.stringify(data)
})
.done(function(data, textStatus, jqXHR) {
tr.fadeOut(300, function() {tr.remove();})
})
.fail(function(jqXHR) {
errorMessage(jqXHR);
tr.css("background", "#ffffff");
});
});
var changeFunction = function(){
var data = {
vo: fix.vo,
source_se: fix.source_se,
dest_se: fix.dest_se,
min_active: changeMinActiveField.val(),
max_active: changeMaxActiveField.val()
};
console.log(data);
$.ajax({
url: "/config/fixed",
type: "POST",
dataType: "json",
contentType: "application/json",
data: JSON.stringify(data)
})
.done(function(data, textStatus, jqXHR) {
tr.css("background", "#ffffff").css("transition", "background .50s ease-in-out");
})
.fail(function(jqXHR) {
alert(jqXHR.responseJSON.message);
});
tr.css("background", "#5bb75b").css("transition", "none");
};
var changeMinActiveField = $("<input type='number' class='form-control'></input>")
.attr("value", fix.min_active)
.attr("min", 2)
.change(changeFunction);
var changeMaxActiveField = $("<input type='number' class='form-control'></input>")
.attr("value", fix.max_active)
.attr("min", 2)
.change(changeFunction);
tr.append($("<td></td>").append(deleteBtn))
.append($("<td></td>").text(fix.vo))
.append($("<td></td>").text(fix.source_se))
.append($("<td></td>").text(fix.dest_se))
.append($("<td></td>").append(changeMinActiveField))
.append($("<td></td>").append(changeMaxActiveField));
tbody.append(tr);
});
})
.fail(function(jqXHR) {
errorMessage(jqXHR);
});
}
function setupFixed()
{
// Refresh
refreshFixList();
// Attach to forms
$("#fixed-add-frm").submit(function(event) {
var payload = {
vo: $(this).find("[name=vo]").val(),
source_se: $(this).find("[name=source_se]").val(),
dest_se: $(this).find("[name=dest_se]").val(),
min_active: $(this).find("[name=min_active]").val(),
max_active: $(this).find("[name=max_active]").val(),
};
$.ajax({
url: "/config/fixed",
type: "POST",
dataType: "json",
contentType: "application/json",
data: JSON.stringify(payload)
})
.done(function(data, textStatus, jqXHR) {
refreshFixList();
$("#fixed-add-frm").trigger("reset");
})
.fail(function(jqXHR) {
errorMessage(jqXHR);
})
.always(function() {
$("#fixed-add-frm input").prop("disabled", false);
$("#fixed-add-frm input> i").attr("class", "glyphicon glyphicon-plus");
});
$("#fixed-add-frm input").prop("disabled", true);
$("#fixed-add-frm input>i").attr("class", "glyphicon glyphicon-refresh");
event.preventDefault();
});
// Autocomplete
$("#fixed-add-field-source").autocomplete({
source: "/autocomplete/source"
});
$("#fixed-add-field-destination").autocomplete({
source: "/autocomplete/destination"
});
}
<%include file="header.html" args="page='/config/fixed', subtitle='Fixed values'"/>
<div class="container">
<h1>Fix values</h1>
<p>
This view allows to force a fixed number of actives per link.
Please, mind that this is a "soft" enforcement, so the actual number will fluctuate slightly
around the value.
</p>
<form id="fixed-add-frm" method="POST">
<table class="table">
<thead>
<tr>
<th style="width: 5em;"></th>
<th>VO</th>
<th>Source</th>
<th>Destination</th>
<th style="width: 10em;">Min Active</th>
<th style="width: 10em;">Max Active</th>
</tr>
</thead>
<tbody id="fixed-list">
</tbody>
<tbody id="fixed-add">
<tr>
<td>
<button class="btn btn-link" type="submit">
<i class="glyphicon glyphicon-plus"></i>
</button>
</td>
<td>
<input type="text" name="vo" id="fixed-add-field-vo" class="form-control"
placeholder="VO"/>
</td>
<td>
<input type="text" name="source_se" id="fixed-add-field-source" class="form-control"
placeholder="Source"/>
</td>
<td>
<input type="text" name="dest_se" id="fixed-add-field-destination" class="form-control"
placeholder="Destination"/>
</td>
<td>
<input type="number" name="min_active" class="form-control" value="2" min="2"/>
</td>
<td>
<input type="number" name="max_active" class="form-control" value="60" min="2"/>
</td>
</tr>
</tbody>
</table>
</form>
</div>
<script src="/js/config/fixed.js" onload="setupFixed()"></script>
<%include file="footer.html"/>
......@@ -16,7 +16,7 @@
from fts3rest.tests import TestController
from fts3rest.lib.base import Session
from fts3.model import ConfigAudit, LinkConfig, OptimizerActive
from fts3.model import ConfigAudit, LinkConfig
class TestConfigLinks(TestController):
......@@ -32,6 +32,7 @@ class TestConfigLinks(TestController):
"""
Set a SE link configuration
"""
resp = self.app.post_json("/config/links", params={
'symbolicname': 'test-link',
'source': 'test.cern.ch',
......@@ -41,8 +42,7 @@ class TestConfigLinks(TestController):
'min_active': 25,
'max_active': 150,
'optimizer_mode': 5
}, status=200).json
}, status=200).json
audits = Session.query(ConfigAudit).all()
self.assertEqual(1, len(audits))
......@@ -159,133 +159,4 @@ class TestConfigLinks(TestController):
audits = Session.query(ConfigAudit).all()
self.assertEqual(2, len(audits))
def test_set_fixed_active(self):
"""
Set the fixed number of actives for a pair
"""
resp = self.app.post_json("/config/fixed", params={
'source_se': 'test.cern.ch',
'dest_se': 'test2.cern.ch',
'min_active': 5,
'max_active': 100,
}, status=200).json
audits = Session.query(ConfigAudit).all()
self.assertEqual(1, len(audits))
opt_active = Session.query(OptimizerActive).get(('test.cern.ch', 'test2.cern.ch'))
self.assertIsNotNone(opt_active)
self.assertEqual(5, opt_active.min_active)
self.assertEqual(100, opt_active.max_active)
self.assertEqual(5, opt_active.active)
self.assertEqual(True, opt_active.fixed)
self.assertEqual(opt_active.source_se, resp['source_se'])
self.assertEqual(opt_active.dest_se, resp['dest_se'])
self.assertEqual(opt_active.min_active, resp['min_active'])
self.assertEqual(opt_active.max_active, resp['max_active'])
def test_set_fixed_active_no_range(self):
"""
Set the fixed number of actives for a pair
"""
resp = self.app.post_json("/config/fixed", params={
'source_se': 'test.cern.ch',
'dest_se': 'test2.cern.ch',
'min_active': 10,
'max_active': 10,
}, status=200).json
audits = Session.query(ConfigAudit).all()
self.assertEqual(1, len(audits))
opt_active = Session.query(OptimizerActive).get(('test.cern.ch', 'test2.cern.ch'))
self.assertIsNotNone(opt_active)
self.assertEqual(10, opt_active.min_active)
self.assertEqual(10, opt_active.max_active)
self.assertEqual(10, opt_active.active)
self.assertEqual(True, opt_active.fixed)
self.assertEqual(opt_active.source_se, resp['source_se'])
self.assertEqual(opt_active.dest_se, resp['dest_se'])
self.assertEqual(opt_active.min_active, resp['min_active'])
self.assertEqual(opt_active.max_active, resp['max_active'])
def test_unset_fixed_active(self):
"""
Unset the fixed number of actives for a pair
"""
self.test_set_fixed_active()
self.app.post_json("/config/fixed", params={
'source_se': 'test.cern.ch',
'dest_se': 'test2.cern.ch',
'min_active': -1
}, status=200)
audits = Session.query(ConfigAudit).all()
self.assertEqual(2, len(audits))
opt_active = Session.query(OptimizerActive).get(('test.cern.ch', 'test2.cern.ch'))
self.assertIsNotNone(opt_active)
self.assertEqual(2, opt_active.min_active)
self.assertEqual(60, opt_active.max_active)
self.assertEqual(False, opt_active.fixed)
def test_get_fixed_active(self):
"""
Get the fixed number of active
"""
pairs = self.app.get_json("/config/fixed", status=200).json
self.assertEqual(0, len(pairs))
self.test_set_fixed_active()
pairs = self.app.get_json("/config/fixed", status=200).json
self.assertEqual(1, len(pairs))
self.assertEqual('test.cern.ch', pairs[0]['source_se'])
self.assertEqual('test2.cern.ch', pairs[0]['dest_se'])
self.assertEqual(5, pairs[0]['min_active'])
self.assertEqual(100, pairs[0]['max_active'])
pairs = self.app.get_json("/config/fixed?source_se=test.cern.ch&dest_se=test2.cern.ch", status=200).json
self.assertEqual(1, len(pairs))
self.assertEqual('test.cern.ch', pairs[0]['source_se'])
self.assertEqual('test2.cern.ch', pairs[0]['dest_se'])
self.assertEqual(5, pairs[0]['min_active'])
self.assertEqual(100, pairs[0]['max_active'])
def test_update_fixed_active(self):
"""
Set fixed, update again.
Number of actives should be bumped to match the minimum.
"""
resp = self.app.post_json("/config/fixed", params={
'source_se': 'test.cern.ch',
'dest_se': 'test2.cern.ch',
'min_active': 5,
'max_active': 100,
}, status=200).json
self.assertEqual('test.cern.ch', resp['source_se'])
resp = self.app.post_json("/config/fixed", params={
'source_se': 'test.cern.ch',
'dest_se': 'test2.cern.ch',
'min_active': 20,
'max_active': 50,
}, status=200).json
audits = Session.query(ConfigAudit).all()
self.assertEqual(2, len(audits))
opt_active = Session.query(OptimizerActive).get(('test.cern.ch', 'test2.cern.ch'))
self.assertIsNotNone(opt_active)
self.assertEqual(20, opt_active.min_active)
self.assertEqual(50, opt_active.max_active)
self.assertEqual(20, opt_active.active)
self.assertEqual(True, opt_active.fixed)
self.assertEqual(opt_active.source_se, resp['source_se'])
self.assertEqual(opt_active.dest_se, resp['dest_se'])
self.assertEqual(opt_active.min_active, resp['min_active'])
self.assertEqual(opt_active.max_active, resp['max_active'])
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment