From 9dedef43ce47ae665b7e904f54939c9953083220 Mon Sep 17 00:00:00 2001 From: Suman Chakravartula Date: Wed, 6 Jan 2016 19:48:30 -0800 Subject: [PATCH 01/13] dynamically set uid of a container with the owner of mapped Share. #858 --- src/rockstor/storageadmin/views/rockon_helpers.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/rockstor/storageadmin/views/rockon_helpers.py b/src/rockstor/storageadmin/views/rockon_helpers.py index ed44720fd..c7465556c 100644 --- a/src/rockstor/storageadmin/views/rockon_helpers.py +++ b/src/rockstor/storageadmin/views/rockon_helpers.py @@ -183,10 +183,19 @@ def vol_ops(container): ops_list.extend(['-v', '%s:%s' % (share_mnt, v.dest_dir)]) return ops_list +def get_uid(container): + # If there are volumes, return the uid of the owner of the first volume. + vo = DVolume.objects.filter(container=container).first() + if (vo is None): return None + share_mnt = ('%s%s' % (settings.MNT_PT, vo.share.name)) + return str(os.stat(share_mnt).st_uid) + def generic_install(rockon): for c in DContainer.objects.filter(rockon=rockon).order_by('launch_order'): cmd = list(DCMD2) + ['--name', c.name,] cmd.extend(vol_ops(c)) + uid = get_uid(c) + if (uid is not None): cmd.extend(['-u', uid]) cmd.extend(port_ops(c)) cmd.extend(container_ops(c)) cmd.append(c.dimage.name) From 0b17c53dc48e0a9cb1ec8d2485907b3932b52e8a Mon Sep 17 00:00:00 2001 From: Suman Chakravartula Date: Wed, 6 Jan 2016 19:50:05 -0800 Subject: [PATCH 02/13] fix a permissions bug in owncloud install. #858 --- src/rockstor/storageadmin/views/rockon_helpers.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/rockstor/storageadmin/views/rockon_helpers.py b/src/rockstor/storageadmin/views/rockon_helpers.py index c7465556c..51ac1950a 100644 --- a/src/rockstor/storageadmin/views/rockon_helpers.py +++ b/src/rockstor/storageadmin/views/rockon_helpers.py @@ -236,10 +236,15 @@ def transmission_install(rockon): def owncloud_install(rockon): for c in DContainer.objects.filter(rockon=rockon).order_by('launch_order'): + rm_container(c.name) cmd = list(DCMD2) + ['--name', c.name, ] db_user = DCustomConfig.objects.get(rockon=rockon, key='db_user').val db_pw = DCustomConfig.objects.get(rockon=rockon, key='db_pw').val if (c.dimage.name == 'postgres'): + #change permissions on the db volume to 700 + vo = DVolume.objects.get(container=c) + share_mnt = ('%s%s' % (settings.MNT_PT, vo.share.name)) + run_command(['/usr/bin/chmod', '700', share_mnt]) cmd.extend(['-e', 'POSTGRES_USER=%s' % db_user, '-e', 'POSTGRES_PASSWORD=%s' % db_pw]) cmd.extend(port_ops(c)) From af304d4add61e06fd1ddd446f655e7506407531a Mon Sep 17 00:00:00 2001 From: Suman Chakravartula Date: Wed, 6 Jan 2016 20:33:00 -0800 Subject: [PATCH 03/13] remove containers from previous failed install, if any. #858 --- src/rockstor/storageadmin/views/rockon_helpers.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rockstor/storageadmin/views/rockon_helpers.py b/src/rockstor/storageadmin/views/rockon_helpers.py index 51ac1950a..5d10e31fb 100644 --- a/src/rockstor/storageadmin/views/rockon_helpers.py +++ b/src/rockstor/storageadmin/views/rockon_helpers.py @@ -192,6 +192,7 @@ def get_uid(container): def generic_install(rockon): for c in DContainer.objects.filter(rockon=rockon).order_by('launch_order'): + rm_container(c.name) cmd = list(DCMD2) + ['--name', c.name,] cmd.extend(vol_ops(c)) uid = get_uid(c) From 99d609c62e29b08854acf26276b6cc95f13ae48c Mon Sep 17 00:00:00 2001 From: Suman Chakravartula Date: Thu, 7 Jan 2016 10:38:54 -0800 Subject: [PATCH 04/13] don't enforce the user in generic_install. #858 It restricts all containers indescriminately. --- src/rockstor/storageadmin/views/rockon_helpers.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/rockstor/storageadmin/views/rockon_helpers.py b/src/rockstor/storageadmin/views/rockon_helpers.py index 5d10e31fb..5d5f289e9 100644 --- a/src/rockstor/storageadmin/views/rockon_helpers.py +++ b/src/rockstor/storageadmin/views/rockon_helpers.py @@ -195,8 +195,6 @@ def generic_install(rockon): rm_container(c.name) cmd = list(DCMD2) + ['--name', c.name,] cmd.extend(vol_ops(c)) - uid = get_uid(c) - if (uid is not None): cmd.extend(['-u', uid]) cmd.extend(port_ops(c)) cmd.extend(container_ops(c)) cmd.append(c.dimage.name) From 463a9a5497eca83a491d7a0b99f9b439fddcd562 Mon Sep 17 00:00:00 2001 From: Suman Chakravartula Date: Thu, 7 Jan 2016 16:29:23 -0800 Subject: [PATCH 05/13] add a dedicated model for container env variables. #858 --- ...ique_dcontainerenv_container_key__add_f.py | 565 ++++++++++++++++++ src/rockstor/storageadmin/models/__init__.py | 3 +- src/rockstor/storageadmin/models/rockon.py | 16 + 3 files changed, 583 insertions(+), 1 deletion(-) create mode 100644 src/rockstor/storageadmin/migrations/0040_auto__add_dcontainerenv__add_unique_dcontainerenv_container_key__add_f.py diff --git a/src/rockstor/storageadmin/migrations/0040_auto__add_dcontainerenv__add_unique_dcontainerenv_container_key__add_f.py b/src/rockstor/storageadmin/migrations/0040_auto__add_dcontainerenv__add_unique_dcontainerenv_container_key__add_f.py new file mode 100644 index 000000000..7f4eeef23 --- /dev/null +++ b/src/rockstor/storageadmin/migrations/0040_auto__add_dcontainerenv__add_unique_dcontainerenv_container_key__add_f.py @@ -0,0 +1,565 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'DContainerEnv' + db.create_table(u'storageadmin_dcontainerenv', ( + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('container', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['storageadmin.DContainer'])), + ('key', self.gf('django.db.models.fields.CharField')(max_length=1024)), + ('val', self.gf('django.db.models.fields.CharField')(max_length=1024, null=True)), + ('description', self.gf('django.db.models.fields.CharField')(max_length=2048, null=True)), + ('label', self.gf('django.db.models.fields.CharField')(max_length=64, null=True)), + )) + db.send_create_signal('storageadmin', ['DContainerEnv']) + + # Adding unique constraint on 'DContainerEnv', fields ['container', 'key'] + db.create_unique(u'storageadmin_dcontainerenv', ['container_id', 'key']) + + # Adding field 'DContainer.uid' + db.add_column(u'storageadmin_dcontainer', 'uid', + self.gf('django.db.models.fields.IntegerField')(null=True), + keep_default=False) + + + def backwards(self, orm): + # Removing unique constraint on 'DContainerEnv', fields ['container', 'key'] + db.delete_unique(u'storageadmin_dcontainerenv', ['container_id', 'key']) + + # Deleting model 'DContainerEnv' + db.delete_table(u'storageadmin_dcontainerenv') + + # Deleting field 'DContainer.uid' + db.delete_column(u'storageadmin_dcontainer', 'uid') + + + models = { + u'auth.group': { + 'Meta': {'object_name': 'Group'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + u'oauth2_provider.application': { + 'Meta': {'object_name': 'Application'}, + 'authorization_grant_type': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'client_id': ('django.db.models.fields.CharField', [], {'default': "u'XIsUzfCj6XywVaoouJukWytqIYKRttTKUAyx0k4p'", 'unique': 'True', 'max_length': '100', 'db_index': 'True'}), + 'client_secret': ('django.db.models.fields.CharField', [], {'default': "u'PEskqf4naFRo89agOUI36xYuzf0t16n7tMolLeAsz7o51DLeXoGmWDuXJ2KjjrVr8ojLKUvjqn68zDCZgXOkVt7PpnC6LCj70uNWDIvdJcP29Q4fmIA4XhYLOsAkNFIK'", 'max_length': '255', 'db_index': 'True', 'blank': 'True'}), + 'client_type': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'redirect_uris': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'skip_authorization': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'oauth2_provider_application'", 'to': u"orm['auth.User']"}) + }, + 'storageadmin.advancednfsexport': { + 'Meta': {'object_name': 'AdvancedNFSExport'}, + 'export_str': ('django.db.models.fields.CharField', [], {'max_length': '4096'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + 'storageadmin.apikeys': { + 'Meta': {'object_name': 'APIKeys'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '10'}), + 'user': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '8'}) + }, + 'storageadmin.appliance': { + 'Meta': {'object_name': 'Appliance'}, + 'client_id': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), + 'client_secret': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}), + 'current_appliance': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'hostname': ('django.db.models.fields.CharField', [], {'default': "'Rockstor'", 'max_length': '128'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '4096'}), + 'mgmt_port': ('django.db.models.fields.IntegerField', [], {'default': '443'}), + 'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}) + }, + 'storageadmin.configbackup': { + 'Meta': {'object_name': 'ConfigBackup'}, + 'config_backup': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True'}), + 'filename': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'md5sum': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), + 'size': ('django.db.models.fields.IntegerField', [], {'null': 'True'}) + }, + 'storageadmin.containeroption': { + 'Meta': {'object_name': 'ContainerOption'}, + 'container': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['storageadmin.DContainer']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '1024'}), + 'val': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}) + }, + 'storageadmin.dashboardconfig': { + 'Meta': {'object_name': 'DashboardConfig'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'unique': 'True'}), + 'widgets': ('django.db.models.fields.CharField', [], {'max_length': '4096'}) + }, + 'storageadmin.dcontainer': { + 'Meta': {'object_name': 'DContainer'}, + 'dimage': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['storageadmin.DImage']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'launch_order': ('django.db.models.fields.IntegerField', [], {'default': '1'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '1024'}), + 'rockon': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['storageadmin.RockOn']"}), + 'uid': ('django.db.models.fields.IntegerField', [], {'null': 'True'}) + }, + 'storageadmin.dcontainerenv': { + 'Meta': {'unique_together': "(('container', 'key'),)", 'object_name': 'DContainerEnv'}, + 'container': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['storageadmin.DContainer']"}), + 'description': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'null': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '1024'}), + 'label': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}), + 'val': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True'}) + }, + 'storageadmin.dcontainerlink': { + 'Meta': {'unique_together': "(('destination', 'name'),)", 'object_name': 'DContainerLink'}, + 'destination': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'destination_container'", 'to': "orm['storageadmin.DContainer']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}), + 'source': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['storageadmin.DContainer']", 'unique': 'True'}) + }, + 'storageadmin.dcustomconfig': { + 'Meta': {'unique_together': "(('rockon', 'key'),)", 'object_name': 'DCustomConfig'}, + 'description': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'null': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '1024'}), + 'label': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}), + 'rockon': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['storageadmin.RockOn']"}), + 'val': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True'}) + }, + 'storageadmin.dimage': { + 'Meta': {'object_name': 'DImage'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '1024'}), + 'repo': ('django.db.models.fields.CharField', [], {'max_length': '1024'}), + 'tag': ('django.db.models.fields.CharField', [], {'max_length': '1024'}) + }, + 'storageadmin.disk': { + 'Meta': {'object_name': 'Disk'}, + 'btrfs_uuid': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}), + 'offline': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'parted': ('django.db.models.fields.BooleanField', [], {}), + 'pool': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['storageadmin.Pool']", 'null': 'True', 'on_delete': 'models.SET_NULL'}), + 'serial': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True'}), + 'size': ('django.db.models.fields.BigIntegerField', [], {'default': '0'}), + 'smart_available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'smart_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'transport': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True'}), + 'vendor': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True'}) + }, + 'storageadmin.dport': { + 'Meta': {'unique_together': "(('container', 'containerp'),)", 'object_name': 'DPort'}, + 'container': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['storageadmin.DContainer']"}), + 'containerp': ('django.db.models.fields.IntegerField', [], {}), + 'description': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True'}), + 'hostp': ('django.db.models.fields.IntegerField', [], {'unique': 'True'}), + 'hostp_default': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'label': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True'}), + 'protocol': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), + 'uiport': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'storageadmin.dvolume': { + 'Meta': {'unique_together': "(('container', 'dest_dir'),)", 'object_name': 'DVolume'}, + 'container': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['storageadmin.DContainer']"}), + 'description': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True'}), + 'dest_dir': ('django.db.models.fields.CharField', [], {'max_length': '1024'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'label': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True'}), + 'min_size': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'share': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['storageadmin.Share']", 'null': 'True'}), + 'uservol': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'storageadmin.emailclient': { + 'Meta': {'object_name': 'EmailClient'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '1024'}), + 'receiver': ('django.db.models.fields.CharField', [], {'max_length': '1024'}), + 'sender': ('django.db.models.fields.CharField', [], {'max_length': '1024'}), + 'smtp_server': ('django.db.models.fields.CharField', [], {'max_length': '1024'}) + }, + 'storageadmin.group': { + 'Meta': {'object_name': 'Group'}, + 'admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'gid': ('django.db.models.fields.IntegerField', [], {'unique': 'True'}), + 'groupname': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + 'storageadmin.installedplugin': { + 'Meta': {'object_name': 'InstalledPlugin'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'install_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'plugin_meta': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['storageadmin.Plugin']"}) + }, + 'storageadmin.iscsitarget': { + 'Meta': {'object_name': 'IscsiTarget'}, + 'dev_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}), + 'dev_size': ('django.db.models.fields.IntegerField', [], {}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'share': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['storageadmin.Share']"}), + 'tid': ('django.db.models.fields.IntegerField', [], {'unique': 'True'}), + 'tname': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}) + }, + 'storageadmin.netatalkshare': { + 'Meta': {'object_name': 'NetatalkShare'}, + 'description': ('django.db.models.fields.CharField', [], {'default': "'afp on rockstor'", 'max_length': '1024'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'path': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '4096'}), + 'share': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'netatalkshare'", 'unique': 'True', 'to': "orm['storageadmin.Share']"}), + 'time_machine': ('django.db.models.fields.CharField', [], {'default': "'yes'", 'max_length': '3'}) + }, + 'storageadmin.networkinterface': { + 'Meta': {'object_name': 'NetworkInterface'}, + 'autoconnect': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True'}), + 'ctype': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}), + 'dname': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), + 'dns_servers': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True'}), + 'dspeed': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}), + 'dtype': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}), + 'gateway': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ipaddr': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}), + 'itype': ('django.db.models.fields.CharField', [], {'default': "'io'", 'max_length': '100'}), + 'mac': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), + 'method': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), + 'netmask': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}), + 'state': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}) + }, + 'storageadmin.nfsexport': { + 'Meta': {'object_name': 'NFSExport'}, + 'export_group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['storageadmin.NFSExportGroup']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'mount': ('django.db.models.fields.CharField', [], {'max_length': '4096'}), + 'share': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['storageadmin.Share']"}) + }, + 'storageadmin.nfsexportgroup': { + 'Meta': {'object_name': 'NFSExportGroup'}, + 'admin_host': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True'}), + 'editable': ('django.db.models.fields.CharField', [], {'default': "'rw'", 'max_length': '2'}), + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'host_str': ('django.db.models.fields.CharField', [], {'max_length': '4096'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'mount_security': ('django.db.models.fields.CharField', [], {'default': "'insecure'", 'max_length': '8'}), + 'nohide': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'syncable': ('django.db.models.fields.CharField', [], {'default': "'async'", 'max_length': '5'}) + }, + 'storageadmin.oauthapp': { + 'Meta': {'object_name': 'OauthApp'}, + 'application': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['oauth2_provider.Application']", 'unique': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['storageadmin.User']"}) + }, + 'storageadmin.plugin': { + 'Meta': {'object_name': 'Plugin'}, + 'css_file_name': ('django.db.models.fields.CharField', [], {'max_length': '4096'}), + 'description': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '4096'}), + 'display_name': ('django.db.models.fields.CharField', [], {'default': "''", 'unique': 'True', 'max_length': '4096'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'js_file_name': ('django.db.models.fields.CharField', [], {'max_length': '4096'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '4096'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '4096'}) + }, + 'storageadmin.pool': { + 'Meta': {'object_name': 'Pool'}, + 'compression': ('django.db.models.fields.CharField', [], {'max_length': '256', 'null': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'mnt_options': ('django.db.models.fields.CharField', [], {'max_length': '4096', 'null': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '4096'}), + 'raid': ('django.db.models.fields.CharField', [], {'max_length': '10'}), + 'size': ('django.db.models.fields.BigIntegerField', [], {'default': '0'}), + 'toc': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}) + }, + 'storageadmin.poolbalance': { + 'Meta': {'object_name': 'PoolBalance'}, + 'end_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'message': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True'}), + 'percent_done': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'pool': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['storageadmin.Pool']"}), + 'start_time': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'started'", 'max_length': '10'}), + 'tid': ('django.db.models.fields.CharField', [], {'max_length': '36', 'null': 'True'}) + }, + 'storageadmin.poolscrub': { + 'Meta': {'object_name': 'PoolScrub'}, + 'corrected_errors': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'csum_discards': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'csum_errors': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'data_extents_scrubbed': ('django.db.models.fields.BigIntegerField', [], {'default': '0'}), + 'end_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'kb_scrubbed': ('django.db.models.fields.BigIntegerField', [], {'null': 'True'}), + 'last_physical': ('django.db.models.fields.BigIntegerField', [], {'default': '0'}), + 'malloc_errors': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'no_csum': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'pid': ('django.db.models.fields.IntegerField', [], {}), + 'pool': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['storageadmin.Pool']"}), + 'read_errors': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'start_time': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'started'", 'max_length': '10'}), + 'super_errors': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'tree_bytes_scrubbed': ('django.db.models.fields.BigIntegerField', [], {'default': '0'}), + 'tree_extents_scrubbed': ('django.db.models.fields.BigIntegerField', [], {'default': '0'}), + 'uncorrectable_errors': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'unverified_errors': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'verify_errors': ('django.db.models.fields.IntegerField', [], {'default': '0'}) + }, + 'storageadmin.posixacls': { + 'Meta': {'object_name': 'PosixACLs'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'owner': ('django.db.models.fields.CharField', [], {'max_length': '5'}), + 'perms': ('django.db.models.fields.CharField', [], {'max_length': '3'}), + 'smb_share': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['storageadmin.SambaShare']"}) + }, + 'storageadmin.rockon': { + 'Meta': {'object_name': 'RockOn'}, + 'description': ('django.db.models.fields.CharField', [], {'max_length': '2048'}), + 'https': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'icon': ('django.db.models.fields.URLField', [], {'max_length': '1024', 'null': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'link': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True'}), + 'more_info': ('django.db.models.fields.CharField', [], {'max_length': '4096', 'null': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '1024'}), + 'state': ('django.db.models.fields.CharField', [], {'max_length': '2048'}), + 'status': ('django.db.models.fields.CharField', [], {'max_length': '2048'}), + 'ui': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'version': ('django.db.models.fields.CharField', [], {'max_length': '2048'}), + 'volume_add_support': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'website': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'null': 'True'}) + }, + 'storageadmin.sambacustomconfig': { + 'Meta': {'object_name': 'SambaCustomConfig'}, + 'custom_config': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'smb_share': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['storageadmin.SambaShare']"}) + }, + 'storageadmin.sambashare': { + 'Meta': {'object_name': 'SambaShare'}, + 'browsable': ('django.db.models.fields.CharField', [], {'default': "'yes'", 'max_length': '3'}), + 'comment': ('django.db.models.fields.CharField', [], {'default': "'foo bar'", 'max_length': '100'}), + 'guest_ok': ('django.db.models.fields.CharField', [], {'default': "'no'", 'max_length': '3'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'path': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '4096'}), + 'read_only': ('django.db.models.fields.CharField', [], {'default': "'no'", 'max_length': '3'}), + 'shadow_copy': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'share': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'sambashare'", 'unique': 'True', 'to': "orm['storageadmin.Share']"}), + 'snapshot_prefix': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}) + }, + 'storageadmin.setup': { + 'Meta': {'object_name': 'Setup'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'setup_disks': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'setup_network': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'setup_system': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'setup_user': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'storageadmin.sftp': { + 'Meta': {'object_name': 'SFTP'}, + 'editable': ('django.db.models.fields.CharField', [], {'default': "'ro'", 'max_length': '2'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'share': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['storageadmin.Share']", 'unique': 'True'}) + }, + 'storageadmin.share': { + 'Meta': {'object_name': 'Share'}, + 'compression_algo': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True'}), + 'eusage': ('django.db.models.fields.BigIntegerField', [], {'default': '0'}), + 'group': ('django.db.models.fields.CharField', [], {'default': "'root'", 'max_length': '4096'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '4096'}), + 'owner': ('django.db.models.fields.CharField', [], {'default': "'root'", 'max_length': '4096'}), + 'perms': ('django.db.models.fields.CharField', [], {'default': "'755'", 'max_length': '9'}), + 'pool': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['storageadmin.Pool']"}), + 'pqgroup': ('django.db.models.fields.CharField', [], {'default': "'-1/-1'", 'max_length': '32'}), + 'qgroup': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'replica': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'rusage': ('django.db.models.fields.BigIntegerField', [], {'default': '0'}), + 'size': ('django.db.models.fields.BigIntegerField', [], {'default': '0'}), + 'subvol_name': ('django.db.models.fields.CharField', [], {'max_length': '4096'}), + 'toc': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}) + }, + 'storageadmin.smartattribute': { + 'Meta': {'object_name': 'SMARTAttribute'}, + 'aid': ('django.db.models.fields.IntegerField', [], {}), + 'atype': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'failed': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'flag': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'info': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['storageadmin.SMARTInfo']"}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'normed_value': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'raw_value': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'threshold': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'updated': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'worst': ('django.db.models.fields.IntegerField', [], {'default': '0'}) + }, + 'storageadmin.smartcapability': { + 'Meta': {'object_name': 'SMARTCapability'}, + 'capabilities': ('django.db.models.fields.CharField', [], {'max_length': '2048'}), + 'flag': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'info': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['storageadmin.SMARTInfo']"}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '1024'}) + }, + 'storageadmin.smarterrorlog': { + 'Meta': {'object_name': 'SMARTErrorLog'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'info': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['storageadmin.SMARTInfo']"}), + 'line': ('django.db.models.fields.CharField', [], {'max_length': '128'}) + }, + 'storageadmin.smarterrorlogsummary': { + 'Meta': {'object_name': 'SMARTErrorLogSummary'}, + 'details': ('django.db.models.fields.CharField', [], {'max_length': '1024'}), + 'error_num': ('django.db.models.fields.IntegerField', [], {}), + 'etype': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'info': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['storageadmin.SMARTInfo']"}), + 'lifetime_hours': ('django.db.models.fields.IntegerField', [], {}), + 'state': ('django.db.models.fields.CharField', [], {'max_length': '64'}) + }, + 'storageadmin.smartidentity': { + 'Meta': {'object_name': 'SMARTIdentity'}, + 'assessment': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'ata_version': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'capacity': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'device_model': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'enabled': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'firmware_version': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'in_smartdb': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'info': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['storageadmin.SMARTInfo']"}), + 'model_family': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'rotation_rate': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'sata_version': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'scanned_on': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'sector_size': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'serial_number': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'supported': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'version': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'world_wide_name': ('django.db.models.fields.CharField', [], {'max_length': '64'}) + }, + 'storageadmin.smartinfo': { + 'Meta': {'object_name': 'SMARTInfo'}, + 'disk': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['storageadmin.Disk']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'toc': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}) + }, + 'storageadmin.smarttestlog': { + 'Meta': {'object_name': 'SMARTTestLog'}, + 'description': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'info': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['storageadmin.SMARTInfo']"}), + 'lba_of_first_error': ('django.db.models.fields.CharField', [], {'max_length': '1024'}), + 'lifetime_hours': ('django.db.models.fields.IntegerField', [], {}), + 'pct_completed': ('django.db.models.fields.IntegerField', [], {}), + 'status': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'test_num': ('django.db.models.fields.IntegerField', [], {}) + }, + 'storageadmin.smarttestlogdetail': { + 'Meta': {'object_name': 'SMARTTestLogDetail'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'info': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['storageadmin.SMARTInfo']"}), + 'line': ('django.db.models.fields.CharField', [], {'max_length': '128'}) + }, + 'storageadmin.snapshot': { + 'Meta': {'unique_together': "(('share', 'name'),)", 'object_name': 'Snapshot'}, + 'eusage': ('django.db.models.fields.BigIntegerField', [], {'default': '0'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '4096'}), + 'qgroup': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'real_name': ('django.db.models.fields.CharField', [], {'default': "'unknownsnap'", 'max_length': '4096'}), + 'rusage': ('django.db.models.fields.BigIntegerField', [], {'default': '0'}), + 'share': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['storageadmin.Share']"}), + 'size': ('django.db.models.fields.BigIntegerField', [], {'default': '0'}), + 'snap_type': ('django.db.models.fields.CharField', [], {'default': "'admin'", 'max_length': '64'}), + 'toc': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'uvisible': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'writable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'storageadmin.supportcase': { + 'Meta': {'object_name': 'SupportCase'}, + 'case_type': ('django.db.models.fields.CharField', [], {'max_length': '6'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'notes': ('django.db.models.fields.TextField', [], {}), + 'status': ('django.db.models.fields.CharField', [], {'max_length': '9'}), + 'zipped_log': ('django.db.models.fields.CharField', [], {'max_length': '128'}) + }, + 'storageadmin.tlscertificate': { + 'Meta': {'object_name': 'TLSCertificate'}, + 'certificate': ('django.db.models.fields.CharField', [], {'max_length': '12288', 'null': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '12288', 'null': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '1024'}) + }, + 'storageadmin.updatesubscription': { + 'Meta': {'object_name': 'UpdateSubscription'}, + 'appliance': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['storageadmin.Appliance']"}), + 'description': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}), + 'status': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'url': ('django.db.models.fields.CharField', [], {'max_length': '512'}) + }, + 'storageadmin.user': { + 'Meta': {'object_name': 'User'}, + 'admin': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'email': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}), + 'gid': ('django.db.models.fields.IntegerField', [], {'default': '5000'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['storageadmin.Group']", 'null': 'True', 'blank': 'True'}), + 'homedir': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'public_key': ('django.db.models.fields.CharField', [], {'max_length': '4096', 'null': 'True', 'blank': 'True'}), + 'shell': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True'}), + 'smb_shares': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'admin_users'", 'null': 'True', 'to': "orm['storageadmin.SambaShare']"}), + 'uid': ('django.db.models.fields.IntegerField', [], {'default': '5000'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'suser'", 'unique': 'True', 'null': 'True', 'to': u"orm['auth.User']"}), + 'username': ('django.db.models.fields.CharField', [], {'default': "''", 'unique': 'True', 'max_length': '4096'}) + } + } + + complete_apps = ['storageadmin'] \ No newline at end of file diff --git a/src/rockstor/storageadmin/models/__init__.py b/src/rockstor/storageadmin/models/__init__.py index cad3bd277..ff30c40b7 100644 --- a/src/rockstor/storageadmin/models/__init__.py +++ b/src/rockstor/storageadmin/models/__init__.py @@ -44,7 +44,8 @@ from pool_balance import PoolBalance from tls_certificate import TLSCertificate from rockon import (RockOn, DImage, DContainer, DPort, DVolume, - ContainerOption, DCustomConfig, DContainerLink) + ContainerOption, DCustomConfig, DContainerLink, + DContainerEnv) from smart import (SMARTAttribute, SMARTCapability, SMARTErrorLog, SMARTErrorLogSummary, SMARTTestLog, SMARTTestLogDetail, SMARTIdentity, SMARTInfo) diff --git a/src/rockstor/storageadmin/models/rockon.py b/src/rockstor/storageadmin/models/rockon.py index 6876c5c2b..9f93a44ec 100644 --- a/src/rockstor/storageadmin/models/rockon.py +++ b/src/rockstor/storageadmin/models/rockon.py @@ -62,6 +62,10 @@ class DContainer(models.Model): dimage = models.ForeignKey(DImage) name = models.CharField(max_length=1024, unique=True) launch_order = models.IntegerField(default=1) + #if uid is None, container's owner is not set. defaults to root. + #if it's -1, then owner is set to the owner of first volume, if any. + #if it's an integer other than -1, like 0, then owner is set to that uid. + uid = models.IntegerField(null=True) class Meta: app_label = 'storageadmin' @@ -131,3 +135,15 @@ class DCustomConfig(models.Model): class Meta: unique_together = ('rockon', 'key',) app_label = 'storageadmin' + + +class DContainerEnv(models.Model): + container = models.ForeignKey(DContainer) + key = models.CharField(max_length=1024) + val = models.CharField(max_length=1024, null=True) + description = models.CharField(max_length=2048, null=True) + label = models.CharField(max_length=64, null=True) + + class Meta: + unique_together = ('container', 'key') + app_label = 'storageadmin' From c453ea171ccd971984840b8e46b9f314171edb8d Mon Sep 17 00:00:00 2001 From: Suman Chakravartula Date: Thu, 7 Jan 2016 16:30:59 -0800 Subject: [PATCH 06/13] add container-env api. #858 --- src/rockstor/storageadmin/serializers.py | 14 +++++--- src/rockstor/storageadmin/urls/rockons.py | 4 ++- src/rockstor/storageadmin/views/__init__.py | 1 + .../storageadmin/views/rockon_environment.py | 36 +++++++++++++++++++ 4 files changed, 49 insertions(+), 6 deletions(-) create mode 100644 src/rockstor/storageadmin/views/rockon_environment.py diff --git a/src/rockstor/storageadmin/serializers.py b/src/rockstor/storageadmin/serializers.py index f93bf5b6d..092c21cdb 100644 --- a/src/rockstor/storageadmin/serializers.py +++ b/src/rockstor/storageadmin/serializers.py @@ -24,11 +24,12 @@ NFSExportGroup, SFTP, AdvancedNFSExport, OauthApp, NetatalkShare, Group, PoolBalance, SambaCustomConfig, TLSCertificate, RockOn, - DVolume, DPort, DCustomConfig, SMARTAttribute, - SMARTCapability, SMARTInfo, SMARTErrorLog, - SMARTErrorLogSummary, SMARTTestLog, - SMARTTestLogDetail, SMARTIdentity, - ConfigBackup, EmailClient, UpdateSubscription) + DVolume, DPort, DCustomConfig, DContainerEnv, + SMARTAttribute, SMARTCapability, SMARTInfo, + SMARTErrorLog, SMARTErrorLogSummary, + SMARTTestLog, SMARTTestLogDetail, + SMARTIdentity, ConfigBackup, EmailClient, + UpdateSubscription) from django.contrib.auth.models import User as DjangoUser @@ -209,6 +210,9 @@ class RockOnCustomConfigSerializer(serializers.ModelSerializer): class Meta: model = DCustomConfig +class RockOnEnvironmentSerializer(serializers.ModelSerializer): + class Meta: + model = DContainerEnv class SMARTCapabilitySerializer(serializers.ModelSerializer): class Meta: diff --git a/src/rockstor/storageadmin/urls/rockons.py b/src/rockstor/storageadmin/urls/rockons.py index 44a3ecd91..0fcc34643 100644 --- a/src/rockstor/storageadmin/urls/rockons.py +++ b/src/rockstor/storageadmin/urls/rockons.py @@ -18,7 +18,8 @@ from django.conf.urls import patterns, url from storageadmin.views import (RockOnView, RockOnIdView, RockOnVolumeView, - RockOnPortView, RockOnCustomConfigView) + RockOnPortView, RockOnCustomConfigView, + RockOnEnvironmentView) urlpatterns = patterns( '', @@ -26,6 +27,7 @@ url(r'^/volumes/(?P\d+)$', RockOnVolumeView.as_view(), ), url(r'^/ports/(?P\d+)$', RockOnPortView.as_view(), ), url(r'^/customconfig/(?P\d+)$', RockOnCustomConfigView.as_view(), ), + url(r'^/environment/(?P\d+)$', RockOnEnvironmentView.as_view(), ), url(r'^/(?Pupdate)$', RockOnView.as_view(), ), url(r'^/(?P\d+)$', RockOnIdView.as_view(), ), url(r'^/(?P\d+)/(?Pinstall|uninstall|update|start|stop|state_update|status_update)$', diff --git a/src/rockstor/storageadmin/views/__init__.py b/src/rockstor/storageadmin/views/__init__.py index 88034ad37..89fed0751 100644 --- a/src/rockstor/storageadmin/views/__init__.py +++ b/src/rockstor/storageadmin/views/__init__.py @@ -47,6 +47,7 @@ from rockon_volume import RockOnVolumeView from rockon_port import RockOnPortView from rockon_custom_config import RockOnCustomConfigView +from rockon_environment import RockOnEnvironmentView from disk_smart import DiskSMARTDetailView from config_backup import (ConfigBackupListView, ConfigBackupDetailView, ConfigBackupUpload) diff --git a/src/rockstor/storageadmin/views/rockon_environment.py b/src/rockstor/storageadmin/views/rockon_environment.py new file mode 100644 index 000000000..24048a376 --- /dev/null +++ b/src/rockstor/storageadmin/views/rockon_environment.py @@ -0,0 +1,36 @@ +""" +Copyright (c) 2012-2013 RockStor, Inc. +This file is part of RockStor. + +RockStor 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 2 of the License, +or (at your option) any later version. + +RockStor 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 . +""" + +from storageadmin.models import (RockOn, DContainer, DContainerEnv) +from storageadmin.serializers import RockOnEnvironmentSerializer +import rest_framework_custom as rfc +from storageadmin.util import handle_exception + + +class RockOnEnvironmentView(rfc.GenericView): + serializer_class = RockOnEnvironmentSerializer + + def get_queryset(self, *args, **kwargs): + try: + rockon = RockOn.objects.get(id=self.kwargs['rid']) + except: + e_msg = ('Rock-on(%s) does not exist' % self.kwargs['rid']) + handle_exception(Exception(e_msg), self.request) + + containers = DContainer.objects.filter(rockon=rockon) + return DContainerEnv.objects.filter(container__in=containers).order_by('id') From 6033b11660a99224778b73c2b8ce22b77c0351d4 Mon Sep 17 00:00:00 2001 From: Suman Chakravartula Date: Thu, 7 Jan 2016 16:32:33 -0800 Subject: [PATCH 07/13] generalize -u and env options to container. #858 Containers are run as root by default. Typically, the container profile switches to a specific user and runs processes as that user. Some containers take the uid of the container into account. Similarly, ownership of mapped Shares can be taken into account. Make this configurable in the Rock-on profile. Handle env variables generically, similar to custom config, but at the container level. To the end user it's completely transparent. --- .../storageadmin/views/rockon_helpers.py | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/rockstor/storageadmin/views/rockon_helpers.py b/src/rockstor/storageadmin/views/rockon_helpers.py index 5d5f289e9..010c6236c 100644 --- a/src/rockstor/storageadmin/views/rockon_helpers.py +++ b/src/rockstor/storageadmin/views/rockon_helpers.py @@ -29,7 +29,7 @@ from system.services import service_status from storageadmin.models import (RockOn, DContainer, DVolume, DPort, DCustomConfig, Share, Disk, DContainerLink, - ContainerOption) + ContainerOption, DContainerEnv) from fs.btrfs import mount_share from system.pkg_mgmt import install_pkg from rockon_utils import container_status @@ -183,23 +183,37 @@ def vol_ops(container): ops_list.extend(['-v', '%s:%s' % (share_mnt, v.dest_dir)]) return ops_list -def get_uid(container): +def vol_owner_uid(container): # If there are volumes, return the uid of the owner of the first volume. vo = DVolume.objects.filter(container=container).first() if (vo is None): return None share_mnt = ('%s%s' % (settings.MNT_PT, vo.share.name)) - return str(os.stat(share_mnt).st_uid) + return os.stat(share_mnt).st_uid + +def envars(container): + var_list = [] + for e in DContainerEnv.objects.filter(container=container): + var_list.extend(['-e', '%s=%s' % (e.key, e.val)]) + return var_list def generic_install(rockon): for c in DContainer.objects.filter(rockon=rockon).order_by('launch_order'): rm_container(c.name) cmd = list(DCMD2) + ['--name', c.name,] cmd.extend(vol_ops(c)) + if (c.uid is not None): + uid = c.uid + if (c.uid is -1): + uid = vol_owner_uid(c) + #@todo: what if the uid does not exist? Create a user with username=container-name? + cmd.extend(['-u', str(uid)]) cmd.extend(port_ops(c)) cmd.extend(container_ops(c)) + cmd.extend(envars(c)) cmd.append(c.dimage.name) run_command(cmd) + def openvpn_install(rockon): #volume container vol_co = DContainer.objects.get(rockon=rockon, launch_order=1) From e5e018f6561a8f6762bbc4cf2857327206e18602 Mon Sep 17 00:00:00 2001 From: Suman Chakravartula Date: Thu, 7 Jan 2016 16:38:34 -0800 Subject: [PATCH 08/13] use generic installer for transmission. #858 the only customization was env variables, which can now be handled by generic install logic. --- src/rockstor/storageadmin/views/rockon_helpers.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/rockstor/storageadmin/views/rockon_helpers.py b/src/rockstor/storageadmin/views/rockon_helpers.py index 010c6236c..2dabcb822 100644 --- a/src/rockstor/storageadmin/views/rockon_helpers.py +++ b/src/rockstor/storageadmin/views/rockon_helpers.py @@ -236,17 +236,6 @@ def openvpn_install(rockon): run_command(server_cmd) -def transmission_install(rockon): - co = DContainer.objects.get(rockon=rockon, launch_order=1) - cmd = list(DCMD2) + ['--name', co.name] - for cco in DCustomConfig.objects.filter(rockon=rockon): - cmd.extend(['-e', '%s=%s' % (cco.key, cco.val)]) - cmd.extend(vol_ops(co)) - cmd.extend(port_ops(co)) - cmd.append(co.dimage.name) - run_command(cmd) - - def owncloud_install(rockon): for c in DContainer.objects.filter(rockon=rockon).order_by('launch_order'): rm_container(c.name) From f2bf474f86e4f6a59c4128da054acd30f3deb8ce Mon Sep 17 00:00:00 2001 From: Suman Chakravartula Date: Thu, 7 Jan 2016 16:41:55 -0800 Subject: [PATCH 09/13] simplify rock-on profile update. #858 --- src/rockstor/storageadmin/views/rockon.py | 232 ++++++++++++---------- 1 file changed, 123 insertions(+), 109 deletions(-) diff --git a/src/rockstor/storageadmin/views/rockon.py b/src/rockstor/storageadmin/views/rockon.py index ecc7f271e..8146a42e6 100644 --- a/src/rockstor/storageadmin/views/rockon.py +++ b/src/rockstor/storageadmin/views/rockon.py @@ -21,7 +21,8 @@ from rest_framework.response import Response from django.db import transaction from storageadmin.models import (RockOn, DImage, DContainer, DPort, DVolume, - ContainerOption, DCustomConfig, DContainerLink) + ContainerOption, DCustomConfig, + DContainerLink, DContainerEnv) from storageadmin.serializers import RockOnSerializer from storageadmin.util import handle_exception import rest_framework_custom as rfc @@ -149,128 +150,141 @@ def _create_update_meta(self, name, r_d): if (not created): co.dimage = io co.launch_order = co_defaults['launch_order'] + if ('uid' in c_d): + co.uid = int(c_d['uid']) co.save() - ports = {} - if ('ports' in containers[c]): - ports = containers[c]['ports'] - for p in ports: - p_d = ports[p] - if ('protocol' not in p_d): - p_d['protocol'] = None - p = int(p) - po = None - if (DPort.objects.filter(containerp=p, container=co).exists()): - po = DPort.objects.get(containerp=p, container=co) - po.hostp_default = p_d['host_default'] - po.description = p_d['description'] - po.protocol = p_d['protocol'] - po.label = p_d['label'] - else: - #let's find next available default if default is already taken - def_hostp = p_d['host_default'] - while (True): - if (DPort.objects.filter(hostp=def_hostp).exists()): - def_hostp += 1 - else: - break - po = DPort(description=p_d['description'], - hostp=def_hostp, containerp=p, - hostp_default=def_hostp, - container=co, - protocol=p_d['protocol'], - label=p_d['label']) - if ('ui' in p_d): - po.uiport = p_d['ui'] - if (po.uiport): - ro.ui = True - ro.save() - po.save() + ports = containers[c].get('ports', {}) + for p in ports: + p_d = ports[p] + if ('protocol' not in p_d): + p_d['protocol'] = None + p = int(p) + po = None + if (DPort.objects.filter(containerp=p, container=co).exists()): + po = DPort.objects.get(containerp=p, container=co) + po.hostp_default = p_d['host_default'] + po.description = p_d['description'] + po.protocol = p_d['protocol'] + po.label = p_d['label'] + else: + #let's find next available default if default is already taken + def_hostp = p_d['host_default'] + while (True): + if (DPort.objects.filter(hostp=def_hostp).exists()): + def_hostp += 1 + else: + break + po = DPort(description=p_d['description'], + hostp=def_hostp, containerp=p, + hostp_default=def_hostp, + container=co, + protocol=p_d['protocol'], + label=p_d['label']) + if ('ui' in p_d): + po.uiport = p_d['ui'] + if (po.uiport): + ro.ui = True + ro.save() + po.save() ports = [int(p) for p in ports] for po in DPort.objects.filter(container=co): if (po.containerp not in ports): po.delete() - v_d = {} - if ('volumes' in c_d): - v_d = c_d['volumes'] - for v in v_d: - cv_d = v_d[v] - vo_defaults = {'description': cv_d['description'], - 'label': cv_d['label']} - vo, created = DVolume.objects.get_or_create(dest_dir=v, container=co, - defaults=vo_defaults) - if (not created): - vo.description = vo_defaults['description'] - vo.label = vo_defaults['label'] - if ('min_size' in cv_d): - vo.min_size = cv_d['min_size'] - vo.save() + v_d = c_d.get('volumes', {}) + for v in v_d: + cv_d = v_d[v] + vo_defaults = {'description': cv_d['description'], + 'label': cv_d['label']} + vo, created = DVolume.objects.get_or_create(dest_dir=v, container=co, + defaults=vo_defaults) + if (not created): + vo.description = vo_defaults['description'] + vo.label = vo_defaults['label'] + if ('min_size' in cv_d): + vo.min_size = cv_d['min_size'] + vo.save() for vo in DVolume.objects.filter(container=co): if (vo.dest_dir not in v_d): vo.delete() - if ('opts' in containers[c]): - options = containers[c]['opts'] - id_l = [] - for o in options: - #there are no unique constraints on this model, so we need this bandaid. - if (ContainerOption.objects.filter(container=co, name=o[0], val=o[1]).count() > 1): - ContainerOption.objects.filter(container=co, name=o[0], val=o[1]).delete() - oo, created = ContainerOption.objects.get_or_create(container=co, - name=o[0], - val=o[1]) - id_l.append(oo.id) - for oo in ContainerOption.objects.filter(container=co): - if (oo.id not in id_l): - oo.delete() + self._update_env(co, c_d) + options = containers[c].get('opts', []) + id_l = [] + for o in options: + #there are no unique constraints on this model, so we need this bandaid. + if (ContainerOption.objects.filter(container=co, name=o[0], val=o[1]).count() > 1): + ContainerOption.objects.filter(container=co, name=o[0], val=o[1]).delete() + oo, created = ContainerOption.objects.get_or_create(container=co, + name=o[0], + val=o[1]) + id_l.append(oo.id) + for oo in ContainerOption.objects.filter(container=co): + if (oo.id not in id_l): + oo.delete() - if ('container_links' in r_d): - l_d = r_d['container_links'] - for cname in l_d: - ll = l_d[cname] - lsources = [l['source_container'] for l in ll] - co = DContainer.objects.get(rockon=ro, name=cname) - for clo in co.destination_container.all(): - if (clo.name not in lsources): - clo.delete() - for cl_d in ll: - sco = DContainer.objects.get(rockon=ro, name=cl_d['source_container']) - clo, created = DContainerLink.objects.get_or_create(source=sco, - destination=co) - clo.name = cl_d['name'] - clo.save() + l_d = r_d.get('container_links', {}) + for cname in l_d: + ll = l_d[cname] + lsources = [l['source_container'] for l in ll] + co = DContainer.objects.get(rockon=ro, name=cname) + for clo in co.destination_container.all(): + if (clo.name not in lsources): + clo.delete() + for cl_d in ll: + sco = DContainer.objects.get(rockon=ro, name=cl_d['source_container']) + clo, created = DContainerLink.objects.get_or_create(source=sco, + destination=co) + clo.name = cl_d['name'] + clo.save() + self._update_cc(ro, r_d) - cc_d = {} - if ('custom_config' in r_d): - cc_d = r_d['custom_config'] - sorted_keys = [''] * len(cc_d.keys()) - for k in cc_d: - ccc_d = cc_d[k] - idx = ccc_d.get('index', 0) - if (idx == 0): - for i in range(len(sorted_keys)): - if (sorted_keys[i] == ''): - sorted_keys[i] = k - break - else: - sorted_keys[idx-1] = k - for k in sorted_keys: - ccc_d = cc_d[k] - cco, created = DCustomConfig.objects.get_or_create( - rockon=ro, key=k, - defaults={'description': ccc_d['description'], 'label': ccc_d['label']}) - if (not created): - cco.description = ccc_d['description'] - cco.label = ccc_d['label'] - cco.save() - def_val = ccc_d.get('default') - if (def_val is not None): - cco.val = def_val - cco.save() + + def _sorted_keys(self, cd): + sorted_keys = [''] * len(cd.keys()) + for k in cd: + ccd = cd[k] + idx = ccd.get('index', 0) + if (idx == 0): + for i in range(len(sorted_keys)): + if (sorted_keys[i] == ''): + sorted_keys[i] = k + break + else: + sorted_keys[idx-1] = k + return sorted_keys + + def _update_model(self, modelinst, ad): + for k,v in ad.iteritems(): + setattr(modelinst, k, v) + modelinst.save() + + def _update_cc(self, ro, r_d): + cc_d = r_d.get('custom_config', {}) + for k in self._sorted_keys(cc_d): + ccc_d = cc_d[k] + defaults = {'description': ccc_d['description'], + 'label': ccc_d['label'], + 'val': ccc_d.get('val', None), } + cco, created = DCustomConfig.objects.get_or_create( + rockon=ro, key=k, defaults=defaults) + if (not created): self._update_model(cco, defaults) for cco in DCustomConfig.objects.filter(rockon=ro): - if (cco.key not in cc_d): - cco.delete() + if (cco.key not in cc_d): cco.delete() + + def _update_env(self, co, c_d): + cc_d = c_d.get('environment', {}) + for k in self._sorted_keys(cc_d): + ccc_d = cc_d[k] + defaults = {'description': ccc_d['description'], + 'label': ccc_d['label'], + 'val': ccc_d.get('val', None), } + cco, created = DContainerEnv.objects.get_or_create( + container=co, key=k, defaults=defaults) + if (not created): self._update_model(cco, defaults) + for eo in DContainerEnv.objects.filter(container=co): + if (eo.key not in cc_d): eo.delete() def _get_available(self, request): msg = ('Network error while checking for updates. ' From 3ed8b7868fe949bea846a4db342c8c1609c70857 Mon Sep 17 00:00:00 2001 From: Suman Chakravartula Date: Thu, 7 Jan 2016 16:42:33 -0800 Subject: [PATCH 10/13] accept env vars from the user for install. #858 --- src/rockstor/storageadmin/views/rockon_id.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/rockstor/storageadmin/views/rockon_id.py b/src/rockstor/storageadmin/views/rockon_id.py index e8b427715..336f482b0 100644 --- a/src/rockstor/storageadmin/views/rockon_id.py +++ b/src/rockstor/storageadmin/views/rockon_id.py @@ -21,7 +21,7 @@ from django.db import transaction from django.db.models import Q from storageadmin.models import (RockOn, DContainer, DVolume, Share, DPort, - DCustomConfig) + DCustomConfig, DContainerEnv) from storageadmin.serializers import RockOnSerializer import rest_framework_custom as rfc from storageadmin.util import handle_exception @@ -82,6 +82,7 @@ def post(self, request, rid, command): share_map = request.data.get('shares', {}) port_map = request.data.get('ports', {}) cc_map = request.data.get('cc', {}) + env_map = request.data.get('environment', {}) containers = DContainer.objects.filter(rockon=rockon) for co in containers: for s in share_map.keys(): @@ -118,6 +119,13 @@ def post(self, request, rid, command): cco = DCustomConfig.objects.get(rockon=rockon, key=c) cco.val = cc_map[c] cco.save() + for e in env_map.keys(): + if (not DContainerEnv.objects.filter(container=co, key=e).exists()): + e_msg = ('Invalid environment variabled(%s)' % e) + handle_exception(Exception(e_msg), request) + ceo = DContainerEnv.objects.get(container=co, key=e) + ceo.val = env_map[e] + ceo.save() install.async(rockon.id) rockon.state = 'pending_install' rockon.save() From cfcb21d585fa0d5c9470b9e81fc218152dcf9626 Mon Sep 17 00:00:00 2001 From: Suman Chakravartula Date: Sat, 9 Jan 2016 13:20:39 -0800 Subject: [PATCH 11/13] merge with master. #858 --- .../static/storageadmin/js/models/models.js | 21 + .../static/storageadmin/js/views/rockons.js | 2049 +++++++++-------- 2 files changed, 1073 insertions(+), 997 deletions(-) diff --git a/src/rockstor/storageadmin/static/storageadmin/js/models/models.js b/src/rockstor/storageadmin/static/storageadmin/js/models/models.js index 171568d5b..4fcb54d49 100644 --- a/src/rockstor/storageadmin/static/storageadmin/js/models/models.js +++ b/src/rockstor/storageadmin/static/storageadmin/js/models/models.js @@ -642,6 +642,27 @@ var RockOnCustomConfigCollection = RockStorPaginatedCollection.extend({ } }); +var RockOnEnvironment = Backbone.Model.extend({ + urlRoot: '/api/rockon/environment/' + this.rid +}); + +var RockOnEnvironmentCollection = RockStorPaginatedCollection.extend({ + model: RockOnEnvironment, + initialize: function(models, options) { + this.constructor.__super__.initialize.apply(this, arguments); + if (options) { + this.rid = options.rid; + } + }, + baseUrl: function() { + if (this.rid) { + return '/api/rockons/environment/' + this.rid; + } else { + return '/api/rockons/environment'; + } + } +}); + var EmailAccount = Backbone.Model.extend({ urlRoot: '/api/email' }); diff --git a/src/rockstor/storageadmin/static/storageadmin/js/views/rockons.js b/src/rockstor/storageadmin/static/storageadmin/js/views/rockons.js index 21e558055..34ccde326 100644 --- a/src/rockstor/storageadmin/static/storageadmin/js/views/rockons.js +++ b/src/rockstor/storageadmin/static/storageadmin/js/views/rockons.js @@ -27,1066 +27,1121 @@ RockonsView = RockstorLayoutView.extend({ - initialize: function() { - this.constructor.__super__.initialize.apply(this, arguments); - this.template = window.JST.rockons_rockons; - this.rockons = new RockOnCollection({}); - this.service = new Service({ name: 'docker' }); - this.dependencies.push(this.rockons, this.service); - this.updateFreq = 15000; - this.defTab = 0; - this.initHandlebarHelpers(); - }, - - events: { - 'switchChange.bootstrapSwitch': 'rockonToggle', - 'click #js-install-rockon': 'installRockon', - 'click #js-uninstall-rockon': 'uninstallRockon', - 'click #js-rockons-installed': 'installedRockons', - 'click #js-update-rockons': 'updateRockons', - 'click #js-rockon-settings': 'rockonSettings', - 'click #js-rockon-info': 'rockonInfo' - }, - - render: function() { - this.service.fetch(); - this.rockons.fetch(); - this.updateStatus(); - - return this; - }, - - renderRockons: function() { - var _this = this; - - var ui_map = {}; - var uis = this.rockons.filter(function(rockon) { - ui_map[rockon.get('id')] = null; - if (rockon.get('ui')) { - var protocol = "http://"; - if (rockon.get('https')) { - protocol = "https://"; - } - var ui_link = protocol + window.location.hostname; - if (rockon.get('ui_port')) { - ui_link += ":" + rockon.get('ui_port'); - } - if (rockon.get('link')) { - ui_link += "/" + rockon.get('link'); - } - ui_map[rockon.get('id')] = ui_link; - } - return false; - }); - - $(this.el).html(this.template({ - rockons: this.rockons, - status: this.service.get('status'), - ui_map: ui_map - })); - - if (!this.dockerServiceView) { - this.dockerServiceView = new DockerServiceView({ - parentView: this, - dockerService: this.dockerService - }); + initialize: function() { + this.constructor.__super__.initialize.apply(this, arguments); + this.template = window.JST.rockons_rockons; + this.rockons = new RockOnCollection({}); + this.service = new Service({ name: 'docker' }); + this.dependencies.push(this.rockons, this.service); + this.updateFreq = 15000; + this.defTab = 0; + this.initHandlebarHelpers(); + }, + + events: { + 'switchChange.bootstrapSwitch': 'rockonToggle', + 'click #js-install-rockon': 'installRockon', + 'click #js-uninstall-rockon': 'uninstallRockon', + 'click #js-rockons-installed': 'installedRockons', + 'click #js-update-rockons': 'updateRockons', + 'click #js-rockon-settings': 'rockonSettings', + 'click #js-rockon-info': 'rockonInfo' + }, + + render: function() { + this.service.fetch(); + this.rockons.fetch(); + this.updateStatus(); + + return this; + }, + + renderRockons: function() { + var _this = this; + + var ui_map = {}; + var uis = this.rockons.filter(function(rockon) { + ui_map[rockon.get('id')] = null; + if (rockon.get('ui')) { + var protocol = "http://"; + if (rockon.get('https')) { + protocol = "https://"; } - // Render the Rockons template with a status describing whether - // the Rockons service has been enabled - - $('#docker-service-ph').append(this.dockerServiceView.render().el); - - $('#install-rockon-overlay').overlay({load: false}); - this.$("ul.nav.nav-tabs").tabs("div.css-panes > div"); - this.$('.nav-tabs li:eq(' + this.defTab + ') a').click(); - - //initalize bootstrap switch - this.$("[type='checkbox']").bootstrapSwitch(); - this.$("[type='checkbox']").bootstrapSwitch('onColor','success'); //left side text color - this.$("[type='checkbox']").bootstrapSwitch('offColor','danger'); //right side text color - }, - - installRockon: function(event) { - var _this = this; - this.defTab = 0; - event.preventDefault(); - var button = $(event.currentTarget); - var rockon_id = button.attr('data-name'); - var rockon_o = _this.rockons.get(rockon_id); - var wizardView = new RockonInstallWizardView({ - model: new Backbone.Model({ rockon: rockon_o }), - title: rockon_o.get('name') + ' install wizard', - parent: this - }); - $('.overlay-content', '#install-rockon-overlay').html(wizardView.render().el); - $('#install-rockon-overlay').overlay().load(); - }, - - uninstallRockon: function(event) { - var _this = this; - event.preventDefault(); - var button = $(event.currentTarget); - if (buttonDisabled(button)) return false; - var rockon_id = button.attr('data-name'); - var rockon_o = _this.rockons.get(rockon_id); - if (confirm("Are you sure you want to uninstall this Rock-on (" + rockon_o.get('name') + ")?")) { - disableButton(button); - $.ajax({ - url: '/api/rockons/' + rockon_id + '/uninstall', - type: 'POST', - dataType: 'json', - success: function() { - _this.defTab = 0; - _this.render(); - enableButton(button); - }, - error: function(xhr, status, error) { - enableButton(button); - } - }); - } - }, - - updateRockons: function(event) { - var _this = this; - event.preventDefault(); - var button = $(event.currentTarget); - if (buttonDisabled(button)) return false; - disableButton(button); - $.ajax({ - url: '/api/rockons/update', - type: 'POST', - dataType: 'json', - success: function() { - _this.defTab = 1; - _this.render(); - enableButton(button); - }, - error: function(xhr, status, error) { - enableButton(button); - } - }); - }, - - rockonSettings: function(event) { - var _this = this; - event.preventDefault(); - var rockon_id = _this.getRockonId(event); - var rockon_o = _this.rockons.get(rockon_id); - _this.stopPolling(); - var wizardView = new RockonSettingsWizardView({ - model: new Backbone.Model({ rockon: rockon_o}), - title: rockon_o.get('name') + ' Settings', - parent: this - }); - $('.overlay-content', '#install-rockon-overlay').html(wizardView.render().el); - $('#install-rockon-overlay').overlay().load(); - }, - - rockonInfo: function(event) { - var _this = this; - event.preventDefault(); - var rockon_id = _this.getRockonId(event); - var rockon_o = _this.rockons.get(rockon_id); - _this.stopPolling(); - var infoView = new RockonInfoView({ - model: new Backbone.Model({ rockon: rockon_o}), - title: 'Additional information about ' + rockon_o.get('name') + ' Rock-on', - parent: this - }); - $('.overlay-content', '#install-rockon-overlay').html(infoView.render().el); - $('#install-rockon-overlay').overlay().load(); - }, - - getRockonId: function(event) { - var slider = $(event.currentTarget); - return slider.attr('data-rockon-id'); - }, - - rockonToggle: function(event,state){ - var rockonId = $(event.target).attr('data-rockon-id'); - if(state){ - this.startRockon(rockonId); - }else{ - this.stopRockon(rockonId); + var ui_link = protocol + window.location.hostname; + if (rockon.get('ui_port')) { + ui_link += ":" + rockon.get('ui_port'); } - }, - - startRockon: function(rockonId) { - console.log(event); - var _this = this; - this.stopPolling(); - $.ajax({ - url: '/api/rockons/' + rockonId + '/start', - type: 'POST', - dataType: 'json', - success: function(data, status, xhr) { - _this.defTab = 0; - _this.updateStatus(); - }, - error: function(data, status, xhr) { - console.log('error while starting rockon'); - } - }); - }, - - stopRockon: function(rockonId) { - var _this = this; - this.stopPolling(); - $.ajax({ - url: '/api/rockons/' + rockonId + '/stop', - type: 'POST', - dataType: 'json', - success: function(data, status, xhr) { - _this.defTab = 0; - _this.updateStatus(); - }, - error: function(data, status, xhr) { - console.log('error while stopping rockon'); - } - }); - }, - - pendingOps: function() { - var pending = this.rockons.find(function(rockon) { - if ((rockon.get('status').search('pending') != -1) || (rockon.get('state').search('pending') != -1)) { - return true; - } - }); - if (pending) { return true; } - return false; - }, - - updateStatus: function() { - var _this = this; - _this.startTime = new Date().getTime(); - _this.rockons.fetch({ - silent: true, - success: function(data, response, options) { - _this.renderRockons(); - if (_this.pendingOps()) { - var ct = new Date().getTime(); - var diff = ct - _this.startTime; - if (diff > _this.updateFreq) { - _this.updateStatus(); - } else { - _this.timeoutId = window.setTimeout( function() { - _this.updateStatus(); - }, _this.updateFreq - diff); - } - } else { - _this.stopPolling(); - } - } - }); - }, - - stopPolling: function() { - if (!_.isUndefined(this.timeoutId)) { - window.clearInterval(this.timeoutId); + if (rockon.get('link')) { + ui_link += "/" + rockon.get('link'); } - }, - - installedRockons: function(event) { - if (this.pendingOps()) { - this.updateStatus(); + ui_map[rockon.get('id')] = ui_link; + } + return false; + }); + + $(this.el).html(this.template({ + rockons: this.rockons, + status: this.service.get('status'), + ui_map: ui_map + })); + + if (!this.dockerServiceView) { + this.dockerServiceView = new DockerServiceView({ + parentView: this, + dockerService: this.dockerService + }); + } + // Render the Rockons template with a status describing whether + // the Rockons service has been enabled + + $('#docker-service-ph').append(this.dockerServiceView.render().el); + + $('#install-rockon-overlay').overlay({load: false}); + this.$("ul.nav.nav-tabs").tabs("div.css-panes > div"); + this.$('.nav-tabs li:eq(' + this.defTab + ') a').click(); + + //initalize bootstrap switch + this.$("[type='checkbox']").bootstrapSwitch(); + this.$("[type='checkbox']").bootstrapSwitch('onColor','success'); //left side text color + this.$("[type='checkbox']").bootstrapSwitch('offColor','danger'); //right side text color + }, + + installRockon: function(event) { + var _this = this; + this.defTab = 0; + event.preventDefault(); + var button = $(event.currentTarget); + var rockon_id = button.attr('data-name'); + var rockon_o = _this.rockons.get(rockon_id); + var wizardView = new RockonInstallWizardView({ + model: new Backbone.Model({ rockon: rockon_o }), + title: rockon_o.get('name') + ' install wizard', + parent: this + }); + $('.overlay-content', '#install-rockon-overlay').html(wizardView.render().el); + $('#install-rockon-overlay').overlay().load(); + }, + + uninstallRockon: function(event) { + var _this = this; + event.preventDefault(); + var button = $(event.currentTarget); + if (buttonDisabled(button)) return false; + var rockon_id = button.attr('data-name'); + var rockon_o = _this.rockons.get(rockon_id); + if (confirm("Are you sure you want to uninstall this Rock-on (" + rockon_o.get('name') + ")?")) { + disableButton(button); + $.ajax({ + url: '/api/rockons/' + rockon_id + '/uninstall', + type: 'POST', + dataType: 'json', + success: function() { + _this.defTab = 0; + _this.render(); + enableButton(button); + }, + error: function(xhr, status, error) { + enableButton(button); } - }, - - initHandlebarHelpers: function(){ - Handlebars.registerHelper('display_installedRockons', function(){ - var html = ''; - _this = this; - var installed = 0; - this.rockons.each(function(rockon, index) { - if (rockon.get('state') == 'installed' || rockon.get('state').match('pending')) { - installed += 1; - html += '
'; - if (rockon.get('state').search('pending') > -1 || rockon.get('status').search('pending') > -1) { - var text = 'Installing ...'; - if (rockon.get('state') == 'pending_uninstall') { - text = 'Uninstalling ...'; - } else if (rockon.get('status') == 'pending_start') { - text = 'Starting ...'; - } else if (rockon.get('status') == 'pending_stop') { - text = 'Stopping ...'; - } - html += '
'; - html += '
'; - html += ''; - html += '
'; - html += '

' + text + '

'; - html += '
'; - html += '
'; - html += '
'; - } - html += '
'; - html += '
'; - html += '

' + rockon.get('name') + '

'; - html += '

' + rockon.get('description') + '

'; - html += '

Current status: ' + rockon.get('status') + '

'; - html += '
'; - html += '
'; - html += '
'; - if (rockon.get('state') == 'installed' && !rockon.get('status').match('pending')) { - if (rockon.get('status') == 'started') { - html += ''; - } else { - html += ''; - } - html += '  '; - if (rockon.get('more_info')) { - html += ''; - } - html += '

'; - if (ui_map[rockon.get('id')]) { - if (rockon.get('status') == 'started') { - html += '' + rockon.get('name') + ' UI'; - } else { - html += '' + rockon.get('name') + ' UI'; - } - } - if (rockon.get('status') != 'started') { - html += 'Uninstall'; - } - - } - html += '
'; - html += '
'; - html += '
'; - } - }); - if (installed == 0) { - html += '
'; - html += '
'; - html += '
'; - html += '

There are no Rock-ons installed currently.

'; - html += '
'; - html += '
'; - html += '
'; - } - return new Handlebars.SafeString(html); - }); - - Handlebars.registerHelper('display_allRockons', function(){ - var html = ''; - var all = 0; - this.rockons.each(function(rockon, index) { - if (rockon.get('state') == 'available' || rockon.get('state') == 'install_failed') { - all += 1; - html += '
'; - html += '
'; - html += '
'; - html += '

' + rockon.get('name') + '

'; - html += '

' + rockon.get('description') + '

'; - if (rockon.get('state') == 'install_failed') { - html += 'Failed to install in the previous attempt. Here\'s how you can proceed.'; - html += '
    '; - html += '
  • Check logs in /opt/rockstor/var/log for clues.
  • '; - html += '
  • Install again.
  • '; - html += '
  • If the problem persists, post on the Forum or email support@rockstor.com
  • '; - html += '
'; - } - html += 'Install'; - html += '
'; - html += '
'; - html += '
'; - } - }); - if (all == 0) { - html += '
'; - html += '
'; - html += '
'; - html += '

Click on Update button to check for new Rock-ons.

'; - html += '
'; - html += '
'; - html += '
'; - } - return new Handlebars.SafeString(html); - }); + }); } + }, + + updateRockons: function(event) { + var _this = this; + event.preventDefault(); + var button = $(event.currentTarget); + if (buttonDisabled(button)) return false; + disableButton(button); + $.ajax({ + url: '/api/rockons/update', + type: 'POST', + dataType: 'json', + success: function() { + _this.defTab = 1; + _this.render(); + enableButton(button); + }, + error: function(xhr, status, error) { + enableButton(button); + } + }); + }, + + rockonSettings: function(event) { + var _this = this; + event.preventDefault(); + var rockon_id = _this.getRockonId(event); + var rockon_o = _this.rockons.get(rockon_id); + _this.stopPolling(); + var wizardView = new RockonSettingsWizardView({ + model: new Backbone.Model({ rockon: rockon_o}), + title: rockon_o.get('name') + ' Settings', + parent: this + }); + $('.overlay-content', '#install-rockon-overlay').html(wizardView.render().el); + $('#install-rockon-overlay').overlay().load(); + }, + + rockonInfo: function(event) { + var _this = this; + event.preventDefault(); + var rockon_id = _this.getRockonId(event); + var rockon_o = _this.rockons.get(rockon_id); + _this.stopPolling(); + var infoView = new RockonInfoView({ + model: new Backbone.Model({ rockon: rockon_o}), + title: 'Additional information about ' + rockon_o.get('name') + ' Rock-on', + parent: this + }); + $('.overlay-content', '#install-rockon-overlay').html(infoView.render().el); + $('#install-rockon-overlay').overlay().load(); + }, + + getRockonId: function(event) { + var slider = $(event.currentTarget); + return slider.attr('data-rockon-id'); + }, + + rockonToggle: function(event,state){ + var rockonId = $(event.target).attr('data-rockon-id'); + if(state){ + this.startRockon(rockonId); + }else{ + this.stopRockon(rockonId); + } + }, + + startRockon: function(rockonId) { + console.log(event); + var _this = this; + this.stopPolling(); + $.ajax({ + url: '/api/rockons/' + rockonId + '/start', + type: 'POST', + dataType: 'json', + success: function(data, status, xhr) { + _this.defTab = 0; + _this.updateStatus(); + }, + error: function(data, status, xhr) { + console.log('error while starting rockon'); + } + }); + }, + + stopRockon: function(rockonId) { + var _this = this; + this.stopPolling(); + $.ajax({ + url: '/api/rockons/' + rockonId + '/stop', + type: 'POST', + dataType: 'json', + success: function(data, status, xhr) { + _this.defTab = 0; + _this.updateStatus(); + }, + error: function(data, status, xhr) { + console.log('error while stopping rockon'); + } + }); + }, + + pendingOps: function() { + var pending = this.rockons.find(function(rockon) { + if ((rockon.get('status').search('pending') != -1) || (rockon.get('state').search('pending') != -1)) { + return true; + } + }); + if (pending) { return true; } + return false; + }, + + updateStatus: function() { + var _this = this; + _this.startTime = new Date().getTime(); + _this.rockons.fetch({ + silent: true, + success: function(data, response, options) { + _this.renderRockons(); + if (_this.pendingOps()) { + var ct = new Date().getTime(); + var diff = ct - _this.startTime; + if (diff > _this.updateFreq) { + _this.updateStatus(); + } else { + _this.timeoutId = window.setTimeout( function() { + _this.updateStatus(); + }, _this.updateFreq - diff); + } + } else { + _this.stopPolling(); + } + } + }); + }, -}); - + stopPolling: function() { + if (!_.isUndefined(this.timeoutId)) { + window.clearInterval(this.timeoutId); + } + }, -RockonInstallWizardView = WizardView.extend({ - initialize: function() { - WizardView.prototype.initialize.apply(this, arguments); - this.pages = []; - this.rockon = this.model.get('rockon'); - this.volumes = new RockOnVolumeCollection(null, {rid: this.rockon.id}); - this.ports = new RockOnPortCollection(null, {rid: this.rockon.id}); - this.custom_config = new RockOnCustomConfigCollection(null, {rid: this.rockon.id}); - }, - - fetchVolumes: function() { - var _this = this; - this.volumes.fetch({ - success: function () { - _this.model.set('volumes', _this.volumes); - _this.fetchPorts(); + installedRockons: function(event) { + if (this.pendingOps()) { + this.updateStatus(); + } + }, + + initHandlebarHelpers: function(){ + Handlebars.registerHelper('display_installedRockons', function(){ + var html = ''; + _this = this; + var installed = 0; + this.rockons.each(function(rockon, index) { + if (rockon.get('state') == 'installed' || rockon.get('state').match('pending')) { + installed += 1; + html += '
'; + if (rockon.get('state').search('pending') > -1 || rockon.get('status').search('pending') > -1) { + var text = 'Installing ...'; + if (rockon.get('state') == 'pending_uninstall') { + text = 'Uninstalling ...'; + } else if (rockon.get('status') == 'pending_start') { + text = 'Starting ...'; + } else if (rockon.get('status') == 'pending_stop') { + text = 'Stopping ...'; } - }); - }, - - fetchPorts: function() { - var _this = this; - this.ports.fetch({ - success: function() { - _this.model.set('ports', _this.ports); - _this.fetchCustomConfig(); + html += '
'; + html += '
'; + html += ''; + html += '
'; + html += '

' + text + '

'; + html += '
'; + html += '
'; + html += '
'; + } + html += '
'; + html += '
'; + html += '

' + rockon.get('name') + '

'; + html += '

' + rockon.get('description') + '

'; + html += '

Current status: ' + rockon.get('status') + '

'; + html += '
'; + html += '
'; + html += '
'; + if (rockon.get('state') == 'installed' && !rockon.get('status').match('pending')) { + if (rockon.get('status') == 'started') { + html += ''; + } else { + html += ''; } - }); - }, - - fetchCustomConfig: function() { - var _this = this; - this.custom_config.fetch({ - success: function() { - _this.model.set('custom_config', _this.custom_config); - _this.addPages(); + html += '  '; + if (rockon.get('more_info')) { + html += ''; + } + html += '

'; + if (_this.ui_map[rockon.get('id')]) { + if (rockon.get('status') == 'started') { + html += '' + rockon.get('name') + ' UI'; + } else { + html += '' + rockon.get('name') + ' UI'; + } + } + if (rockon.get('status') != 'started') { + html += 'Uninstall'; } - }); - }, - - render: function() { - this.fetchVolumes(); - return this; - }, - addPages: function() { - if (this.volumes.length > 0) { - this.pages.push(RockonShareChoice); + } + html += '
'; + html += '
'; + html += '
'; } - if (this.ports.length > 0) { - this.pages.push(RockonPortChoice); + }); + if (installed == 0) { + html += '
'; + html += '
'; + html += '
'; + html += '

There are no Rock-ons installed currently.

'; + html += '
'; + html += '
'; + html += '
'; + } + return new Handlebars.SafeString(html); + }); + + Handlebars.registerHelper('display_allRockons', function(){ + var html = ''; + var all = 0; + this.rockons.each(function(rockon, index) { + if (rockon.get('state') == 'available' || rockon.get('state') == 'install_failed') { + all += 1; + html += '
'; + html += '
'; + html += '
'; + html += '

' + rockon.get('name') + '

'; + html += '

' + rockon.get('description') + '

'; + if (rockon.get('state') == 'install_failed') { + html += 'Failed to install in the previous attempt. Here\'s how you can proceed.'; + html += '
    '; + html += '
  • Check logs in /opt/rockstor/var/log for clues.
  • '; + html += '
  • Install again.
  • '; + html += '
  • If the problem persists, post on the Forum or email support@rockstor.com
  • '; + html += '
'; + } + html += 'Install'; + html += '
'; + html += '
'; + html += '
'; } - if (this.custom_config.length > 0) { - this.pages.push(RockonCustomChoice); - } - this.pages.push.apply(this.pages, [RockonInstallSummary, RockonInstallComplete]); - WizardView.prototype.render.apply(this, arguments); - return this; - }, - - setCurrentPage: function() { - this.currentPage = new this.pages[this.currentPageNum]({ - model: this.model, - parent: this, - evAgg: this.evAgg - }); - }, - - modifyButtonText: function() { - if (this.currentPageNum == (this.pages.length - 2)) { - this.$('#next-page').html('Submit'); - } else if (this.currentPageNum == (this.pages.length - 1)) { - this.$('#prev-page').hide(); - this.$('#next-page').html('Close'); - } else if (this.currentPageNum == 0) { - this.$('#prev-page').hide(); - } else { - this.$('#prev-page').show(); - this.$('#next-page').html('Next'); - this.$('#ph-wizard-buttons').show(); - } - }, + }); + if (all == 0) { + html += '
'; + html += '
'; + html += '
'; + html += '

Click on Update button to check for new Rock-ons.

'; + html += '
'; + html += '
'; + html += '
'; + } + return new Handlebars.SafeString(html); + }); + } + +}); - lastPage: function() { - return ((this.pages.length > 1) - && ((this.pages.length-1) == this.currentPageNum)); - }, - finish: function() { - this.parent.$('#install-rockon-overlay').overlay().close(); - this.parent.render(); +RockonInstallWizardView = WizardView.extend({ + initialize: function() { + WizardView.prototype.initialize.apply(this, arguments); + this.pages = []; + this.rockon = this.model.get('rockon'); + this.volumes = new RockOnVolumeCollection(null, {rid: this.rockon.id}); + this.ports = new RockOnPortCollection(null, {rid: this.rockon.id}); + this.custom_config = new RockOnCustomConfigCollection(null, {rid: this.rockon.id}); + this.environment = new RockOnEnvironmentCollection(null, {rid: this.rockon.id}); + }, + + fetchVolumes: function() { + var _this = this; + this.volumes.fetch({ + success: function () { + _this.model.set('volumes', _this.volumes); + _this.fetchPorts(); + } + }); + }, + + fetchPorts: function() { + var _this = this; + this.ports.fetch({ + success: function() { + _this.model.set('ports', _this.ports); + _this.fetchCustomConfig(); + } + }); + }, + + fetchCustomConfig: function() { + var _this = this; + this.custom_config.fetch({ + success: function() { + _this.model.set('custom_config', _this.custom_config); + _this.fetchEnvironment(); + } + }); + }, + + fetchEnvironment: function() { + var _this = this; + this.environment.fetch({ + success: function() { + _this.model.set('environment', _this.environment); + _this.addPages(); + } + }); + }, + + render: function() { + this.fetchVolumes(); + return this; + }, + + addPages: function() { + if (this.volumes.length > 0) { + this.pages.push(RockonShareChoice); } + if (this.ports.length > 0) { + this.pages.push(RockonPortChoice); + } + if (this.environment.length > 0) { + this.pages.push(RockonEnvironment); + } + if (this.custom_config.length > 0) { + this.pages.push(RockonCustomChoice); + } + this.pages.push.apply(this.pages, [RockonInstallSummary, RockonInstallComplete]); + WizardView.prototype.render.apply(this, arguments); + return this; + }, + + setCurrentPage: function() { + this.currentPage = new this.pages[this.currentPageNum]({ + model: this.model, + parent: this, + evAgg: this.evAgg + }); + }, + + modifyButtonText: function() { + if (this.currentPageNum == (this.pages.length - 2)) { + this.$('#next-page').html('Submit'); + } else if (this.currentPageNum == (this.pages.length - 1)) { + this.$('#prev-page').hide(); + this.$('#next-page').html('Close'); + } else if (this.currentPageNum == 0) { + this.$('#prev-page').hide(); + } else { + this.$('#prev-page').show(); + this.$('#next-page').html('Next'); + this.$('#ph-wizard-buttons').show(); + } + }, + lastPage: function() { + return ((this.pages.length > 1) + && ((this.pages.length-1) == this.currentPageNum)); + }, + + finish: function() { + this.parent.$('#install-rockon-overlay').overlay().close(); + this.parent.render(); + } }); RockonShareChoice = RockstorWizardPage.extend({ - initialize: function() { - this.template = window.JST.rockons_install_choice; - this.vol_template = window.JST.rockons_vol_form; - this.rockon = this.model.get('rockon'); - this.volumes = this.model.get('volumes'); - this.shares = new ShareCollection(); - this.shares.setPageSize(100); - RockstorWizardPage.prototype.initialize.apply(this, arguments); - this.shares.on('reset', this.renderVolumes, this); - this.initHandlebarHelpers(); - }, - - render: function() { - RockstorWizardPage.prototype.render.apply(this, arguments); - this.shares.fetch(); - return this; - }, - - renderVolumes: function() { - this.$('#ph-vols-table').html(this.vol_template({volumes: this.volumes, shares: this.shares})); - //form validation - this.volForm = this.$('#vol-select-form'); - var rules = {}; - var messages = {}; - this.volumes.each(function(volume) { - rules[volume.id] = { required: true }; - messages[volume.id] = "Please read the tooltip and make the right selection"; - }); - this.validator = this.volForm.validate({ - rules: rules, - messages: messages - }); - }, - - save: function() { - - // Validate the form - if (!this.volForm.valid()) { - this.validator.showErrors(); - return $.Deferred().reject(); - } + initialize: function() { + this.template = window.JST.rockons_install_choice; + this.vol_template = window.JST.rockons_vol_form; + this.rockon = this.model.get('rockon'); + this.volumes = this.model.get('volumes'); + this.shares = new ShareCollection(); + this.shares.setPageSize(100); + RockstorWizardPage.prototype.initialize.apply(this, arguments); + this.shares.on('reset', this.renderVolumes, this); + this.initHandlebarHelpers(); + }, + + render: function() { + RockstorWizardPage.prototype.render.apply(this, arguments); + this.shares.fetch(); + return this; + }, + + renderVolumes: function() { + this.$('#ph-vols-table').html(this.vol_template({volumes: this.volumes, shares: this.shares})); + //form validation + this.volForm = this.$('#vol-select-form'); + var rules = {}; + var messages = {}; + this.volumes.each(function(volume) { + rules[volume.id] = { required: true }; + messages[volume.id] = "Please read the tooltip and make the right selection"; + }); + this.validator = this.volForm.validate({ + rules: rules, + messages: messages + }); + }, + + save: function() { + + // Validate the form + if (!this.volForm.valid()) { + this.validator.showErrors(); + return $.Deferred().reject(); + } - var share_map = {}; - var volumes = this.volumes.filter(function(volume) { - share_map[volume.get('dest_dir')] = this.$('#' + volume.id).val(); - return volume; - }, this); - this.model.set('share_map', share_map); - return $.Deferred().resolve(); - }, - - initHandlebarHelpers: function(){ - Handlebars.registerHelper('display_volumesForm', function(){ - var html = ''; - var _this = this; - this.volumes.each(function(volume, index) { - html += '
'; - html += ''; - html += '
'; - html += ''; - html += '
'; - html += ''; - html += '
'; - }); - return new Handlebars.SafeString(html); + var share_map = {}; + var volumes = this.volumes.filter(function(volume) { + share_map[volume.get('dest_dir')] = this.$('#' + volume.id).val(); + return volume; + }, this); + this.model.set('share_map', share_map); + return $.Deferred().resolve(); + }, + + initHandlebarHelpers: function(){ + Handlebars.registerHelper('display_volumesForm', function(){ + var html = ''; + var _this = this; + this.volumes.each(function(volume, index) { + html += '
'; + html += ''; + html += '
'; + html += ''; + html += '
'; + html += ''; + html += '
'; + }); + return new Handlebars.SafeString(html); + }); + } }); RockonPortChoice = RockstorWizardPage.extend({ - initialize: function() { - this.template = window.JST.rockons_port_choice; - this.port_template = window.JST.rockons_ports_form; - this.ports = this.model.get('ports'); - RockstorWizardPage.prototype.initialize.apply(this, arguments); - this.initHandlebarHelpers(); - }, - - render: function() { - RockstorWizardPage.prototype.render.apply(this, arguments); - this.$('#ph-ports-form').html(this.port_template({ports: this.ports})); - - // Add form validation - this.portForm = this.$('#port-select-form'); - var rules = {}; - var messages = {}; - this.ports.each(function(port) { - rules[port.id] = { required: true, number: true }; - messages[port.id] = "Please enter a valid port number"; - }); - this.validator = this.portForm.validate({ - rules: rules, - messages: messages - }); - return this; - }, - - save: function() { - - // Validate the form - if (!this.portForm.valid()) { - this.validator.showErrors(); - // return rejected promise so that the wizard doesn't proceed to the next page. - return $.Deferred().reject(); - } - - var port_map = {}; - var cports = this.ports.filter(function(port) { - port_map[this.$('#' + port.id).val()] = port.get('containerp'); - return port; - }, this); - this.model.set('port_map', port_map); - return $.Deferred().resolve(); - }, - - initHandlebarHelpers: function(){ - Handlebars.registerHelper('display_portsForm', function(){ - var html = ''; - this.ports.each(function(port, index) { - html += '
'; - html += ''; - html += '
'; - html += ''; - html += '
'; - html += ''; - html += '
'; - }); - return new Handlebars.SafeString(html); - }); + initialize: function() { + this.template = window.JST.rockons_port_choice; + this.port_template = window.JST.rockons_ports_form; + this.ports = this.model.get('ports'); + RockstorWizardPage.prototype.initialize.apply(this, arguments); + this.initHandlebarHelpers(); + }, + + render: function() { + RockstorWizardPage.prototype.render.apply(this, arguments); + this.$('#ph-ports-form').html(this.port_template({ports: this.ports})); + + // Add form validation + this.portForm = this.$('#port-select-form'); + var rules = {}; + var messages = {}; + this.ports.each(function(port) { + rules[port.id] = { required: true, number: true }; + messages[port.id] = "Please enter a valid port number"; + }); + this.validator = this.portForm.validate({ + rules: rules, + messages: messages + }); + return this; + }, + + save: function() { + + // Validate the form + if (!this.portForm.valid()) { + this.validator.showErrors(); + // return rejected promise so that the wizard doesn't proceed to the next page. + return $.Deferred().reject(); } + + var port_map = {}; + var cports = this.ports.filter(function(port) { + port_map[this.$('#' + port.id).val()] = port.get('containerp'); + return port; + }, this); + this.model.set('port_map', port_map); + return $.Deferred().resolve(); + }, + + initHandlebarHelpers: function(){ + Handlebars.registerHelper('display_portsForm', function(){ + var html = ''; + this.ports.each(function(port, index) { + html += '
'; + html += ''; + html += '
'; + html += ''; + html += '
'; + html += ''; + html += '
'; + }); + return new Handlebars.SafeString(html); + }); + } }); RockonCustomChoice = RockstorWizardPage.extend({ - initialize: function() { - this.template = window.JST.rockons_custom_choice; - this.cc_template = window.JST.rockons_cc_form; - this.custom_config = this.model.get('custom_config'); - RockstorWizardPage.prototype.initialize.apply(this, arguments); - this.initHandlebarHelpers(); - }, - - render: function() { - RockstorWizardPage.prototype.render.apply(this, arguments); - this.$('#ph-cc-form').html(this.cc_template({cc: this.custom_config})); - this.cc_form = this.$('#custom-choice-form'); - var rules = {}; - var messages = {}; - this.custom_config.each(function(cc) { - rules[cc.id] = "required"; - messages[cc.id] = "This is a required field."; - }); - this.validator = this.cc_form.validate({ - rules: rules, - messages: messages - }); - return this; - }, + initialize: function() { + this.template = window.JST.rockons_custom_choice; + this.cc_template = window.JST.rockons_cc_form; + this.custom_config = this.model.get('custom_config'); + RockstorWizardPage.prototype.initialize.apply(this, arguments); + this.initHandlebarHelpers(); + }, + + render: function() { + RockstorWizardPage.prototype.render.apply(this, arguments); + this.$('#ph-cc-form').html(this.cc_template({cc: this.custom_config})); + this.cc_form = this.$('#custom-choice-form'); + var rules = {}; + var messages = {}; + this.custom_config.each(function(cc) { + rules[cc.id] = "required"; + messages[cc.id] = "This is a required field."; + }); + this.validator = this.cc_form.validate({ + rules: rules, + messages: messages + }); + return this; + }, + + save: function() { + if (!this.cc_form.valid()) { + this.validator.showErrors(); + return $.Deferred().reject(); + } + var cc_map = {}; + var cconfigs = this.custom_config.filter(function(cc) { + cc_map[cc.get('key')] = this.$('#' + cc.id).val(); + return cc; + }, this); + this.model.set('cc_map', cc_map); + return $.Deferred().resolve(); + }, + + initHandlebarHelpers: function(){ + Handlebars.registerHelper('display_ccForm', function(){ + var html = ''; + this.cc.each(function(cci, index) { + html += '
'; + html += ''; + html += '
'; + html += '
'; + html += ''; + html += '
  '; + html += ''; + html += '
'; + html += '
'; + }); + return new Handlebars.SafeString(html); + }); + } +}); - save: function() { - if (!this.cc_form.valid()) { - this.validator.showErrors(); - return $.Deferred().reject(); - } - var cc_map = {}; - var cconfigs = this.custom_config.filter(function(cc) { - cc_map[cc.get('key')] = this.$('#' + cc.id).val(); - return cc; - }, this); - this.model.set('cc_map', cc_map); - return $.Deferred().resolve(); - }, - - initHandlebarHelpers: function(){ - Handlebars.registerHelper('display_ccForm', function(){ - var html = ''; - this.cc.each(function(cci, index) { - html += '
'; - html += ''; - html += '
'; - html += '
'; - html += ''; - html += '
  '; - html += ''; - html += '
'; - html += '
'; - }); - return new Handlebars.SafeString(html); - }); +RockonEnvironment = RockstorWizardPage.extend({ + initialize: function() { + this.template = window.JST.rockons_custom_choice; + this.cc_template = window.JST.rockons_cc_form; + this.custom_config = this.model.get('environment'); + RockstorWizardPage.prototype.initialize.apply(this, arguments); + }, + + render: function() { + RockstorWizardPage.prototype.render.apply(this, arguments); + this.$('#ph-cc-form').html(this.cc_template({cc: this.custom_config})); + this.cc_form = this.$('#custom-choice-form'); + var rules = {}; + var messages = {}; + this.custom_config.each(function(cc) { + rules[cc.id] = "required"; + messages[cc.id] = "This is a required field."; + }); + this.validator = this.cc_form.validate({ + rules: rules, + messages: messages + }); + return this; + }, + + save: function() { + if (!this.cc_form.valid()) { + this.validator.showErrors(); + return $.Deferred().reject(); } + var env_map = {}; + var envars = this.custom_config.filter(function(cvar) { + env_map[cvar.get('key')] = this.$('#' + cvar.id).val(); + return cvar; + }, this); + this.model.set('env_map', env_map); + return $.Deferred().resolve(); + } }); RockonInstallSummary = RockstorWizardPage.extend({ - initialize: function() { - this.template = window.JST.rockons_install_summary; - this.table_template = window.JST.rockons_summary_table; - this.share_map = this.model.get('share_map'); - this.port_map = this.model.get('port_map'); - this.cc_map = this.model.get('cc_map'); - this.ports = this.model.get('ports'); - this.cc = this.model.get('custom_config'); - this.rockon = this.model.get('rockon'); - RockstorWizardPage.prototype.initialize.apply(this, arguments); - this.initHandlebarHelpers(); - }, - - render: function() { - RockstorWizardPage.prototype.render.apply(this, arguments); - this.$('#ph-summary-table').html(this.table_template({ - share_map: this.share_map, - port_map: this.port_map, - cc_map: this.cc_map})); - return this; - }, - - save: function() { - var _this = this; - //$('button#next-page').prop('disable', true); - document.getElementById('next-page').disabled = true; - return $.ajax({ - url: '/api/rockons/' + this.rockon.id + '/install', - type: 'POST', - dataType: 'json', - contentType: 'application/json', - data: JSON.stringify({ - 'ports': this.port_map, - 'shares': this.share_map, - 'cc': this.cc_map - }), - success: function() { - document.getElementById('next-page').disabled = false; - }, - error: function(request, status, error) { } - }); - }, - - initHandlebarHelpers: function(){ - Handlebars.registerHelper('display_rockonsSummary_table', function(){ - var html = ''; - for (s in this.share_map) { - html += ''; - html += 'Share'; - html += '' + this.share_map[s] + ''; - html += '' + s + ''; - html += ''; - } - for (p in this.port_map) { - html += ''; - html += 'Port'; - html += '' + this.port_map[p] + ''; - html += '' + p + ''; - html += ''; - } - for (c in this.cc_map) { - html += ''; - html += 'Custom'; - html += '' + this.cc_map[c] + ''; - html += '' + c + ''; - html += ''; - } - return new Handlebars.SafeString(html); - }); - } + initialize: function() { + this.template = window.JST.rockons_install_summary; + this.table_template = window.JST.rockons_summary_table; + this.share_map = this.model.get('share_map'); + this.port_map = this.model.get('port_map'); + this.cc_map = this.model.get('cc_map'); + this.ports = this.model.get('ports'); + this.environment = this.model.get('environment'); + this.cc = this.model.get('custom_config'); + this.rockon = this.model.get('rockon'); + RockstorWizardPage.prototype.initialize.apply(this, arguments); + this.initHandlebarHelpers(); + }, + + render: function() { + RockstorWizardPage.prototype.render.apply(this, arguments); + this.$('#ph-summary-table').html(this.table_template({ + share_map: this.share_map, + port_map: this.port_map, + cc_map: this.cc_map})); + return this; + }, + + save: function() { + var _this = this; + //$('button#next-page').prop('disable', true); + document.getElementById('next-page').disabled = true; + return $.ajax({ + url: '/api/rockons/' + this.rockon.id + '/install', + type: 'POST', + dataType: 'json', + contentType: 'application/json', + data: JSON.stringify({ + 'ports': this.port_map, + 'shares': this.share_map, + 'cc': this.cc_map, + 'environment': this.env_map + }), + success: function() { + document.getElementById('next-page').disabled = false; + }, + error: function(request, status, error) { } + }); + }, + + initHandlebarHelpers: function(){ + Handlebars.registerHelper('display_rockonsSummary_table', function(){ + var html = ''; + for (s in this.share_map) { + html += ''; + html += 'Share'; + html += '' + this.share_map[s] + ''; + html += '' + s + ''; + html += ''; + } + for (p in this.port_map) { + html += ''; + html += 'Port'; + html += '' + this.port_map[p] + ''; + html += '' + p + ''; + html += ''; + } + for (c in this.cc_map) { + html += ''; + html += 'Custom'; + html += '' + this.cc_map[c] + ''; + html += '' + c + ''; + html += ''; + } + return new Handlebars.SafeString(html); + }); + } }); RockonInstallComplete = RockstorWizardPage.extend({ - initialize: function() { - this.template = window.JST.rockons_install_complete; - this.port_map = this.model.get('port_map'); - this.share_map = this.model.get('share_map'); - RockstorWizardPage.prototype.initialize.apply(this, arguments); - }, - - render: function() { - $(this.el).html(this.template({ - model: this.model, - port_map: this.port_map, - share_map: this.share_map - })); - return this; - } + initialize: function() { + this.template = window.JST.rockons_install_complete; + this.port_map = this.model.get('port_map'); + this.share_map = this.model.get('share_map'); + RockstorWizardPage.prototype.initialize.apply(this, arguments); + }, + + render: function() { + $(this.el).html(this.template({ + model: this.model, + port_map: this.port_map, + share_map: this.share_map + })); + return this; + } }); RockonInfoView = WizardView.extend({ - initialize: function() { - WizardView.prototype.initialize.apply(this, arguments); - this.pages = [RockonInfoSummary,]; - }, - - render: function() { - WizardView.prototype.render.apply(this, arguments); - return this; - }, - - modifyButtonText: function() { - this.$('#prev-page').hide(); - this.$('#next-page').hide(); - } + initialize: function() { + WizardView.prototype.initialize.apply(this, arguments); + this.pages = [RockonInfoSummary,]; + }, + + render: function() { + WizardView.prototype.render.apply(this, arguments); + return this; + }, + + modifyButtonText: function() { + this.$('#prev-page').hide(); + this.$('#next-page').hide(); + } }); RockonSettingsWizardView = WizardView.extend({ - initialize: function() { - WizardView.prototype.initialize.apply(this, arguments); - this.pages = [RockonSettingsSummary,]; - this.rockon = this.model.get('rockon'); - this.volumes = new RockOnVolumeCollection(null, {rid: this.rockon.id}); - this.ports = new RockOnPortCollection(null, {rid: this.rockon.id}); - this.custom_config = new RockOnCustomConfigCollection(null, {rid: this.rockon.id}); - this.shares = {}; - this.model.set('shares', this.shares); - }, - - fetchVolumes: function() { - var _this = this; - this.volumes.fetch({ - success: function () { - _this.model.set('volumes', _this.volumes); - _this.fetchPorts(); - } - }); - }, - - fetchPorts: function() { - var _this = this; - this.ports.fetch({ - success: function() { - _this.model.set('ports', _this.ports); - _this.fetchCustomConfig(); - } - }); - }, - - fetchCustomConfig: function() { - var _this = this; - this.custom_config.fetch({ - success: function() { - _this.model.set('custom_config', _this.custom_config); - _this.addPages(); - } - }); - }, - - render: function() { - this.fetchVolumes(); - return this; - }, - - addPages: function() { - if (this.rockon.get('volume_add_support')) { - this.pages.push.apply(this.pages, - [RockonAddShare, RockonSettingsSummary, - RockonSettingsComplete]); - } - WizardView.prototype.render.apply(this, arguments); - return this; - }, - - setCurrentPage: function() { - this.currentPage = new this.pages[this.currentPageNum]({ - model: this.model, - parent: this, - evAgg: this.evAgg - }); - }, - - modifyButtonText: function() { - if (this.currentPageNum == 0) { - this.$('#prev-page').hide(); - this.$('#next-page').html('Add Storage'); - if (!this.rockon.get('volume_add_support')) { - this.$('#next-page').hide(); - } - } else if (this.currentPageNum == (this.pages.length - 2)) { - this.$('#prev-page').html('Add Storage'); - this.$('#next-page').html('Next'); - } else if (this.currentPageNum == (this.pages.length - 1)) { - this.$('#prev-page').hide(); - this.$('#next-page').html('Submit'); - } else { - this.$('#prev-page').show(); - this.$('#next-page').html('Next'); - this.$('#ph-wizard-buttons').show(); - } - }, + initialize: function() { + WizardView.prototype.initialize.apply(this, arguments); + this.pages = [RockonSettingsSummary,]; + this.rockon = this.model.get('rockon'); + this.volumes = new RockOnVolumeCollection(null, {rid: this.rockon.id}); + this.ports = new RockOnPortCollection(null, {rid: this.rockon.id}); + this.custom_config = new RockOnCustomConfigCollection(null, {rid: this.rockon.id}); + this.shares = {}; + this.model.set('shares', this.shares); + }, + + fetchVolumes: function() { + var _this = this; + this.volumes.fetch({ + success: function () { + _this.model.set('volumes', _this.volumes); + _this.fetchPorts(); + } + }); + }, + + fetchPorts: function() { + var _this = this; + this.ports.fetch({ + success: function() { + _this.model.set('ports', _this.ports); + _this.fetchCustomConfig(); + } + }); + }, + + fetchCustomConfig: function() { + var _this = this; + this.custom_config.fetch({ + success: function() { + _this.model.set('custom_config', _this.custom_config); + _this.addPages(); + } + }); + }, + + render: function() { + this.fetchVolumes(); + return this; + }, + + addPages: function() { + if (this.rockon.get('volume_add_support')) { + this.pages.push.apply(this.pages, + [RockonAddShare, RockonSettingsSummary, + RockonSettingsComplete]); + } + WizardView.prototype.render.apply(this, arguments); + return this; + }, + + setCurrentPage: function() { + this.currentPage = new this.pages[this.currentPageNum]({ + model: this.model, + parent: this, + evAgg: this.evAgg + }); + }, + + modifyButtonText: function() { + if (this.currentPageNum == 0) { + this.$('#prev-page').hide(); + this.$('#next-page').html('Add Storage'); + if (!this.rockon.get('volume_add_support')) { + this.$('#next-page').hide(); + } + } else if (this.currentPageNum == (this.pages.length - 2)) { + this.$('#prev-page').html('Add Storage'); + this.$('#next-page').html('Next'); + } else if (this.currentPageNum == (this.pages.length - 1)) { + this.$('#prev-page').hide(); + this.$('#next-page').html('Submit'); + } else { + this.$('#prev-page').show(); + this.$('#next-page').html('Next'); + this.$('#ph-wizard-buttons').show(); + } + }, - lastPage: function() { - return ((this.pages.length > 1) - && ((this.pages.length-1) == this.currentPageNum)); - }, + lastPage: function() { + return ((this.pages.length > 1) + && ((this.pages.length-1) == this.currentPageNum)); + }, - finish: function() { - this.parent.$('#install-rockon-overlay').overlay().close(); - this.parent.render(); - } + finish: function() { + this.parent.$('#install-rockon-overlay').overlay().close(); + this.parent.render(); + } }); RockonAddShare = RockstorWizardPage.extend({ - initialize: function() { - this.template = window.JST.rockons_add_shares; - this.sub_template = window.JST.rockons_add_shares_form; - this.shares = new ShareCollection(); - this.shares.setPageSize(100); - RockstorWizardPage.prototype.initialize.apply(this, arguments); - this.shares.on('reset', this.renderShares, this); - }, - - render: function() { - RockstorWizardPage.prototype.render.apply(this, arguments); - this.shares.fetch(); - return this; - }, - - renderShares: function() { - this.share_map = this.model.get('shares'); - this.volumes = this.model.get('volumes'); - this.used_shares = []; - var _this = this; - this.volumes.each(function(volume, index) { - _this.used_shares.push(volume.get('share_name')); - }); - for (var s in this.share_map) { - this.used_shares.push(s); - } - this.filtered_shares = this.shares.filter(function(share) { - if (_this.used_shares.indexOf(share.get('name')) == -1) { - return share; - } - }, this); - this.$('#ph-add-shares-form').html(this.sub_template({ - shares: this.filtered_shares - })); - this.share_form = this.$('#vol-select-form'); - this.validator = this.share_form.validate({ - rules: { "volume": "required", - "share": "required" }, - messages: { "volume": "Must be a valid unix path. Eg: /data/media", - "share": "Select an appropriate Share to map"} - }); - return this; - }, - - save: function() { - if (!this.share_form.valid()) { - this.validator.showErrors(); - return $.Deferred().reject(); - } - this.share_map = this.model.get('shares'); - this.share_map[this.$('#volume').val()] = this.$('#share').val(); - this.model.set('shares', this.share_map); - return $.Deferred().resolve(); + initialize: function() { + this.template = window.JST.rockons_add_shares; + this.sub_template = window.JST.rockons_add_shares_form; + this.shares = new ShareCollection(); + this.shares.setPageSize(100); + RockstorWizardPage.prototype.initialize.apply(this, arguments); + this.shares.on('reset', this.renderShares, this); + }, + + render: function() { + RockstorWizardPage.prototype.render.apply(this, arguments); + this.shares.fetch(); + return this; + }, + + renderShares: function() { + this.share_map = this.model.get('shares'); + this.volumes = this.model.get('volumes'); + this.used_shares = []; + var _this = this; + this.volumes.each(function(volume, index) { + _this.used_shares.push(volume.get('share_name')); + }); + for (var s in this.share_map) { + this.used_shares.push(s); + } + this.filtered_shares = this.shares.filter(function(share) { + if (_this.used_shares.indexOf(share.get('name')) == -1) { + return share; + } + }, this); + this.$('#ph-add-shares-form').html(this.sub_template({ + shares: this.filtered_shares + })); + this.share_form = this.$('#vol-select-form'); + this.validator = this.share_form.validate({ + rules: { "volume": "required", + "share": "required" }, + messages: { "volume": "Must be a valid unix path. Eg: /data/media", + "share": "Select an appropriate Share to map"} + }); + return this; + }, + + save: function() { + if (!this.share_form.valid()) { + this.validator.showErrors(); + return $.Deferred().reject(); } + this.share_map = this.model.get('shares'); + this.share_map[this.$('#volume').val()] = this.$('#share').val(); + this.model.set('shares', this.share_map); + return $.Deferred().resolve(); + } }); RockonInfoSummary = RockstorWizardPage.extend({ - initialize: function() { - this.template = window.JST.rockons_settings_summary; - this.sub_template = window.JST.rockons_more_info; - RockstorWizardPage.prototype.initialize.apply(this, arguments); - }, - - render: function() { - RockstorWizardPage.prototype.render.apply(this, arguments); - this.$('#ph-settings-summary-table').html(this.sub_template({ - rockonMoreInfo: this.model.get('more_info'), - - })); - return this; - } + initialize: function() { + this.template = window.JST.rockons_settings_summary; + this.sub_template = window.JST.rockons_more_info; + RockstorWizardPage.prototype.initialize.apply(this, arguments); + }, -}); - -RockonSettingsSummary = RockstorWizardPage.extend({ - initialize: function() { - this.template = window.JST.rockons_settings_summary; - this.sub_template = window.JST.rockons_settings_summary_table; - this.rockon = this.model.get('rockon'); - RockstorWizardPage.prototype.initialize.apply(this, arguments); - this.initHandlebarHelpers(); - }, - - render: function() { - RockstorWizardPage.prototype.render.apply(this, arguments); - this.$('#ph-settings-summary-table').html(this.sub_template({ - model: this.model, - volumes: this.model.get('volumes'), - new_volumes: this.model.get('shares'), - ports: this.model.get('ports'), - cc: this.model.get('custom_config'), - rockon: this.model.get('rockon') - })); - return this; - }, - initHandlebarHelpers: function(){ - Handlebars.registerHelper('display_volumes', function(){ - var html = ''; - this.volumes.each(function(volume, index) { - html += ''; - html += 'Share'; - html += '' + volume.get('share_name') + ''; - html += '' + volume.get('dest_dir') + ''; - html += ''; - }); - return new Handlebars.SafeString(html); - }); + render: function() { + RockstorWizardPage.prototype.render.apply(this, arguments); + this.$('#ph-settings-summary-table').html(this.sub_template({ + rockonMoreInfo: this.model.get('more_info'), - Handlebars.registerHelper('display_newVolumes', function(){ - var html = ''; - for (share in this.new_volumes) { - html += ''; - html += 'Share'; - html += '' + this.new_volumes[share] + ''; - html += '' + share + ''; - html += ''; - } - return new Handlebars.SafeString(html); - }); - - Handlebars.registerHelper('display_ports', function(){ - var html = ''; - this.ports.each(function(port, index) { - html += ''; - html += 'Port'; - html += '' + port.get('hostp') + ''; - html += '' + port.get('containerp') + ''; - html += ''; - }); - return new Handlebars.SafeString(html); - }); + })); + return this; + } - Handlebars.registerHelper('display_cc', function(){ - var html = ''; - this.cc.each(function(cci, index) { - html += ''; - html += 'Custom'; - html += '' + cci.get('val') + '  '; - html += '' + cci.get('key') + ''; - html += ''; - }); - return new Handlebars.SafeString(html); - }); +}); - } +RockonSettingsSummary = RockstorWizardPage.extend({ + initialize: function() { + this.template = window.JST.rockons_settings_summary; + this.sub_template = window.JST.rockons_settings_summary_table; + this.rockon = this.model.get('rockon'); + RockstorWizardPage.prototype.initialize.apply(this, arguments); + this.initHandlebarHelpers(); + }, + + render: function() { + RockstorWizardPage.prototype.render.apply(this, arguments); + this.$('#ph-settings-summary-table').html(this.sub_template({ + model: this.model, + volumes: this.model.get('volumes'), + new_volumes: this.model.get('shares'), + ports: this.model.get('ports'), + cc: this.model.get('custom_config'), + rockon: this.model.get('rockon') + })); + return this; + }, + initHandlebarHelpers: function(){ + Handlebars.registerHelper('display_volumes', function(){ + var html = ''; + this.volumes.each(function(volume, index) { + html += ''; + html += 'Share'; + html += '' + volume.get('share_name') + ''; + html += '' + volume.get('dest_dir') + ''; + html += ''; + }); + return new Handlebars.SafeString(html); + }); + + Handlebars.registerHelper('display_newVolumes', function(){ + var html = ''; + for (share in this.new_volumes) { + html += ''; + html += 'Share'; + html += '' + this.new_volumes[share] + ''; + html += '' + share + ''; + html += ''; + } + return new Handlebars.SafeString(html); + }); + + Handlebars.registerHelper('display_ports', function(){ + var html = ''; + this.ports.each(function(port, index) { + html += ''; + html += 'Port'; + html += '' + port.get('hostp') + ''; + html += '' + port.get('containerp') + ''; + html += ''; + }); + return new Handlebars.SafeString(html); + }); + + Handlebars.registerHelper('display_cc', function(){ + var html = ''; + this.cc.each(function(cci, index) { + html += ''; + html += 'Custom'; + html += '' + cci.get('val') + '  '; + html += '' + cci.get('key') + ''; + html += ''; + }); + return new Handlebars.SafeString(html); + }); + + } }); RockonSettingsComplete = RockstorWizardPage.extend({ - initialize: function() { - this.template = window.JST.rockons_update_complete; - this.rockon = this.model.get('rockon'); - this.shares = this.model.get('shares'); - RockstorWizardPage.prototype.initialize.apply(this, arguments); - }, - - render: function() { - $(this.el).html(this.template({ - model: this.model - })); - return this; - }, - - save: function() { - var _this = this; - if (document.getElementById('next-page').disabled) return false; - document.getElementById('next-page').disabled = true; - return $.ajax({ - url: '/api/rockons/' + this.rockon.id + '/update', - type: 'POST', - dataType: 'json', - contentType: 'application/json', - data: JSON.stringify({ - 'shares': this.shares - }), - success: function() {} - }); - } + initialize: function() { + this.template = window.JST.rockons_update_complete; + this.rockon = this.model.get('rockon'); + this.shares = this.model.get('shares'); + RockstorWizardPage.prototype.initialize.apply(this, arguments); + }, + + render: function() { + $(this.el).html(this.template({ + model: this.model + })); + return this; + }, + + save: function() { + var _this = this; + if (document.getElementById('next-page').disabled) return false; + document.getElementById('next-page').disabled = true; + return $.ajax({ + url: '/api/rockons/' + this.rockon.id + '/update', + type: 'POST', + dataType: 'json', + contentType: 'application/json', + data: JSON.stringify({ + 'shares': this.shares + }), + success: function() {} + }); + } }); From d0b326415e4979197c472bbf83394211f5ec0187 Mon Sep 17 00:00:00 2001 From: Suman Chakravartula Date: Sat, 9 Jan 2016 16:08:17 -0800 Subject: [PATCH 12/13] fix up rockons templates. #1019 --- .../static/storageadmin/js/rockstor.js | 2 +- .../js/templates/rockons/more_info.jst | 2 +- .../static/storageadmin/js/views/rockons.js | 56 +++++++++++++++++-- 3 files changed, 53 insertions(+), 7 deletions(-) diff --git a/src/rockstor/storageadmin/static/storageadmin/js/rockstor.js b/src/rockstor/storageadmin/static/storageadmin/js/rockstor.js index c49b9eca3..03d6a3293 100644 --- a/src/rockstor/storageadmin/static/storageadmin/js/rockstor.js +++ b/src/rockstor/storageadmin/static/storageadmin/js/rockstor.js @@ -493,7 +493,7 @@ RockstorWizardPage = Backbone.View.extend({ render: function() { $(this.el).html(this.template({ - model: this.model, + model: this.model })); return this; }, diff --git a/src/rockstor/storageadmin/static/storageadmin/js/templates/rockons/more_info.jst b/src/rockstor/storageadmin/static/storageadmin/js/templates/rockons/more_info.jst index 06ea2360b..fa70887af 100644 --- a/src/rockstor/storageadmin/static/storageadmin/js/templates/rockons/more_info.jst +++ b/src/rockstor/storageadmin/static/storageadmin/js/templates/rockons/more_info.jst @@ -1 +1 @@ -{{rockonMoreInfo}} +{{{rockonMoreInfo}}} diff --git a/src/rockstor/storageadmin/static/storageadmin/js/views/rockons.js b/src/rockstor/storageadmin/static/storageadmin/js/views/rockons.js index 34ccde326..4441727e7 100644 --- a/src/rockstor/storageadmin/static/storageadmin/js/views/rockons.js +++ b/src/rockstor/storageadmin/static/storageadmin/js/views/rockons.js @@ -334,7 +334,7 @@ RockonsView = RockstorLayoutView.extend({ } else { html += ''; } - html += '  '; + html += '   '; if (rockon.get('more_info')) { html += ''; } @@ -704,7 +704,7 @@ RockonCustomChoice = RockstorWizardPage.extend({ html += ''; html += '
'; html += '
'; - html += ''; + html += ''; html += '
  '; html += ''; html += '
'; @@ -721,6 +721,7 @@ RockonEnvironment = RockstorWizardPage.extend({ this.cc_template = window.JST.rockons_cc_form; this.custom_config = this.model.get('environment'); RockstorWizardPage.prototype.initialize.apply(this, arguments); + this.initHandlebarHelpers(); }, render: function() { @@ -740,6 +741,24 @@ RockonEnvironment = RockstorWizardPage.extend({ return this; }, + initHandlebarHelpers: function(){ + Handlebars.registerHelper('display_ccForm', function(){ + var html = ''; + this.cc.each(function(cci, index) { + html += '
'; + html += ''; + html += '
'; + html += '
'; + html += ''; + html += '
  '; + html += ''; + html += '
'; + html += '
'; + }); + return new Handlebars.SafeString(html); + }); + }, + save: function() { if (!this.cc_form.valid()) { this.validator.showErrors(); @@ -762,6 +781,7 @@ RockonInstallSummary = RockstorWizardPage.extend({ this.share_map = this.model.get('share_map'); this.port_map = this.model.get('port_map'); this.cc_map = this.model.get('cc_map'); + this.env_map = this.model.get('env_map'); this.ports = this.model.get('ports'); this.environment = this.model.get('environment'); this.cc = this.model.get('custom_config'); @@ -775,7 +795,8 @@ RockonInstallSummary = RockstorWizardPage.extend({ this.$('#ph-summary-table').html(this.table_template({ share_map: this.share_map, port_map: this.port_map, - cc_map: this.cc_map})); + cc_map: this.cc_map, + env_map: this.env_map})); return this; }, @@ -825,6 +846,13 @@ RockonInstallSummary = RockstorWizardPage.extend({ html += '' + c + ''; html += ''; } + for (e in this.env_map) { + html += ''; + html += 'Env'; + html += '' + this.env_map[e] + ''; + html += '' + e + ''; + html += ''; + } return new Handlebars.SafeString(html); }); } @@ -875,6 +903,7 @@ RockonSettingsWizardView = WizardView.extend({ this.volumes = new RockOnVolumeCollection(null, {rid: this.rockon.id}); this.ports = new RockOnPortCollection(null, {rid: this.rockon.id}); this.custom_config = new RockOnCustomConfigCollection(null, {rid: this.rockon.id}); + this.environment = new RockOnEnvironmentCollection(null, {rid: this.rockon.id}); this.shares = {}; this.model.set('shares', this.shares); }, @@ -904,6 +933,16 @@ RockonSettingsWizardView = WizardView.extend({ this.custom_config.fetch({ success: function() { _this.model.set('custom_config', _this.custom_config); + _this.fetchEnvironment(); + } + }); + }, + + fetchEnvironment: function() { + var _this = this; + this.environment.fetch({ + success: function() { + _this.model.set('environment', _this.environment); _this.addPages(); } }); @@ -1027,14 +1066,14 @@ RockonInfoSummary = RockstorWizardPage.extend({ initialize: function() { this.template = window.JST.rockons_settings_summary; this.sub_template = window.JST.rockons_more_info; + this.rockon = this.model.get('rockon'); RockstorWizardPage.prototype.initialize.apply(this, arguments); }, render: function() { RockstorWizardPage.prototype.render.apply(this, arguments); this.$('#ph-settings-summary-table').html(this.sub_template({ - rockonMoreInfo: this.model.get('more_info'), - + rockonMoreInfo: this.rockon.get('more_info') })); return this; } @@ -1058,6 +1097,7 @@ RockonSettingsSummary = RockstorWizardPage.extend({ new_volumes: this.model.get('shares'), ports: this.model.get('ports'), cc: this.model.get('custom_config'), + env: this.model.get('environment'), rockon: this.model.get('rockon') })); return this; @@ -1108,6 +1148,12 @@ RockonSettingsSummary = RockstorWizardPage.extend({ html += '' + cci.get('key') + ''; html += ''; }); + //@todo: separate env and cc stuff. + this.env.each(function(envi, index) { + html += 'Env'; + html += '' + envi.get('val') + '  '; + html += '' + envi.get('key') + ''; + }); return new Handlebars.SafeString(html); }); From 30054d0cc1c2a47045a1e6dc117299fb72fd6973 Mon Sep 17 00:00:00 2001 From: Suman Chakravartula Date: Sat, 9 Jan 2016 16:08:33 -0800 Subject: [PATCH 13/13] don't set value from meta. #858 --- src/rockstor/storageadmin/views/rockon.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/rockstor/storageadmin/views/rockon.py b/src/rockstor/storageadmin/views/rockon.py index 8146a42e6..098d99c9c 100644 --- a/src/rockstor/storageadmin/views/rockon.py +++ b/src/rockstor/storageadmin/views/rockon.py @@ -265,8 +265,7 @@ def _update_cc(self, ro, r_d): for k in self._sorted_keys(cc_d): ccc_d = cc_d[k] defaults = {'description': ccc_d['description'], - 'label': ccc_d['label'], - 'val': ccc_d.get('val', None), } + 'label': ccc_d['label'], } cco, created = DCustomConfig.objects.get_or_create( rockon=ro, key=k, defaults=defaults) if (not created): self._update_model(cco, defaults) @@ -278,8 +277,7 @@ def _update_env(self, co, c_d): for k in self._sorted_keys(cc_d): ccc_d = cc_d[k] defaults = {'description': ccc_d['description'], - 'label': ccc_d['label'], - 'val': ccc_d.get('val', None), } + 'label': ccc_d['label'], } cco, created = DContainerEnv.objects.get_or_create( container=co, key=k, defaults=defaults) if (not created): self._update_model(cco, defaults)