Commit ac5f3142 by Őry Máté

Merge branch 'connect-via-client' into 'master'

Connect via Client support

fixes #222
parents dd87b7e7 19408a06
......@@ -368,7 +368,7 @@ if get_env_variable('DJANGO_SAML', 'FALSE') == 'TRUE':
from shutilwhich import which
from saml2 import BINDING_HTTP_POST, BINDING_HTTP_REDIRECT
INSTALLED_APPS += (
INSTALLED_APPS += (
'djangosaml2',
)
AUTHENTICATION_BACKENDS = (
......@@ -465,3 +465,6 @@ SESSION_COOKIE_NAME = "csessid%x" % (((getnode() // 139) ^
(getnode() % 983)) & 0xffff)
MAX_NODE_RAM = get_env_variable("MAX_NODE_RAM", 1024)
# Url to download the client: (e.g. http://circlecloud.org/client/download/)
CLIENT_DOWNLOAD_URL = get_env_variable('CLIENT_DOWNLOAD_URL', 'http://circlecloud.org/client/download/')
......@@ -383,6 +383,19 @@ $(function () {
$('.notification-messages').load("/dashboard/notifications/");
$('#notification-button a span[class*="badge-pulse"]').remove();
});
/* on the client confirmation button fire the clientInstalledAction */
$(document).on("click", "#client-check-button", function(event) {
var connectUri = $('#connect-uri').val();
clientInstalledAction(connectUri);
return false;
});
$("#dashboard-vm-details-connect-button").click(function(event) {
var connectUri = $(this).attr("href");
clientInstalledAction(connectUri);
return false;
});
});
function generateVmHTML(pk, name, host, icon, _status, fav, is_last) {
......@@ -589,6 +602,12 @@ function addModalConfirmation(func, data) {
});
}
function clientInstalledAction(location) {
setCookie('downloaded_client', true, 365 * 24 * 60 * 60, "/");
window.location.href = location;
$('#confirmation-modal').modal("hide");
}
// for AJAX calls
/**
* Getter for user cookies
......@@ -611,6 +630,14 @@ function getCookie(name) {
return cookieValue;
}
function setCookie(name, value, seconds, path) {
if (seconds!=null) {
var today = new Date();
var expire = new Date();
expire.setTime(today.getTime() + seconds);
}
document.cookie = name+"="+escape(value)+"; expires="+expire.toUTCString()+"; path="+path;
}
/* no js compatibility */
function noJS() {
......
......@@ -378,8 +378,14 @@ function checkNewActivity(runs) {
}
$("#vm-details-state span").html(data['human_readable_status'].toUpperCase());
if(data['status'] == "RUNNING") {
if(data['connect_uri']) {
$("#dashboard-vm-details-connect-button").removeClass('disabled');
}
$("[data-target=#_console]").attr("data-toggle", "pill").attr("href", "#console").parent("li").removeClass("disabled");
} else {
if(data['connect_uri']) {
$("#dashboard-vm-details-connect-button").addClass('disabled');
}
$("[data-target=#_console]").attr("data-toggle", "_pill").attr("href", "#").parent("li").addClass("disabled");
}
......
......@@ -115,7 +115,7 @@ $(function() {
$('#confirmation-modal').modal("hide");
updateStatuses(1);
/* if there are messages display them */
if(data.messages && data.messages.length > 0) {
addMessage(data.messages.join("<br />"), "danger");
......@@ -123,7 +123,7 @@ $(function() {
},
error: function(xhr, textStatus, error) {
$('#confirmation-modal').modal("hide");
if (xhr.status == 500) {
addMessage("500 Internal Server Error", "danger");
} else {
......@@ -145,7 +145,7 @@ $(function() {
// this didn't work ;;
// var th = $("this").find("th");
$(".table-sorting").hide();
$(".vm-list-table thead th i").remove();
var icon_html = '<i class="fa fa-sort-' + (data.direction == "desc" ? "desc" : "asc") + ' pull-right"></i>';
......@@ -160,13 +160,13 @@ $(function() {
});
$(document).on("click", ".mass-migrate-node", function() {
$(this).find('input[type="radio"]').prop("checked", true);
$(this).find('input[type="radio"]').prop("checked", true);
});
if(checkStatusUpdate() || $("#vm-list-table tbody tr").length >= 100) {
updateStatuses(1);
}
});
......@@ -209,10 +209,10 @@ function updateStatuses(runs) {
$(this).remove();
}
});
if(checkStatusUpdate()) {
setTimeout(
function() {updateStatuses(runs + 1)},
function() {updateStatuses(runs + 1)},
1000 + Math.exp(runs * 0.05)
);
}
......@@ -239,7 +239,7 @@ function collectIds(rows) {
for(var i = 0; i < rows.length; i++) {
ids.push(rows[i].vm);
}
return ids;
return ids;
}
function setRowColor(row) {
......
{% load i18n %}
<p>
{% blocktrans %}
To effortlessly connect to all kind of virtual machines you have to install the <strong>CIRCLE Client</strong>.
{% endblocktrans %}
</p>
<p class="text-info">
{% blocktrans %}
To install the <strong>CIRCLE Client</strong> click on the <strong>Download the Client</strong> button.
The button takes you to the installation detail page, where you can choose your operating system and start
the download or read more detailed information about the <strong>Client</strong>. The program can be installed on Windows XP (and above)
or Debian based Linux operating systems. To successfully install the client you have to have admin (root or elevated) rights.
After the installation complete clicking on the <strong>I have the Client installed</strong> button will launch the appropriate tool
designed for that connection with necessarily predefined configurations. This option will also save your answer and this prompt about
installation will not pop up again.
{% endblocktrans %}
</p>
<br>
<div class="pull-right">
<form method="POST" id="dashboard-client-check" action="">
{% csrf_token %}
<a class="btn btn-default" href="{% url "dashboard.views.detail" pk=instance.pk %}" data-dismiss="modal">{% trans "Cancel" %}</a>
<a class="btn btn-info" href="{{ client_download_url }}" traget="_blank">{% trans "Download the Client" %}</a>
<button data-dismiss="modal" id="client-check-button" type="submit" class="btn btn-success" title="{% trans "I downloaded and installed the client and I want to connect using it. This choice will be saved to your compuer" %}">
<i class="fa fa-external-link"></i> {% trans "I have the Client installed" %}
</button>
<input id="connect-uri" name="connect-uri" type="hidden" value="{% if instance.get_connect_uri %}{{ instance.get_connect_uri}}{% endif %}" />
<input name="vm" type="hidden" value="{% if instance.get_connect_uri %}{{ instance.pk}}{% endif %}" />
</form>
</div>
\ No newline at end of file
<a data-group-pk="{{ record.pk }}"
class="btn btn-danger btn-xs real-link group-delete"
<a data-group-pk="{{ record.pk }}"
class="btn btn-danger btn-xs real-link group-delete"
href="{% url "dashboard.views.delete-group" pk=record.pk %}?next={{ request.path }}">
<i class="fa fa-trash-o"></i>
</a>
......@@ -3,10 +3,10 @@
<div class="panel-heading">
<div class="pull-right toolbar">
<div class="btn-group">
<a href="#index-graph-view" data-index-box="vm" class="btn btn-default btn-xs"
<a href="#index-graph-view" data-index-box="vm" class="btn btn-default btn-xs"
data-container="body"
title="{% trans "summary view" %}"><i class="fa fa-dashboard"></i></a>
<a href="#index-list-view" data-index-box="vm" class="btn btn-default btn-xs disabled"
<a href="#index-list-view" data-index-box="vm" class="btn btn-default btn-xs disabled"
data-container="body"
title="{% trans "list view" %}"><i class="fa fa-list"></i></a>
</div>
......@@ -50,7 +50,7 @@
<div class="row">
<form action="{% url "dashboard.views.vm-list" %}" method="GET" id="dashboard-vm-search-form">
<div class="col-sm-6 col-xs-6 input-group input-group-sm">
<input id="dashboard-vm-search-input" type="text" class="form-control" name="s"
<input id="dashboard-vm-search-input" type="text" class="form-control" name="s"
placeholder="{% trans "Search..." %}" />
<div class="input-group-btn">
<button type="submit" class="form-control btn btn-primary"><i class="fa fa-search"></i></button>
......
......@@ -23,7 +23,7 @@
{{ search_form.s }}
<div class="input-group-btn">
{{ search_form.stype }}
<button type="submit" class="btn btn-primary input-tags">
<button type="submit" class="btn btn-primary input-tags">
<i class="fa fa-search"></i>
</button>
</div>
......
......@@ -133,8 +133,21 @@
<i class="fa fa-copy" title="{% trans "Select all" %}"></i>
</span>
</div>
{% endfor %}
{% if instance.get_connect_uri %}
<div id="dashboard-vm-details-connect" class="operation-wrapper">
{% if client_download %}
<a id="dashboard-vm-details-connect-button" class="btn btn-xs btn-default operation " href="{{ instance.get_connect_uri}}" title="{% trans "Connect via the CIRCLE Client" %}">
<i class="fa fa-external-link"></i> {% trans "Connect" %}
</a>
<a href="{% url "dashboard.views.client-check" %}?vm={{ instance.pk }}">{% trans "Download client" %}</a>
{% else %}
<a id="dashboard-vm-details-connect-download-button" class="btn btn-xs btn-default operation " href="{% url "dashboard.views.client-check" %}?vm={{ instance.pk }}" title="{% trans "Download the CIRCLE Client" %}">
<i class="fa fa-external-link"></i> {% trans "Connect (download client)" %}
</a>
{% endif %}
</div>
{% endif %}
</div>
<div class="col-md-8" id="vm-detail-pane">
<div class="panel panel-default" id="vm-detail-panel">
......
......@@ -21,7 +21,7 @@
<strong>{% trans "Group actions" %}</strong>
<button id="vm-list-group-select-all" class="btn btn-info btn-xs">{% trans "Select all" %}</button>
{% for o in ops %}
<a href="{{ o.get_url }}" class="btn btn-xs btn-{{ o.effect }} mass-operation"
<a href="{{ o.get_url }}" class="btn btn-xs btn-{{ o.effect }} mass-operation"
title="{{ o.name|capfirst }}" disabled>
<i class="fa fa-{{ o.icon }}"></i>
</a>
......@@ -34,12 +34,12 @@
<div class="input-group-btn">
{{ search_form.stype }}
</div>
<label class="input-group-addon input-tags" title="{% trans "Include deleted VMs" %}"
<label class="input-group-addon input-tags" title="{% trans "Include deleted VMs" %}"
id="vm-list-search-checkbox-span" data-container="body">
{{ search_form.include_deleted }}
</label>
<div class="input-group-btn">
<button type="submit" class="btn btn-primary input-tags">
<button type="submit" class="btn btn-primary input-tags">
<i class="fa fa-search"></i>
</button>
</div>
......
......@@ -45,6 +45,7 @@ from .views import (
VmTraitsUpdate, VmRawDataUpdate,
GroupPermissionsView,
LeaseAclUpdateView,
ClientCheck,
)
autocomplete_light.autodiscover()
......@@ -204,4 +205,6 @@ urlpatterns = patterns(
name="dashboard.views.store-new-directory"),
url(r"^store/refresh_toplist$", store_refresh_toplist,
name="dashboard.views.store-refresh-toplist"),
url(r"^client/check$", ClientCheck.as_view(),
name="dashboard.views.client-check"),
)
......@@ -411,6 +411,9 @@ class VmDetailView(CheckedDetailView):
context['can_change_resources'] = self.request.user.has_perm(
"vm.change_resources")
# client info
context['client_download'] = self.request.COOKIES.get(
'downloaded_client')
# can link template
context['can_link_template'] = (
instance.template and instance.template.has_level(user, "operator")
......@@ -1531,6 +1534,37 @@ class GroupAclUpdateView(AclUpdateView):
return super(GroupAclUpdateView, self).get_object().profile
class ClientCheck(LoginRequiredMixin, TemplateView):
def get_template_names(self):
if self.request.is_ajax():
return ['dashboard/_modal.html']
else:
return ['dashboard/nojs-wrapper.html']
def get_context_data(self, *args, **kwargs):
context = super(ClientCheck, self).get_context_data(*args, **kwargs)
context.update({
'box_title': _('About CIRCLE Client'),
'ajax_title': False,
'client_download_url': settings.CLIENT_DOWNLOAD_URL,
'template': "dashboard/_client-check.html",
'instance': get_object_or_404(
Instance, pk=self.request.GET.get('vm')),
})
if not context['instance'].has_level(self.request.user, 'operator'):
raise PermissionDenied()
return context
def post(self, request, *args, **kwargs):
instance = get_object_or_404(Instance, pk=request.POST.get('vm'))
if not instance.has_level(request.user, 'operator'):
raise PermissionDenied()
response = HttpResponseRedirect(instance.get_absolute_url())
response.set_cookie('downloaded_client', 'True', 365 * 24 * 60 * 60)
return response
class TemplateChoose(LoginRequiredMixin, TemplateView):
def get_template_names(self):
......@@ -2675,6 +2709,7 @@ def vm_activity(request, pk):
if not show_all:
activities = activities[:10]
response['connect_uri'] = instance.get_connect_uri()
response['human_readable_status'] = instance.get_status_display()
response['status'] = instance.status
response['icon'] = instance.get_status_icon()
......
......@@ -6,7 +6,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-09-03 12:57+0200\n"
"POT-Creation-Date: 2014-09-03 18:49+0200\n"
"PO-Revision-Date: 2014-09-03 12:51+0200\n"
"Last-Translator: Mate Ory <ory.mate@ik.bme.hu>\n"
"Language-Team: Hungarian <cloud@ik.bme.hu>\n"
......
......@@ -607,9 +607,7 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin,
port = self.get_connect_port(use_ipv6=use_ipv6)
host = self.get_connect_host(use_ipv6=use_ipv6)
proto = self.access_method
if proto == 'ssh':
proto = 'sshterm'
return ('%(proto)s:cloud:%(pw)s:%(host)s:%(port)d' %
return ('circle:%(proto)s:cloud:%(pw)s:%(host)s:%(port)d' %
{'port': port, 'proto': proto, 'pw': self.pw,
'host': host})
except:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment