Revision 426e1fb9
b/snf-cyclades-app/conf/20-snf-cyclades-app-ui.conf | ||
---|---|---|
39 | 39 |
## consecutive API calls (aligning changes-since attribute). |
40 | 40 |
#UI_CHANGES_SINCE_ALIGNMENT = 0 |
41 | 41 |
# |
42 |
## How often to check for user usage changes |
|
43 |
#UI_QUOTAS_UPDATE_INTERVAL = 10000 |
|
44 |
# |
|
45 | 42 |
## URL to redirect not authenticated users |
46 | 43 |
#UI_LOGIN_URL = "/im/login" |
47 | 44 |
# |
... | ... | |
183 | 180 |
## The name of the grouped network view |
184 | 181 |
#UI_GROUPED_PUBLIC_NETWORK_NAME = 'Internet' |
185 | 182 |
# |
183 |
## Endpoint to make account specific requests (resources/quotas) |
|
184 |
#UI_ACCOUNTS_API_URL = '/astakos/api' |
|
186 | 185 |
# |
187 | 186 |
################ |
188 | 187 |
## UI EXTENSIONS |
b/snf-cyclades-app/synnefo/api/delegate.py | ||
---|---|---|
42 | 42 |
ASTAKOS_URL = getattr(settings, 'ASTAKOS_URL', None) |
43 | 43 |
USER_CATALOG_URL = urlparse.urljoin(ASTAKOS_URL, "user_catalogs") |
44 | 44 |
USER_FEEDBACK_URL = urlparse.urljoin(ASTAKOS_URL, "feedback") |
45 |
USER_QUOTA_URL = urlparse.urljoin(ASTAKOS_URL, "astakos/api/quotas") |
|
46 |
RESOURCES_URL = urlparse.urljoin(ASTAKOS_URL, "astakos/api/resources") |
|
45 | 47 |
|
46 | 48 |
from objpool.http import PooledHTTPConnection |
47 | 49 |
|
... | ... | |
68 | 70 |
|
69 | 71 |
|
70 | 72 |
@csrf_exempt |
73 |
def delegate_to_resources_service(request): |
|
74 |
logger.debug("Delegate resources request to %s" % RESOURCES_URL) |
|
75 |
token = request.META.get('HTTP_X_AUTH_TOKEN') |
|
76 |
headers = {'X-Auth-Token': token} |
|
77 |
return proxy(request, RESOURCES_URL, headers=headers, |
|
78 |
body=request.raw_post_data) |
|
79 |
|
|
80 |
|
|
81 |
@csrf_exempt |
|
82 |
def delegate_to_user_quota_service(request): |
|
83 |
logger.debug("Delegate quotas request to %s" % USER_QUOTA_URL) |
|
84 |
token = request.META.get('HTTP_X_AUTH_TOKEN') |
|
85 |
headers = {'X-Auth-Token': token} |
|
86 |
return proxy(request, USER_QUOTA_URL, headers=headers, |
|
87 |
body=request.raw_post_data) |
|
88 |
|
|
89 |
|
|
90 |
@csrf_exempt |
|
71 | 91 |
def delegate_to_feedback_service(request): |
72 | 92 |
logger.debug("Delegate feedback request to %s" % USER_FEEDBACK_URL) |
73 | 93 |
token = request.META.get('HTTP_X_AUTH_TOKEN') |
b/snf-cyclades-app/synnefo/app_settings/urls.py | ||
---|---|---|
51 | 51 |
urlpatterns += patterns( |
52 | 52 |
'', |
53 | 53 |
(r'^feedback/?$', 'synnefo.api.delegate.delegate_to_feedback_service'), |
54 |
(r'^user_catalogs/?$', 'synnefo.api.delegate.delegate_to_user_catalogs_service')) |
|
55 |
|
|
54 |
(r'^user_catalogs/?$', |
|
55 |
'synnefo.api.delegate.delegate_to_user_catalogs_service'), |
|
56 |
(r'^astakos/api/resources/?$', |
|
57 |
'synnefo.api.delegate.delegate_to_resources_service'), |
|
58 |
(r'^astakos/api/quotas/?$', |
|
59 |
'synnefo.api.delegate.delegate_to_user_quota_service')) |
b/snf-cyclades-app/synnefo/ui/static/snf/js/models.js | ||
---|---|---|
615 | 615 |
|
616 | 616 |
var _success = _.bind(function() { |
617 | 617 |
if (success) { success() }; |
618 |
snf.ui.main.load_user_quotas();
|
|
618 |
synnefo.storage.quotas.get('cyclades.network.private').decrease();
|
|
619 | 619 |
}, this); |
620 | 620 |
var _error = _.bind(function() { |
621 | 621 |
this.set({state: previous_state, status: previous_status}) |
... | ... | |
1441 | 1441 |
// set state after successful call |
1442 | 1442 |
self.state('DESTROY'); |
1443 | 1443 |
success.apply(this, arguments); |
1444 |
snf.ui.main.load_user_quotas();
|
|
1444 |
synnefo.storage.quotas.get('cyclades.vm').decrease();
|
|
1445 | 1445 |
|
1446 | 1446 |
}, |
1447 | 1447 |
error, 'destroy', params); |
... | ... | |
1693 | 1693 |
|
1694 | 1694 |
var cb = function() { |
1695 | 1695 |
callback(); |
1696 |
snf.ui.main.load_user_quotas();
|
|
1696 |
synnefo.storage.quotas.get('cyclades.network.private').increase();
|
|
1697 | 1697 |
} |
1698 | 1698 |
return this.api_call(this.path, "create", params, cb); |
1699 | 1699 |
}, |
... | ... | |
2122 | 2122 |
} |
2123 | 2123 |
} |
2124 | 2124 |
|
2125 |
opts = {name: name, imageRef: image.id, flavorRef: flavor.id, metadata:meta} |
|
2125 |
opts = {name: name, imageRef: image.id, flavorRef: flavor.id, |
|
2126 |
metadata:meta} |
|
2126 | 2127 |
opts = _.extend(opts, extra); |
2127 | 2128 |
|
2128 | 2129 |
var cb = function(data) { |
2129 |
snf.ui.main.load_user_quotas();
|
|
2130 |
synnefo.storage.quotas.get('cyclades.vm').increase();
|
|
2130 | 2131 |
callback(data); |
2131 | 2132 |
} |
2132 | 2133 |
|
2133 |
this.api_call(this.path, "create", {'server': opts}, undefined, undefined, cb, {critical: true}); |
|
2134 |
this.api_call(this.path, "create", {'server': opts}, undefined, |
|
2135 |
undefined, cb, {critical: true}); |
|
2134 | 2136 |
}, |
2135 | 2137 |
|
2136 | 2138 |
load_missing_images: function(callback) { |
... | ... | |
2365 | 2367 |
|
2366 | 2368 |
this.create(m.attributes, options); |
2367 | 2369 |
} |
2370 |
}); |
|
2371 |
|
|
2372 |
|
|
2373 |
models.Quota = models.Model.extend({ |
|
2374 |
|
|
2375 |
initialize: function() { |
|
2376 |
models.Quota.__super__.initialize.apply(this, arguments); |
|
2377 |
this.bind("change", this.check, this); |
|
2378 |
this.check(); |
|
2379 |
}, |
|
2380 |
|
|
2381 |
check: function() { |
|
2382 |
var usage, limit; |
|
2383 |
usage = this.get('usage'); |
|
2384 |
limit = this.get('limit'); |
|
2385 |
if (usage >= limit) { |
|
2386 |
this.trigger("available"); |
|
2387 |
} else { |
|
2388 |
this.trigger("unavailable"); |
|
2389 |
} |
|
2390 |
}, |
|
2391 |
|
|
2392 |
increase: function(val) { |
|
2393 |
if (val === undefined) { val = 1}; |
|
2394 |
this.set({'usage': this.get('usage') + val}) |
|
2395 |
}, |
|
2396 |
|
|
2397 |
decrease: function(val) { |
|
2398 |
if (val === undefined) { val = 1}; |
|
2399 |
this.set({'usage': this.get('usage') - val}) |
|
2400 |
}, |
|
2401 |
|
|
2402 |
can_consume: function() { |
|
2403 |
var usage, limit; |
|
2404 |
usage = this.get('usage'); |
|
2405 |
limit = this.get('limit'); |
|
2406 |
if (usage >= limit) { |
|
2407 |
return false |
|
2408 |
} else { |
|
2409 |
return true |
|
2410 |
} |
|
2411 |
}, |
|
2412 |
|
|
2413 |
is_bytes: function() { |
|
2414 |
return this.get('resource').get('unit') == 'bytes'; |
|
2415 |
}, |
|
2416 |
|
|
2417 |
get_available: function() { |
|
2418 |
var value = this.get('limit') - this.get('usage'); |
|
2419 |
if (value < 0) { return value } |
|
2420 |
return value |
|
2421 |
}, |
|
2422 |
|
|
2423 |
get_readable: function(key) { |
|
2424 |
var value; |
|
2425 |
if (key == 'available') { |
|
2426 |
value = this.get_available(); |
|
2427 |
} else { |
|
2428 |
value = this.get(key) |
|
2429 |
} |
|
2430 |
if (!this.is_bytes()) { |
|
2431 |
return value + ""; |
|
2432 |
} |
|
2433 |
return snf.util.readablizeBytes(value); |
|
2434 |
} |
|
2435 |
}); |
|
2436 |
|
|
2437 |
models.Quotas = models.Collection.extend({ |
|
2438 |
model: models.Quota, |
|
2439 |
api_type: 'accounts', |
|
2440 |
path: 'quotas', |
|
2441 |
parse: function(resp) { |
|
2442 |
return _.map(resp.system, function(value, key) { |
|
2443 |
var available = (value.limit - value.usage) || 0; |
|
2444 |
return _.extend(value, {'name': key, 'id': key, |
|
2445 |
'available': available, |
|
2446 |
'resource': snf.storage.resources.get(key)}); |
|
2447 |
}) |
|
2448 |
} |
|
2368 | 2449 |
}) |
2450 |
|
|
2451 |
models.Resource = models.Model.extend({ |
|
2452 |
api_type: 'accounts', |
|
2453 |
path: 'resources' |
|
2454 |
}); |
|
2455 |
|
|
2456 |
models.Resources = models.Collection.extend({ |
|
2457 |
api_type: 'accounts', |
|
2458 |
path: 'resources', |
|
2459 |
model: models.Network, |
|
2460 |
|
|
2461 |
parse: function(resp) { |
|
2462 |
return _.map(resp, function(value, key) { |
|
2463 |
return _.extend(value, {'name': key, 'id': key}); |
|
2464 |
}) |
|
2465 |
} |
|
2466 |
}); |
|
2369 | 2467 |
|
2370 | 2468 |
// storage initialization |
2371 | 2469 |
snf.storage.images = new models.Images(); |
... | ... | |
2374 | 2472 |
snf.storage.vms = new models.VMS(); |
2375 | 2473 |
snf.storage.keys = new models.PublicKeys(); |
2376 | 2474 |
snf.storage.nics = new models.NICs(); |
2377 |
|
|
2378 |
//snf.storage.vms.fetch({update:true}); |
|
2379 |
//snf.storage.images.fetch({update:true}); |
|
2380 |
//snf.storage.flavors.fetch({update:true}); |
|
2475 |
snf.storage.resources = new models.Resources(); |
|
2476 |
snf.storage.quotas = new models.Quotas(); |
|
2381 | 2477 |
|
2382 | 2478 |
})(this); |
b/snf-cyclades-app/synnefo/ui/static/snf/js/ui/web/ui_create_view.js | ||
---|---|---|
678 | 678 |
image_excluded = storage.flavors.unavailable_values_for_image(this.current_image); |
679 | 679 |
} |
680 | 680 |
|
681 |
if (snf.user.quota) { |
|
682 |
quotas = this.get_vm_params_quotas(); |
|
683 |
user_excluded = storage.flavors.unavailable_values_for_quotas(quotas); |
|
684 |
} |
|
681 |
quotas = this.get_vm_params_quotas(); |
|
682 |
user_excluded = storage.flavors.unavailable_values_for_quotas(quotas); |
|
685 | 683 |
|
686 | 684 |
unavailable.disk = user_excluded.disk.concat(image_excluded.disk); |
687 | 685 |
unavailable.ram = user_excluded.ram.concat(image_excluded.ram); |
... | ... | |
691 | 689 |
}, |
692 | 690 |
|
693 | 691 |
get_vm_params_quotas: function() { |
692 |
var quotas = synnefo.storage.quotas; |
|
694 | 693 |
var quota = { |
695 |
'ram': snf.user.quota.get_available('cyclades.ram'),
|
|
696 |
'cpu': snf.user.quota.get_available('cyclades.cpu'),
|
|
697 |
'disk': snf.user.quota.get_available('cyclades.disk')
|
|
694 |
'ram': quotas.get('cyclades.ram').get('available'),
|
|
695 |
'cpu': quotas.get('cyclades.cpu').get('available'),
|
|
696 |
'disk': quotas.get('cyclades.disk').get('available')
|
|
698 | 697 |
} |
699 | 698 |
return quota; |
700 | 699 |
}, |
... | ... | |
769 | 768 |
}; |
770 | 769 |
}, this)); |
771 | 770 |
|
772 |
this.$("#create-vm-flavor-options .flavor-options.ram li").each(_.bind(function(i, el){
|
|
771 |
this.$("#create-vm-flavor-options .flavor-options.mem li").each(_.bind(function(i, el){
|
|
773 | 772 |
var el_value = $(el).data("value"); |
774 | 773 |
if (this.unavailable_values.ram.indexOf(el_value) > -1) { |
775 | 774 |
$(el).addClass("disabled"); |
... | ... | |
941 | 940 |
}, |
942 | 941 |
|
943 | 942 |
update_quota_display: function() { |
944 |
if (!snf.user.quota || !snf.user.quota.data) { return }; |
|
945 | 943 |
|
944 |
var quotas = synnefo.storage.quotas; |
|
946 | 945 |
_.each(["disk", "ram", "cpu"], function(type) { |
947 |
var available_dsp = snf.user.quota.get_available_readable(type);
|
|
948 |
var available = snf.user.quota.get_available(type);
|
|
946 |
var available_dsp = quotas.get('cyclades.'+type).get_readable('available');
|
|
947 |
var available = quotas.get('cyclades.'+type).get('available');
|
|
949 | 948 |
var content = "({0} left)".format(available_dsp); |
950 | 949 |
if (available <= 0) { content = "(None left)" } |
951 | 950 |
|
b/snf-cyclades-app/synnefo/ui/static/snf/js/ui/web/ui_main_view.js | ||
---|---|---|
231 | 231 |
this.fix_position(); |
232 | 232 |
}, this)); |
233 | 233 |
|
234 |
storage.vms.bind("change:pending_action", _.bind(this.handle_action_add, this, "vms")); |
|
235 |
storage.vms.bind("change:reboot_required", _.bind(this.handle_action_add, this, "reboots")); |
|
236 |
storage.networks.bind("change:actions", _.bind(this.handle_action_add, this, "nets")); |
|
234 |
storage.vms.bind("change:pending_action", |
|
235 |
_.bind(this.handle_action_add, this, "vms")); |
|
236 |
storage.vms.bind("change:reboot_required", |
|
237 |
_.bind(this.handle_action_add, this, "reboots")); |
|
238 |
storage.networks.bind("change:actions", |
|
239 |
_.bind(this.handle_action_add, this, "nets")); |
|
237 | 240 |
}, |
238 | 241 |
|
239 | 242 |
handle_action_add: function(type, model, action) { |
... | ... | |
532 | 535 |
storage.vms.bind("add", _.bind(this.check_empty, this)); |
533 | 536 |
storage.vms.bind("change:status", _.bind(this.check_empty, this)); |
534 | 537 |
storage.vms.bind("reset", _.bind(this.check_empty, this)); |
538 |
storage.quotas.bind("change", _.bind(this.update_create_buttons_status, this)); |
|
535 | 539 |
|
536 | 540 |
}, |
537 | 541 |
|
... | ... | |
592 | 596 |
$(".css-panes").show(); |
593 | 597 |
}, |
594 | 598 |
|
595 |
items_to_load: 4,
|
|
599 |
items_to_load: 6,
|
|
596 | 600 |
completed_items: 0, |
597 | 601 |
check_status: function(loaded) { |
598 | 602 |
this.completed_items++; |
... | ... | |
629 | 633 |
self.update_status("networks", 1); |
630 | 634 |
self.check_status(); |
631 | 635 |
}}); |
636 |
|
|
632 | 637 |
}, |
633 | 638 |
|
634 | 639 |
init_intervals: function() { |
... | ... | |
642 | 647 |
|
643 | 648 |
this._networks = storage.networks.get_fetcher.apply(storage.networks, _.clone(fetcher_params)); |
644 | 649 |
this._vms = storage.vms.get_fetcher.apply(storage.vms, _.clone(fetcher_params)); |
650 |
this._quotas = storage.quotas.get_fetcher.apply(storage.quotas, _.clone(fetcher_params)); |
|
645 | 651 |
}, |
646 | 652 |
|
647 | 653 |
stop_intervals: function() { |
648 | 654 |
if (this._networks) { this._networks.stop(); } |
649 | 655 |
if (this._vms) { this._vms.stop(); } |
656 |
if (this._quotas) { this._quotas.stop(); } |
|
650 | 657 |
this.intervals_stopped = true; |
651 | 658 |
}, |
652 | 659 |
|
... | ... | |
665 | 672 |
this.init_intervals(); |
666 | 673 |
} |
667 | 674 |
|
675 |
if (this._quotas) { |
|
676 |
this._quotas.stop(); |
|
677 |
this._quotas.start(); |
|
678 |
} else { |
|
679 |
this.init_intervals(); |
|
680 |
} |
|
681 |
|
|
668 | 682 |
this.intervals_stopped = false; |
669 | 683 |
}, |
670 | 684 |
|
... | ... | |
683 | 697 |
snf.config.update_hidden_views = uhv; |
684 | 698 |
|
685 | 699 |
window.setTimeout(function() { |
686 |
self.update_status("layout", 0); |
|
687 | 700 |
self.load_initialize_overlays(); |
688 | 701 |
}, 20); |
689 | 702 |
}, |
... | ... | |
732 | 745 |
self.update_status("flavors", 1); |
733 | 746 |
self.check_status() |
734 | 747 |
}}); |
748 |
|
|
749 |
this.update_status("resources", 0); |
|
750 |
storage.resources.fetch({refresh:true, update:false, success: function(){ |
|
751 |
self.update_status("resources", 1); |
|
752 |
self.update_status("quotas", 0); |
|
753 |
self.check_status(); |
|
754 |
storage.quotas.fetch({refresh:true, update:true, success: function() { |
|
755 |
self.update_status("quotas", 1); |
|
756 |
self.update_status("layout", 1); |
|
757 |
self.check_status() |
|
758 |
}}) |
|
759 |
}}) |
|
735 | 760 |
}, |
736 | 761 |
|
737 | 762 |
update_status: function(ns, state) { |
... | ... | |
779 | 804 |
} |
780 | 805 |
}, |
781 | 806 |
|
782 |
quota_handlers_initialized: false, |
|
783 |
|
|
784 |
load_user_quotas: function(repeat) { |
|
785 |
var main_view = this; |
|
786 |
if (!snf.user.quota) { |
|
787 |
snf.user.quota = new snf.quota.Quota("cyclades"); |
|
788 |
main_view.init_quotas_handlers(); |
|
789 |
|
|
790 |
} |
|
791 |
|
|
792 |
snf.api.sync('read', undefined, { |
|
793 |
url: synnefo.config.quota_url, |
|
794 |
success: function(d) { |
|
795 |
snf.user.quota.load(d); |
|
796 |
}, |
|
797 |
complete: function() { |
|
798 |
if (repeat) { |
|
799 |
setTimeout(function(){ |
|
800 |
main_view.load_user_quotas(1); |
|
801 |
}, synnefo.config.quotas_update_interval || 10000); |
|
802 |
} |
|
803 |
} |
|
804 |
}); |
|
805 |
}, |
|
806 |
|
|
807 |
check_quotas: function(type) { |
|
808 |
var storage = synnefo.storage[type]; |
|
809 |
var consumed = storage.length; |
|
810 |
var quotakey = { |
|
811 |
'networks': 'cyclades.network.private', |
|
812 |
'vms': 'cyclades.vm' |
|
813 |
} |
|
814 |
if (type == "networks") { |
|
815 |
consumed = storage.filter(function(net){ |
|
816 |
return !net.is_public() && !net.is_deleted(); |
|
817 |
}).length; |
|
818 |
} |
|
819 |
|
|
820 |
var limit = snf.user.quota.get_limit(quotakey[type]); |
|
821 |
if (snf.user.quota && snf.user.quota.data && consumed >= limit) { |
|
822 |
storage.trigger("quota_reached"); |
|
823 |
} else { |
|
824 |
storage.trigger("quota_free"); |
|
825 |
} |
|
826 |
}, |
|
827 |
|
|
828 |
init_quotas_handlers: function() { |
|
829 |
var self = this, event; |
|
830 |
snf.user.quota.bind("cyclades.vm.quota.changed", function() { |
|
831 |
this.check_quotas("vms"); |
|
832 |
}, this); |
|
833 |
|
|
834 |
var event = "cyclades.network.private.quota.changed"; |
|
835 |
snf.user.quota.bind(event, function() { |
|
836 |
this.check_quotas("networks"); |
|
837 |
}, this); |
|
838 |
}, |
|
839 |
|
|
840 | 807 |
// initial view based on user cookie |
841 | 808 |
show_initial_view: function() { |
842 | 809 |
this.set_vm_view_handlers(); |
843 |
this.load_user_quotas(1); |
|
844 | 810 |
this.hide_loading_view(); |
845 |
|
|
846 | 811 |
bb.history.start(); |
847 |
|
|
848 | 812 |
this.trigger("ready"); |
849 | 813 |
}, |
850 | 814 |
|
... | ... | |
853 | 817 |
this.router.vm_details_view(vm.id); |
854 | 818 |
} |
855 | 819 |
}, |
820 |
|
|
821 |
update_create_buttons_status: function() { |
|
822 |
var nets = storage.quotas.get('cyclades.network.private'); |
|
823 |
var vms = storage.quotas.get('cyclades.vm'); |
|
824 |
|
|
825 |
if (!nets || !vms) { return } |
|
826 |
|
|
827 |
if (!nets.can_consume()) { |
|
828 |
$("#networks-pane a.createbutton").addClass("disabled"); |
|
829 |
} else { |
|
830 |
$("#networks-pane a.createbutton").removeClass("disabled"); |
|
831 |
} |
|
832 |
|
|
833 |
if (!vms.can_consume()) { |
|
834 |
$("#createcontainer #create").addClass("disabled"); |
|
835 |
} else { |
|
836 |
$("#createcontainer #create").removeClass("disabled"); |
|
837 |
} |
|
838 |
}, |
|
856 | 839 |
|
857 | 840 |
set_vm_view_handlers: function() { |
858 | 841 |
var self = this; |
... | ... | |
861 | 844 |
if ($(this).hasClass("disabled")) { return } |
862 | 845 |
self.router.vm_create_view(); |
863 | 846 |
}); |
864 |
|
|
865 |
synnefo.storage.vms.bind("quota_reached", function(){ |
|
866 |
$("#createcontainer #create").addClass("disabled"); |
|
867 |
$("#createcontainer #create").attr("title", "Machines limit reached"); |
|
868 |
}); |
|
869 |
|
|
870 |
synnefo.storage.vms.bind("quota_free", function(){ |
|
871 |
$("#createcontainer #create").removeClass("disabled"); |
|
872 |
$("#createcontainer #create").attr("title", ""); |
|
873 |
}); |
|
874 |
|
|
875 | 847 |
}, |
876 | 848 |
|
877 | 849 |
check_empty: function() { |
b/snf-cyclades-app/synnefo/ui/templates/home.html | ||
---|---|---|
558 | 558 |
<div id="loading-view" class="hidden clearfix"> |
559 | 559 |
<div class="header clearfix images off">Loading images...<span></span></div> |
560 | 560 |
<div class="header clearfix flavors off">Loading flavors...<span></span></div> |
561 |
<div class="header clearfix resources off">Loading resources...<span></span></div> |
|
562 |
<div class="header clearfix quotas off">Loading quotas...<span></span></div> |
|
561 | 563 |
<div class="header clearfix vms off">Loading machines...<span></span></div> |
562 | 564 |
<div class="header clearfix networks off">Loading networks...<span></span></div> |
563 | 565 |
<div class="header clearfix layout off">Rendering layout...<span></span></div> |
... | ... | |
611 | 613 |
// TODO: make it dynamic |
612 | 614 |
synnefo.config.api_urls = { |
613 | 615 |
'compute': {{ compute_api_url|safe }}, |
614 |
'glance': {{ glance_api_url|safe }} |
|
616 |
'glance': {{ glance_api_url|safe }}, |
|
617 |
'accounts': {{ accounts_api_url|safe }}, |
|
615 | 618 |
}; |
616 | 619 |
|
617 | 620 |
// TODO: configurable userdata urls in models.js |
... | ... | |
649 | 652 |
synnefo.config.grouped_public_network_name = {{ grouped_public_network_name|safe }}; |
650 | 653 |
synnefo.config.vm_hostname_format = {{ vm_hostname_format|safe }}; |
651 | 654 |
synnefo.config.automatic_network_range_format = {{ automatic_network_range_format|safe }}; |
652 |
synnefo.config.quota_url = '{% url synnefo.ui.views.user_quota %}'; |
|
653 | 655 |
synnefo.config.custom_image_help_url = '{{ custom_image_help_url|safe }}'; |
654 | 656 |
synnefo.config.quotas_update_interval = {{ quotas_update_interval }}; |
655 | 657 |
|
b/snf-cyclades-app/synnefo/ui/urls.py | ||
---|---|---|
37 | 37 |
|
38 | 38 |
urlpatterns = patterns('', |
39 | 39 |
url(r'^$', 'synnefo.ui.views.home', name='ui_index'), |
40 |
url(r'^userquota$', 'synnefo.ui.views.user_quota', name='ui_userquota'), |
|
41 | 40 |
url(r'userdata/', include('synnefo.ui.userdata.urls')) |
42 | 41 |
) |
43 | 42 |
|
b/snf-cyclades-app/synnefo/ui/views.py | ||
---|---|---|
62 | 62 |
getattr(settings, "UI_UPDATE_INTERVAL_INCREASE_AFTER_CALLS_COUNT", 3) |
63 | 63 |
UPDATE_INTERVAL_FAST = getattr(settings, "UI_UPDATE_INTERVAL_FAST", 2500) |
64 | 64 |
UPDATE_INTERVAL_MAX = getattr(settings, "UI_UPDATE_INTERVAL_MAX", 10000) |
65 |
QUOTAS_UPDATE_INTERVAL = getattr(settings, "UI_QUOTAS_UPDATE_INTERVAL", 10000) |
|
66 | 65 |
|
67 | 66 |
# predefined values settings |
68 | 67 |
VM_IMAGE_COMMON_METADATA = \ |
... | ... | |
161 | 160 |
USER_CATALOG_URL = getattr(settings, 'UI_USER_CATALOG_URL', '/user_catalogs') |
162 | 161 |
FEEDBACK_POST_URL = getattr(settings, 'UI_FEEDBACK_POST_URL', '/feedback') |
163 | 162 |
TRANSLATE_UUIDS = not getattr(settings, 'TRANSLATE_UUIDS', False) |
163 |
ACCOUNTS_API_URL = getattr(settings, 'UI_ACCOUNTS_API_URL', '/astakos/api') |
|
164 | 164 |
|
165 | 165 |
|
166 | 166 |
def template(name, request, context): |
... | ... | |
189 | 189 |
'compute_api_url': json.dumps(COMPUTE_API_URL), |
190 | 190 |
'user_catalog_url': json.dumps(USER_CATALOG_URL), |
191 | 191 |
'feedback_post_url': json.dumps(FEEDBACK_POST_URL), |
192 |
'accounts_api_url': json.dumps(ACCOUNTS_API_URL), |
|
192 | 193 |
'translate_uuids': json.dumps(TRANSLATE_UUIDS), |
193 | 194 |
# update interval settings |
194 | 195 |
'update_interval': UPDATE_INTERVAL, |
195 | 196 |
'update_interval_increase': UPDATE_INTERVAL_INCREASE, |
196 | 197 |
'update_interval_increase_after_calls': |
197 |
UPDATE_INTERVAL_INCREASE_AFTER_CALLS_COUNT, |
|
198 |
UPDATE_INTERVAL_INCREASE_AFTER_CALLS_COUNT,
|
|
198 | 199 |
'update_interval_fast': UPDATE_INTERVAL_FAST, |
199 | 200 |
'update_interval_max': UPDATE_INTERVAL_MAX, |
200 | 201 |
'changes_since_alignment': CHANGES_SINCE_ALIGNMENT, |
201 |
'quotas_update_interval': QUOTAS_UPDATE_INTERVAL, |
|
202 | 202 |
'image_icons': IMAGE_ICONS, |
203 | 203 |
'logout_redirect': LOGOUT_URL, |
204 | 204 |
'login_redirect': LOGIN_URL, |
... | ... | |
255 | 255 |
return template('machines_console', request, context) |
256 | 256 |
|
257 | 257 |
|
258 |
def user_quota(request): |
|
259 |
get_user(request, settings.ASTAKOS_URL, usage=True) |
|
260 |
|
|
261 |
response = json.dumps(request.user['usage']) |
|
262 |
|
|
263 |
return HttpResponse(response, mimetype="application/json") |
|
264 |
|
|
265 |
|
|
266 | 258 |
def js_tests(request): |
267 | 259 |
return template('tests', request, {}) |
268 | 260 |
|
Also available in: Unified diff