diff --git a/src/rockstor/cli/disks_console.py b/src/rockstor/cli/disks_console.py index 886caa7b7..f09734aa0 100644 --- a/src/rockstor/cli/disks_console.py +++ b/src/rockstor/cli/disks_console.py @@ -90,6 +90,6 @@ def do_wipe(self, args): def help_wipe(self): snps = "Wipe the partition table of a disk" params = { - "disk_name": "Name of the disk to be wiped of it's data", + "disk_name": "Name of the disk to be wiped of its data", } self.print_help(snps, "wipe", args=("disk_name",), params=params) diff --git a/src/rockstor/storageadmin/migrations/0013_auto_20200815_2004.py b/src/rockstor/storageadmin/migrations/0013_auto_20200815_2004.py new file mode 100644 index 000000000..ec32a92bf --- /dev/null +++ b/src/rockstor/storageadmin/migrations/0013_auto_20200815_2004.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('storageadmin', '0012_auto_20200429_1428'), + ] + + operations = [ + migrations.CreateModel( + name='BridgeConnection', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('docker_name', models.CharField(max_length=64, null=True)), + ('usercon', models.BooleanField(default=False)), + ('aux_address', models.CharField(max_length=2048, null=True)), + ('dgateway', models.CharField(max_length=64, null=True)), + ('host_binding', models.CharField(max_length=64, null=True)), + ('icc', models.BooleanField(default=False)), + ('internal', models.BooleanField(default=False)), + ('ip_masquerade', models.BooleanField(default=False)), + ('ip_range', models.CharField(max_length=64, null=True)), + ('subnet', models.CharField(max_length=64, null=True)), + ('connection', models.ForeignKey(to='storageadmin.NetworkConnection', null=True)), + ], + ), + migrations.CreateModel( + name='DContainerNetwork', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('connection', models.ForeignKey(to='storageadmin.BridgeConnection')), + ('container', models.ForeignKey(to='storageadmin.DContainer')), + ], + ), + migrations.AddField( + model_name='dport', + name='publish', + field=models.BooleanField(default=True), + ), + migrations.AlterUniqueTogether( + name='dcontainerlink', + unique_together=set([('source', 'destination', 'name')]), + ), + migrations.AlterUniqueTogether( + name='dcontainernetwork', + unique_together=set([('container', 'connection')]), + ), + ] diff --git a/src/rockstor/storageadmin/models/__init__.py b/src/rockstor/storageadmin/models/__init__.py index d8d0293e2..b718cb656 100644 --- a/src/rockstor/storageadmin/models/__init__.py +++ b/src/rockstor/storageadmin/models/__init__.py @@ -24,13 +24,9 @@ from nfs_export import NFSExport # noqa E501 from iscsi_target import IscsiTarget # noqa E501 from api_keys import APIKeys # noqa E501 -from network_interface import ( - NetworkConnection, - NetworkDevice, # noqa E501 - EthernetConnection, - TeamConnection, - BondConnection, -) # noqa E501 +from network_interface import (NetworkConnection, NetworkDevice, # noqa E501 + EthernetConnection, TeamConnection, BondConnection, + BridgeConnection) # noqa E501 from appliance import Appliance # noqa E501 from support_case import SupportCase # noqa E501 from dashboard_config import DashboardConfig # noqa E501 @@ -47,30 +43,13 @@ from oauth_app import OauthApp # noqa E501 from pool_balance import PoolBalance # noqa E501 from tls_certificate import TLSCertificate # noqa E501 -from rockon import ( - RockOn, - DImage, - DContainer, - DPort, - DVolume, # noqa E501 - ContainerOption, - DCustomConfig, - DContainerLink, # noqa E501 - DContainerEnv, - DContainerDevice, - DContainerArgs, - DContainerLabel, -) # noqa E501 -from smart import ( - SMARTAttribute, - SMARTCapability, - SMARTErrorLog, # noqa E501 - SMARTErrorLogSummary, - SMARTTestLog, - SMARTTestLogDetail, # noqa E501 - SMARTIdentity, - SMARTInfo, -) # noqa E501 +from rockon import (RockOn, DImage, DContainer, DPort, DVolume, # noqa E501 + ContainerOption, DCustomConfig, DContainerLink, # noqa E501 + DContainerEnv, DContainerDevice, DContainerArgs, + DContainerLabel, DContainerNetwork) # noqa E501 +from smart import (SMARTAttribute, SMARTCapability, SMARTErrorLog, # noqa E501 + SMARTErrorLogSummary, SMARTTestLog, SMARTTestLogDetail, # noqa E501 + SMARTIdentity, SMARTInfo) # noqa E501 from config_backup import ConfigBackup # noqa E501 from email import EmailClient # noqa E501 from update_subscription import UpdateSubscription # noqa E501 diff --git a/src/rockstor/storageadmin/models/network_interface.py b/src/rockstor/storageadmin/models/network_interface.py index c404d91cf..c1133780d 100644 --- a/src/rockstor/storageadmin/models/network_interface.py +++ b/src/rockstor/storageadmin/models/network_interface.py @@ -19,7 +19,6 @@ import json from django.db import models - # This is the key abstraction for network configuration that is user # configurable in Rockstor. user can add, delete or modify connections which # results in CRUD ops on this model and also on other models linked to this @@ -86,6 +85,8 @@ def ctype(self): return "team" if self.bondconnection_set.count() > 0: return "bond" + if self.bridgeconnection_set.count() > 0: + return "bridge" return None @property @@ -112,6 +113,66 @@ def bond_profile(self): finally: return profile + @property + def docker_name(self): + dname = None + if self.bridgeconnection_set.count() > 0: + brco = self.bridgeconnection_set.first() + dname = brco.docker_name + return dname + + @property + def user_dnet(self): + """ + Returns True if the docker network is a rocknet (defined by the user). + Used by rockons.js to list available rocknets available for connection. + :return: Boolean + """ + user_dnet = None + if self.bridgeconnection_set.count() > 0: + brco = self.bridgeconnection_set.first() + user_dnet = brco.usercon + if user_dnet: + user_dnet = True + return user_dnet + + @property + def docker_options(self): + """ + Gather all connection's settings in a dict to be displayed in the UI connection form + needed to edit an existing docker network connection. + :return: + """ + docker_options = {} + if self.bridgeconnection_set.count() > 0: + brco = self.bridgeconnection_set.first() + connected_containers = [] + if brco.dcontainernetwork_set.filter(connection=brco.id).count() > 0: + for i in range( + brco.dcontainernetwork_set.filter(connection=brco.id).count() + ): + cname = ( + brco.dcontainernetwork_set.filter(connection=brco.id) + .order_by("id")[i] + .container_name + ) + rname = ( + brco.dcontainernetwork_set.filter(connection=brco.id) + .order_by("id")[i] + .container.rockon.name + ) + connected_containers.append("{} ({})".format(cname, rname)) + docker_options["aux_address"] = brco.aux_address + docker_options["dgateway"] = brco.dgateway + docker_options["host_binding"] = brco.host_binding + docker_options["icc"] = brco.icc + docker_options["internal"] = brco.internal + docker_options["ip_masquerade"] = brco.ip_masquerade + docker_options["ip_range"] = brco.ip_range + docker_options["subnet"] = brco.subnet + docker_options["containers"] = connected_containers + return docker_options + class Meta: app_label = "storageadmin" @@ -141,6 +202,17 @@ def cname(self): return None return self.connection.name + @property + def dev_name(self): + """ + Return the user-friendly docker_name as device name for bridge connections + to be displayed in the network widget on the dashboard. + :return: + """ + if (self.dtype == "bridge") and (self.connection is not None): + return self.connection.docker_name + return self.name + class Meta: app_label = "storageadmin" @@ -177,3 +249,20 @@ class BondConnection(models.Model): class Meta: app_label = "storageadmin" + + +class BridgeConnection(models.Model): + connection = models.ForeignKey(NetworkConnection, null=True) + docker_name = models.CharField(max_length=64, null=True) + usercon = models.BooleanField(default=False) + aux_address = models.CharField(max_length=2048, null=True) + dgateway = models.CharField(max_length=64, null=True) + host_binding = models.CharField(max_length=64, null=True) + icc = models.BooleanField(default=False) + internal = models.BooleanField(default=False) + ip_masquerade = models.BooleanField(default=False) + ip_range = models.CharField(max_length=64, null=True) + subnet = models.CharField(max_length=64, null=True) + + class Meta: + app_label = "storageadmin" diff --git a/src/rockstor/storageadmin/models/rockon.py b/src/rockstor/storageadmin/models/rockon.py index a003587b2..261a1df29 100644 --- a/src/rockstor/storageadmin/models/rockon.py +++ b/src/rockstor/storageadmin/models/rockon.py @@ -17,7 +17,9 @@ """ from django.db import models -from storageadmin.models import Share +from storageadmin.models import Share, BridgeConnection + +from system.docker import probe_running_containers class RockOn(models.Model): @@ -44,6 +46,36 @@ def ui_port(self): return po.hostp return None + @property + def ui_publish(self): + """ + Returns True if the rock-on has a container with a UI port defined + and set to be published. This property is used to decide whether or + not to disable a rock-on's UI button. + :return: + """ + if not self.ui: + return None + for co in self.dcontainer_set.all(): + for po in co.dport_set.all(): + if po.uiport and po.publish: + return True + return None + + @property + def host_network(self): + """ + Checks whether the rock-on uses host networking and disable networking + post-install customization options accordingly. + :return: True if using host networking. + """ + for co in self.dcontainer_set.all(): + res = probe_running_containers(container=co.name, network="host", all=True) + if len(res) > 1: + return True + else: + return False + class Meta: app_label = "storageadmin" @@ -77,7 +109,28 @@ class DContainerLink(models.Model): name = models.CharField(max_length=64, null=True) class Meta: - unique_together = ("destination", "name") + unique_together = ("source", "destination", "name") + app_label = "storageadmin" + + +class DContainerNetwork(models.Model): + container = models.ForeignKey(DContainer) + connection = models.ForeignKey(BridgeConnection) + + @property + def docker_name(self): + if self.connection is not None: + return self.connection.docker_name + return None + + @property + def container_name(self): + if self.container is not None: + return self.container.name + return None + + class Meta: + unique_together = ("container", "connection") app_label = "storageadmin" @@ -90,6 +143,13 @@ class DPort(models.Model): protocol = models.CharField(max_length=32, null=True) uiport = models.BooleanField(default=False) label = models.CharField(max_length=1024, null=True) + publish = models.BooleanField(default=True) + + @property + def container_name(self): + if self.container is not None: + return self.container.name + return None class Meta: unique_together = ( diff --git a/src/rockstor/storageadmin/serializers.py b/src/rockstor/storageadmin/serializers.py index e91506881..5dbf79f2f 100644 --- a/src/rockstor/storageadmin/serializers.py +++ b/src/rockstor/storageadmin/serializers.py @@ -49,6 +49,7 @@ DContainerEnv, DContainerDevice, DContainerLabel, + DContainerNetwork, SMARTAttribute, SMARTCapability, SMARTInfo, @@ -201,6 +202,7 @@ class Meta: class NetworkDeviceSerializer(serializers.ModelSerializer): cname = serializers.CharField() + dev_name = serializers.CharField() class Meta: model = NetworkDevice @@ -211,6 +213,9 @@ class NetworkConnectionSerializer(serializers.ModelSerializer): mtu = serializers.IntegerField() team_profile = serializers.CharField() bond_profile = serializers.CharField() + docker_name = serializers.CharField() + user_dnet = serializers.BooleanField() + docker_options = serializers.DictField() class Meta: model = NetworkConnection @@ -254,6 +259,8 @@ class Meta: class RockOnSerializer(serializers.ModelSerializer): ui_port = serializers.IntegerField() + ui_publish = serializers.BooleanField() + host_network = serializers.BooleanField() class Meta: model = RockOn @@ -272,6 +279,8 @@ class Meta: class RockOnPortSerializer(serializers.ModelSerializer): + container_name = serializers.CharField() + class Meta: model = DPort @@ -296,6 +305,14 @@ class Meta: model = DContainerLabel +class RockOnNetworkSerializer(serializers.ModelSerializer): + docker_name = serializers.CharField() + container_name = serializers.CharField() + + class Meta: + model = DContainerNetwork + + class SMARTCapabilitySerializer(serializers.ModelSerializer): class Meta: model = SMARTCapability diff --git a/src/rockstor/storageadmin/static/storageadmin/js/models/models.js b/src/rockstor/storageadmin/static/storageadmin/js/models/models.js index b16ec3e1d..b3f0e8ac6 100644 --- a/src/rockstor/storageadmin/static/storageadmin/js/models/models.js +++ b/src/rockstor/storageadmin/static/storageadmin/js/models/models.js @@ -784,6 +784,26 @@ var RockOnLabelCollection = RockStorPaginatedCollection.extend({ } }); +var RockOnNetwork = Backbone.Model.extend({ + urlRoot: '/api/rockon/networks/' + this.rid +}); + +var RockOnNetworkCollection = RockStorPaginatedCollection.extend({ + model: RockOnNetwork, + 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/networks/' + this.rid; + } + return '/api/rockons/networks'; + } +}); + var RockOnCustomConfig = Backbone.Model.extend({ urlRoot: '/api/rockon/customconfig/' + this.rid }); diff --git a/src/rockstor/storageadmin/static/storageadmin/js/templates/network/network.jst b/src/rockstor/storageadmin/static/storageadmin/js/templates/network/network.jst index 4836e5a29..759c2f4dd 100644 --- a/src/rockstor/storageadmin/static/storageadmin/js/templates/network/network.jst +++ b/src/rockstor/storageadmin/static/storageadmin/js/templates/network/network.jst @@ -35,6 +35,7 @@ UUID Type State + Docker name Connection method IP Address Gateway @@ -49,17 +50,26 @@ {{this.name}}   + {{#unless this.docker_name}} + {{/unless}} + {{#if this.user_dnet}} + + + {{/if}} {{this.uuid}} {{this.ctype}} {{#if this.team_profile}}[{{this.team_profile}}]{{/if}}{{#if this.bond_profile}}[{{this.bond_profile}}]{{/if}} - {{this.state}}   + {{this.state}}  {{#unless this.docker_name}} @@ -68,10 +78,12 @@
+ {{/unless}} + {{this.docker_name}} {{this.ipv4_method}} {{this.ipv4_addresses}} - {{this.ipv4_gw}} + {{#if this.ipv4_gw}}{{this.ipv4_gw}}{{else}}{{this.docker_options.dgateway}}{{/if}} {{this.ipv4_dns}} {{this.ipv4_dns_search}} {{this.mtu}} @@ -105,6 +117,33 @@ {{/each}} {{/hasChildren}} + {{#if this.docker_name}} +

Rocknet details

+ + + + + + + + + + + + + + + + + + + + + + + +
NameAuxiliary addressesInter-containers communicationInternalHost bindingIP rangeIP masqueradeSubnetContainers (Rock-on)
{{this.docker_name}}{{this.docker_options.aux_address}}{{#if this.docker_options.icc}}Yes{{else}}No{{/if}}{{#if this.docker_options.internal}}Yes{{else}}No{{/if}}{{this.docker_options.host_binding}}{{this.docker_options.ip_range}}{{#if this.docker_options.ip_masquerade}}Yes{{else}}No{{/if}}{{this.docker_options.subnet}}{{this.docker_options.containers}}
+ {{else}}

member Devices

@@ -129,6 +168,7 @@ {{/if}} {{/each}}
+ {{/if}} diff --git a/src/rockstor/storageadmin/static/storageadmin/js/templates/network/new_connection.jst b/src/rockstor/storageadmin/static/storageadmin/js/templates/network/new_connection.jst index 6522ac50c..1e4fcab84 100644 --- a/src/rockstor/storageadmin/static/storageadmin/js/templates/network/new_connection.jst +++ b/src/rockstor/storageadmin/static/storageadmin/js/templates/network/new_connection.jst @@ -35,6 +35,14 @@ + {{#if connection.docker_name}} +
+ +
+ +
+
+ {{/if}}
@@ -135,6 +143,56 @@
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
diff --git a/src/rockstor/storageadmin/static/storageadmin/js/templates/rockons/edit_ports.jst b/src/rockstor/storageadmin/static/storageadmin/js/templates/rockons/edit_ports.jst new file mode 100644 index 000000000..177b7266e --- /dev/null +++ b/src/rockstor/storageadmin/static/storageadmin/js/templates/rockons/edit_ports.jst @@ -0,0 +1 @@ +
diff --git a/src/rockstor/storageadmin/static/storageadmin/js/templates/rockons/edit_ports_form.jst b/src/rockstor/storageadmin/static/storageadmin/js/templates/rockons/edit_ports_form.jst new file mode 100644 index 000000000..700acb7b2 --- /dev/null +++ b/src/rockstor/storageadmin/static/storageadmin/js/templates/rockons/edit_ports_form.jst @@ -0,0 +1,66 @@ +
+
+
+

+ You can choose to unpublish any port currently exported by this rock-on. Be aware, + however, that this is likely to interfere with its proper function, so change these settings + only if you know what it implies. +

+
+
+ + + + + + + + + + + {{#each ports}} + + + + + + + + {{/each}} +
ContainerDescriptionHost  Mapped Port  Publish  
{{this.container_name}}{{this.label}}{{this.hostp}}{{this.containerp}}{{#if this.uiport}} {{/if}} +
+ {{#unless ports}} +
+

This rock-on does not have any port exposed to modify.

+
+ {{/unless}} +
+

Rocknets (optional) 

+

Select one or more rocknet(s) to join for each container of this rock-on:

+
+
+ {{#each containers}} +
+ +
+ +
+
+ {{/each}} +
+
+
+
diff --git a/src/rockstor/storageadmin/static/storageadmin/js/templates/rockons/settings_summary_table.jst b/src/rockstor/storageadmin/static/storageadmin/js/templates/rockons/settings_summary_table.jst index 41005bfaa..08c863831 100644 --- a/src/rockstor/storageadmin/static/storageadmin/js/templates/rockons/settings_summary_table.jst +++ b/src/rockstor/storageadmin/static/storageadmin/js/templates/rockons/settings_summary_table.jst @@ -19,9 +19,21 @@ Port {{this.hostp}} - {{this.containerp}} + {{this.containerp}}{{isPublished this.id this.publish}} + {{/each}} +{{#if new_cnets}} + {{display_newCnets}} +{{else}} +{{#each rocknets}} + + Network + {{this.container_name}} + {{this.docker_name}} + +{{/each}} +{{/if}} {{#each cc}} Custom diff --git a/src/rockstor/storageadmin/static/storageadmin/js/templates/rockons/wizard_summary.jst b/src/rockstor/storageadmin/static/storageadmin/js/templates/rockons/wizard_summary.jst index 179eb018a..3eee69dc2 100644 --- a/src/rockstor/storageadmin/static/storageadmin/js/templates/rockons/wizard_summary.jst +++ b/src/rockstor/storageadmin/static/storageadmin/js/templates/rockons/wizard_summary.jst @@ -29,6 +29,7 @@
+
diff --git a/src/rockstor/storageadmin/static/storageadmin/js/templates/share/shares_table.jst b/src/rockstor/storageadmin/static/storageadmin/js/templates/share/shares_table.jst index e6f08a5c5..deb6d3e06 100644 --- a/src/rockstor/storageadmin/static/storageadmin/js/templates/share/shares_table.jst +++ b/src/rockstor/storageadmin/static/storageadmin/js/templates/share/shares_table.jst @@ -76,7 +76,7 @@
diff --git a/src/rockstor/storageadmin/static/storageadmin/js/views/dashboard/network_utilization.js b/src/rockstor/storageadmin/static/storageadmin/js/views/dashboard/network_utilization.js index faaf99fea..49abdefb7 100644 --- a/src/rockstor/storageadmin/static/storageadmin/js/views/dashboard/network_utilization.js +++ b/src/rockstor/storageadmin/static/storageadmin/js/views/dashboard/network_utilization.js @@ -168,7 +168,7 @@ NetworkUtilizationWidget = RockStorWidgetView.extend({ this.networkInterfaces.each(function(ni, i) { var opt = $('