Merge branch 'master' of ssh://git.planet-lab.org/git/plstackapi
diff --git a/observer-initscript b/observer-initscript
new file mode 100644
index 0000000..d948eac
--- /dev/null
+++ b/observer-initscript
@@ -0,0 +1,63 @@
+#!/bin/bash
+#
+# observer Starts and stops Observer daemon
+#
+
+# Source function library.
+. /etc/init.d/functions
+
+[ -f /etc/sysconfig/plstackobserver ] && . /etc/sysconfig/plstackobserver
+
+
+plstackobserver=${NODEMANAGER-"python /opt/planetstack/planetstack-backend.py -d"}
+prog="OpenCloud Observer"
+pidfile=${PIDFILE-/var/run/plstackobserver.pid}
+
+RETVAL=0
+
+function start() {
+ action $"Starting $prog: " daemon --pidfile=$pidfile --check=plstackobserver $plstackobserver "$@"
+}
+
+function stop() {
+ action $"Stopping $prog: " killproc -p $pidfile plstackobserver
+}
+
+case "$1" in
+ start)
+ start $options
+ ;;
+ stop)
+ stop
+ ;;
+ status)
+ status -p $pidfile plstackobserver
+ RETVAL=$?
+ ;;
+ restart|reload)
+ shift
+ stop
+ start $options "$@"
+ ;;
+ condrestart)
+ shift
+ [ -f ${pidfile} ] && { stop; start $options "$@"; }
+ ;;
+ restartverbose)
+ shift
+ stop
+ $plstackobserver $verboseoptions "$@"
+ ;;
+ restartdebug)
+ shift
+ stop
+ echo "Restarting with $debugoptions $@ .."
+ $plstackobserver $debugoptions "$@"
+ ;;
+ *)
+ echo $"Usage: $0 {start|stop|status|restart|condrestart|restartdebug [-d]}"
+ exit 1
+ ;;
+esac
+
+exit $RETVAL
diff --git a/opencloud.spec b/opencloud.spec
index 184cbf8..26f67f8 100644
--- a/opencloud.spec
+++ b/opencloud.spec
@@ -70,12 +70,15 @@
rm -rf %{buildroot}
mkdir -p %{buildroot}
install -d %{buildroot}/opt/planetstack
+install -d %{buildroot}/etc/init.d
# in builddir
cp -rp ./planetstack %{buildroot}/opt/.
+cp observer-initscript %{buildroot}/etc/init.d/plstackobserver
find %{buildroot}/opt/planetstack -type f -print | sed "s@^$RPM_BUILD_ROOT@@g" > %{_tmppath}/tmp-filelist
cp %{_tmppath}/tmp-filelist /tmp/tmp-filelist
+echo /etc/init.d/plstackobserver > %{_tmppath}/tmp-filelist
%clean
rm -rf %{buildroot}
diff --git a/planetstack/core/admin.py b/planetstack/core/admin.py
index 8e81b4b..9f8fbb0 100644
--- a/planetstack/core/admin.py
+++ b/planetstack/core/admin.py
@@ -234,6 +234,13 @@
def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
if db_field.name == 'deploymentNetwork':
kwargs['queryset'] = Deployment.select_by_acl(request.user)
+ # the inscrutable jquery selector below says:
+ # find the closest parent "tr" to the current element
+ # then find the child with class "field-node"
+ # then find the child with that is a select
+ # then return its id
+ kwargs['widget'] = forms.Select(attrs={'onChange': "update_nodes(this, $($(this).closest('tr')[0]).find('.field-node select')[0].id)"})
+ #kwargs['widget'] = forms.Select(attrs={'onChange': "console.log($($($(this).closest('tr')[0]).children('.field-node')[0]).children('select')[0].id);"})
field = super(SliverInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
@@ -760,6 +767,19 @@
('reservations','Reservations'),
)
+ def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
+ #deployment_nodes = {}
+ #for node in Node.objects.all():
+ # deployment_nodes[node.deployment.id] = get(deployment_nodes, node.deployment.id, []).append( (node.id, node.name) )
+
+ deployment_nodes = []
+ for node in Node.objects.all():
+ deployment_nodes.append( (node.deployment.id, node.id, node.name) )
+
+ context["deployment_nodes"] = deployment_nodes
+
+ return super(SliceAdmin, self).render_change_form(request, context, add, change, form_url, obj)
+
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == 'site':
kwargs['queryset'] = Site.select_by_user(request.user)
diff --git a/planetstack/core/context_processors.py b/planetstack/core/context_processors.py
index 270ec4e..9cfcaa6 100644
--- a/planetstack/core/context_processors.py
+++ b/planetstack/core/context_processors.py
@@ -1,4 +1,14 @@
from django.conf import settings
+from core.models import Site
+
def planetstack(request):
- return {"DISABLE_MINIDASHBOARD": settings.DISABLE_MINIDASHBOARD}
+ allSites = []
+ for site in Site.objects.all():
+ allowNewUsers = True # replace with logic for blessing sites for registration, if necessary
+ allSites.append( {"name": site.name,
+ "id": site.id,
+ "allowNewUsers": allowNewUsers} )
+
+ return {"DISABLE_MINIDASHBOARD": settings.DISABLE_MINIDASHBOARD,
+ "sites": allSites}
diff --git a/planetstack/core/fixtures/initial_data.json b/planetstack/core/fixtures/initial_data.json
index 1c81b7e..c5c754f 100644
--- a/planetstack/core/fixtures/initial_data.json
+++ b/planetstack/core/fixtures/initial_data.json
@@ -501,6 +501,16 @@
}
},
{
+ "pk": 5,
+ "model": "core.siterole",
+ "fields": {
+ "updated": "2014-04-22T11:34:39.770Z",
+ "enacted": null,
+ "role": "default",
+ "created": "2014-04-22T11:34:39.770Z"
+ }
+},
+{
"pk": 5,
"model": "core.deployment",
"fields": {
diff --git a/planetstack/core/static/planetstack.css b/planetstack/core/static/planetstack.css
index 44d62b4..8fb6e86 100644
--- a/planetstack/core/static/planetstack.css
+++ b/planetstack/core/static/planetstack.css
@@ -496,13 +496,13 @@
display:none;
}
-.login #content-main form input[type=text]{
+.login #content-main form input[type=text], .requestDialog.ui-widget input{
width: 94%;
padding:4px 6px;
border-radius: 0px;
height: 30px;
-background-color: #E5E5E5;
-background-image: url('name.png');
+background-color: rgb(250, 255, 189);
+/*background-image: url('name.png');*/
background-repeat: no-repeat;
background-position: 95%;
font-size: 12px;
@@ -671,11 +671,18 @@
float: left;
}
-.createAccountLink {
+#request-account-form{
+ display:none;
+}
+
+#requestAccountLink {
width: 55%;
text-align: right;
float: left;
-
+ padding-left: 21%;
+ cursor: pointer;
+ color: #448CCA;
+ text-decoration: underline;
}
.login .btn-info {
@@ -1031,6 +1038,15 @@
margin-left: 5%;
}
+
+#adv-slice-image-value{
+margin-right: 0.5%;
+}
+
+#adv-network-value {
+margin-right: 0.3%;
+}
+
#network-dropdown,#adv-network-dropdown,#adv-network-value{
margin-left: 3.7%;
}
@@ -1043,16 +1059,16 @@
margin-left: 2%;
}
#adv-dataset-dropdown{
-margin-left: 3%;
+margin-left: 3.7%;
}
#advanced-tenant,#basic-tenant,#sliver-btn,#save-btn{
float:right;
}
- #delete-slice-btn,#download-details{
+ #delete-slice-btn,#download-details,#add-user-btn{
margin-left:1%;
}
-#sliver-btn,#save-btn,#create-slice-btn,#delete-slice-btn,#download-details{
+#sliver-btn,#save-btn,#create-slice-btn,#delete-slice-btn,#add-user-btn,#download-details{
margin-top:1%;
}
@@ -1138,12 +1154,64 @@
#private-vol{
margin-right: 15% !important;
-}
-
-.customize_row {
- display: table;
-}
-.customize_column {
- display: table-cell;
- padding: 10px;
-}
+}
+.customize_row {
+ display: table;
+}
+.customize_column {
+ display: table-cell;
+ padding: 10px;
+}
+
+.request-form-row{
+padding:1% 8%;
+}
+
+.requestDialog{
+background-color: white;
+border-radius: 8px;
+width: 30% !important;
+height: 40% !important;
+margin-top: -16%;
+top: -103.703125px !important;
+}
+
+.request-form-row label{
+ float: left;
+}
+
+
+.requestDialog .ui-dialog-buttonset .ui-button{
+border-radius: 0 !important;
+background-color: grey !important;
+font-weight: bold !important;
+font-size: 0.9em; !important
+}
+
+.requestDialog .ui-dialog-titlebar-close{
+float:right;
+}
+
+#request-signup{
+height: 40px !important;
+margin: 0 14%;
+float: left;
+background-color: #448CCA;
+background-image: none;
+width: 70% !important;
+}
+
+.requestDialog .ui-dialog-titlebar{
+border-radius: 0 !important;
+height: 25px;
+padding-top: 2%;
+}
+
+.requestDialog #ui-id-1{
+padding-left: 28%;
+font-size: medium;
+}
+
+#request-site-name{
+ width: 98%;
+}
diff --git a/planetstack/ec2_observer/syncstep.py b/planetstack/ec2_observer/syncstep.py
index dcfea7d..d5f7523 100644
--- a/planetstack/ec2_observer/syncstep.py
+++ b/planetstack/ec2_observer/syncstep.py
@@ -60,7 +60,10 @@
for dep in self.dependencies:
peer_name = dep[0].lower() + dep[1:] # django names are camelCased with the first letter lower
peer_object = getattr(obj, peer_name)
- if (peer_object.pk==failed.pk):
+
+ # peer_object can be None, and if so there
+ # is no object-level dependency
+ if (peer_object and peer_object.pk==failed.pk):
raise FailedDependency
def call(self, failed=[], deletion=False):
diff --git a/planetstack/planetstack/urls.py b/planetstack/planetstack/urls.py
index 3513a38..929505d 100644
--- a/planetstack/planetstack/urls.py
+++ b/planetstack/planetstack/urls.py
@@ -114,7 +114,7 @@
url(r'^plstackapi/users/$', UserList.as_view(), name='user-list'),
url(r'^plstackapi/users/(?P<pk>[a-zA-Z0-9_\-]+)/$', UserDetail.as_view(), name='user-detail'),
- url(r'^legacyapi/$', 'core.views.legacyapi.LegacyXMLRPC', name='xmlrpc'),
+ url(r'^xmlrpc/legacyapi/$', 'core.views.legacyapi.LegacyXMLRPC', name='xmlrpc'),
# url(r'^analytics/(?P<name>\w+)/$', AnalyticsAjaxView.as_view(), name="analytics"),
diff --git a/planetstack/templates/admin/core/slice/change_form.html b/planetstack/templates/admin/core/slice/change_form.html
new file mode 100644
index 0000000..c94b580
--- /dev/null
+++ b/planetstack/templates/admin/core/slice/change_form.html
@@ -0,0 +1,26 @@
+{% extends 'admin/change_form.html' %}
+{% block extrahead %}
+{{ block.super }}
+<script>
+deployment_nodes = [
+{% for dn in deployment_nodes %}
+ [{{ dn.0 }}, {{ dn.1 }} , "{{ dn.2 }}"],
+{% endfor %}
+];
+
+function update_nodes(deployment_select, node_select_id) {
+ deployment_id = $(deployment_select).val();
+ html = "<option value=''>---------</option>\n";
+ for (i in deployment_nodes) {
+ dn = deployment_nodes[i];
+ if (dn[0] == deployment_id) {
+ html = html + '<option value="' + dn[1] + '">' + dn[2] + '</option>\n'
+ }
+ }
+ //console.log(html);
+ $("#"+node_select_id).empty().append(html);
+}
+</script>
+
+{% endblock %}
+
diff --git a/planetstack/templates/admin/login.html b/planetstack/templates/admin/login.html
index b04f842..a165707 100644
--- a/planetstack/templates/admin/login.html
+++ b/planetstack/templates/admin/login.html
@@ -4,8 +4,11 @@
{% block extrastyle %}{{ block.super }}
<link rel="stylesheet" type="text/css" href="/static/suit/bootstrap/css/bootstrap.min.css" media="all"/>
<link rel="stylesheet" type="text/css" href="{% static "planetstack.css" %}" />
+<script src="{% static 'suit/js/jquery-1.9.1.min.js' %}"></script>
+<script src="http://code.jquery.com/ui/1.11.0/jquery-ui.js"></script>
{% endblock %}
+
{% block bodyclass %}login{% endblock %}
{% block nav-global %}{% endblock %}
@@ -42,22 +45,96 @@
<input type="hidden" name="this_is_the_login_form" value="1" />
<input type="hidden" name="next" value="{{ next }}" />
</div>
- {% url 'admin_password_reset' as password_reset_url %}
- {% if password_reset_url %}
- <div class="password-reset-link">
- <a href="{{ password_reset_url }}">{% trans 'Forgotten your password or username?' %}</a>
- </div>
- {% endif %}
<div class="submit-row">
<input type="submit" class="btn btn-info" value="{% trans 'SIGN IN' %}" />
</div>
-{% url 'django-admindocs-docroot' as docsroot %}
- {% if docsroot %}
-<div class="createAccountLink"><a href="{{ docsroot }}">{% trans 'Request a new Account' %}</a></div>
-
- {% endif %}
+ <div id="requestAccountLink">{% trans 'Request a new Account' %}</div>
</form>
+
+<div id="request-account-form" title="Request an Account" style="display: none;">
+ <form>
+ <fieldset>
+ <div class="request-form-row">
+ <label for="request-first-name">First Name</label>
+ <input type="text" name="request-first-name" id="request-first-name">
+ </div>
+ <div class="request-form-row">
+ <label for="request-last-name">Last Name</label>
+ <input type="text" name="request-last-name" id="request-last-name">
+ </div>
+ <div class="request-form-row">
+ <label for="request-email">Email</label>
+ <input type="text" name="request-email" id="request-email">
+ </div>
+ <div class="request-form-row">
+ <label for="request-site-name">Site</label><br>
+ <select id="request-site-name" name="request-site-name">
+ <option>---------</option>
+ {% for site in sites %}
+ {% if site.allowNewUsers %}
+ <option>{{ site.name }}</option>
+ {% endif %}
+ {% endfor %}
+ </select>
+ </div>
+ <div class="submit-row">
+ <input id ="request-signup" class="btn btn-info" value="SIGN UP">
+ </div>
+ </fieldset>
+ </form>
+</div>
+</div>
+</div>
+
+
<script type="text/javascript">
+$(function() {
+ initRequest();
+});
+function initRequest(){
+ $.ajax({
+ url: '/tenantview',
+ dataType: 'json',
+ success: function (data) {
+ var sites = data['sitesToBeRequested'];
+ console.log(sites);
+ for (site in sites){
+ $("#request-site-name").append("<option>" + site + "</option>");
+ }
+ }
+ });
+}
+$("#requestAccountLink").unbind().click(function(){
+ $("#request-account-form").dialog({
+ autoOpen: false,
+ modal: true,
+ dialogClass: "requestDialog",
+ });
+ $("#request-account-form").dialog("open");
+})
+$("#request-signup").unbind().click(function(){
+ $.ajax({
+ url: '/requestaccess/',
+ dataType: 'json',
+ data: {
+ email: $("#request-email").val(),
+ firstname: $("#request-first-name").val(),
+ lastname: $("#request-last-name").val(),
+ site: $("#request-site-name").val(),
+ csrfmiddlewaretoken: "{{ csrf_token }}", // < here
+ state: "inactive"
+ },
+ async: false,
+ type: 'POST',
+ success: function () {
+ $("#request-account-form").dialog("close");
+ alert("Your request has been submitted");
+ },
+ error:function (xhr, textStatus, thrownError){
+ alert("Error:", textStatus + " " + xhr.responseText);
+ }
+ });
+})
document.getElementById('id_username').focus()
</script>
</div>