feat(networks): add ipv6 support (#3717)
* feat(portainer-core): add ipv6 support * feat(networks): add few changes * refacto(networks): write regex once * fix(networks): fix indentation * refacto(networks): use foreach instead map and pluralize ipvxconfig * refacto(networks): pluralize ipvxconfig * feat(networks): support ipv6 with ports * feat(networks): add an explicit error message * fix(networks): hide ipv6 configuration when creating macvlanpull/3770/head
parent
b2f36a3bbe
commit
3de533042d
|
@ -34,17 +34,25 @@
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Network</th>
|
<th>Network</th>
|
||||||
<th>IP Address</th>
|
<th>
|
||||||
|
IP Address
|
||||||
|
<a ng-click="$ctrl.expandAll()" ng-if="$ctrl.hasExpandableItems()">
|
||||||
|
<i ng-class="{ 'fas fa-angle-down': $ctrl.state.expandAll, 'fas fa-angle-right': !$ctrl.state.expandAll }" aria-hidden="true"></i>
|
||||||
|
</a>
|
||||||
|
</th>
|
||||||
<th>Gateway</th>
|
<th>Gateway</th>
|
||||||
<th>MAC Address</th>
|
<th>MAC Address</th>
|
||||||
<th authorization="DockerNetworkDisconnect">Actions</th>
|
<th authorization="DockerNetworkDisconnect">Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr dir-paginate="(key, value) in $ctrl.dataset | itemsPerPage: $ctrl.state.paginatedItemLimit" ng-class="{ active: item.Checked }">
|
<tr dir-paginate-start="(key, value) in $ctrl.dataset | itemsPerPage: $ctrl.state.paginatedItemLimit" ng-class="{active: item.Checked}" ng-click="$ctrl.expandItem(value, !value.Expanded)">
|
||||||
<td
|
<td>
|
||||||
><a ui-sref="docker.networks.network({ id: key, nodeName: $ctrl.nodeName })">{{ key }}</a></td
|
<a ng-if="$ctrl.itemCanExpand(value)">
|
||||||
>
|
<i ng-class="{ 'fas fa-angle-down': value.Expanded, 'fas fa-angle-right': !value.Expanded }" class="space-right" aria-hidden="true"></i>
|
||||||
|
</a>
|
||||||
|
<a ui-sref="docker.networks.network({ id: key, nodeName: $ctrl.nodeName })">{{ key }}</a>
|
||||||
|
</td>
|
||||||
<td>{{ value.IPAddress || '-' }}</td>
|
<td>{{ value.IPAddress || '-' }}</td>
|
||||||
<td>{{ value.Gateway || '-' }}</td>
|
<td>{{ value.Gateway || '-' }}</td>
|
||||||
<td>{{ value.MacAddress || '-' }}</td>
|
<td>{{ value.MacAddress || '-' }}</td>
|
||||||
|
@ -61,6 +69,15 @@
|
||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr dir-paginate-end ng-show="value.Expanded" ng-style="{background: (value.Highlighted ? '#d5e8f3' : '#f5f5f5')}">
|
||||||
|
<td colspan="1"></td>
|
||||||
|
<td colspan="1">
|
||||||
|
{{ value.GlobalIPv6Address }}
|
||||||
|
</td>
|
||||||
|
<td colspan="3">
|
||||||
|
{{ value.IPv6Gateway || '-' }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
<tr ng-if="!$ctrl.dataset">
|
<tr ng-if="!$ctrl.dataset">
|
||||||
<td colspan="5" class="text-center text-muted">Loading...</td>
|
<td colspan="5" class="text-center text-muted">Loading...</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
angular.module('portainer.docker').component('containerNetworksDatatable', {
|
angular.module('portainer.docker').component('containerNetworksDatatable', {
|
||||||
templateUrl: './containerNetworksDatatable.html',
|
templateUrl: './containerNetworksDatatable.html',
|
||||||
controller: 'GenericDatatableController',
|
controller: 'ContainerNetworksDatatableController',
|
||||||
bindings: {
|
bindings: {
|
||||||
titleText: '@',
|
titleText: '@',
|
||||||
titleIcon: '@',
|
titleIcon: '@',
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
import _ from 'lodash-es';
|
||||||
|
|
||||||
|
angular.module('portainer.docker')
|
||||||
|
.controller('ContainerNetworksDatatableController', ['$scope', '$controller', 'DatatableService',
|
||||||
|
function ($scope, $controller, DatatableService) {
|
||||||
|
|
||||||
|
angular.extend(this, $controller('GenericDatatableController', { $scope: $scope }));
|
||||||
|
this.state = Object.assign(this.state, {
|
||||||
|
expandedItems: [],
|
||||||
|
expandAll: true
|
||||||
|
});
|
||||||
|
|
||||||
|
this.expandItem = function (item, expanded) {
|
||||||
|
if (!this.itemCanExpand(item)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
item.Expanded = expanded;
|
||||||
|
if (!expanded) {
|
||||||
|
item.Highlighted = false;
|
||||||
|
}
|
||||||
|
if (!item.Expanded) {
|
||||||
|
this.state.expandAll = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.itemCanExpand = function (item) {
|
||||||
|
return item.GlobalIPv6Address !== '';
|
||||||
|
}
|
||||||
|
|
||||||
|
this.hasExpandableItems = function () {
|
||||||
|
return _.filter(this.dataset, (item) => this.itemCanExpand(item)).length;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.expandAll = function () {
|
||||||
|
this.state.expandAll = !this.state.expandAll;
|
||||||
|
_.forEach(this.dataset, (item) => {
|
||||||
|
if (this.itemCanExpand(item)) {
|
||||||
|
this.expandItem(item, this.state.expandAll);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$onInit = function () {
|
||||||
|
this.setDefaults();
|
||||||
|
this.prepareTableFromDataset();
|
||||||
|
|
||||||
|
this.state.orderBy = this.orderBy;
|
||||||
|
var storedOrder = DatatableService.getDataTableOrder(this.tableKey);
|
||||||
|
if (storedOrder !== null) {
|
||||||
|
this.state.reverseOrder = storedOrder.reverse;
|
||||||
|
this.state.orderBy = storedOrder.orderBy;
|
||||||
|
}
|
||||||
|
|
||||||
|
var textFilter = DatatableService.getDataTableTextFilters(this.tableKey);
|
||||||
|
if (textFilter !== null) {
|
||||||
|
this.state.textFilter = textFilter;
|
||||||
|
this.onTextFilterChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
var storedFilters = DatatableService.getDataTableFilters(this.tableKey);
|
||||||
|
if (storedFilters !== null) {
|
||||||
|
this.filters = storedFilters;
|
||||||
|
}
|
||||||
|
if (this.filters && this.filters.state) {
|
||||||
|
this.filters.state.open = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var storedSettings = DatatableService.getDataTableSettings(this.tableKey);
|
||||||
|
if (storedSettings !== null) {
|
||||||
|
this.settings = storedSettings;
|
||||||
|
this.settings.open = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_.forEach(this.dataset, (item) => {
|
||||||
|
item.Expanded = true;
|
||||||
|
item.Highlighted = true;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
]);
|
|
@ -232,13 +232,6 @@
|
||||||
Created
|
Created
|
||||||
</a>
|
</a>
|
||||||
</th>
|
</th>
|
||||||
<th ng-show="$ctrl.columnVisibility.columns.ip.display">
|
|
||||||
<a ng-click="$ctrl.changeOrderBy('IP')">
|
|
||||||
IP Address
|
|
||||||
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'IP' && !$ctrl.state.reverseOrder"></i>
|
|
||||||
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'IP' && $ctrl.state.reverseOrder"></i>
|
|
||||||
</a>
|
|
||||||
</th>
|
|
||||||
<th ng-if="$ctrl.showHostColumn" ng-show="$ctrl.columnVisibility.columns.host.display">
|
<th ng-if="$ctrl.showHostColumn" ng-show="$ctrl.columnVisibility.columns.host.display">
|
||||||
<a ng-click="$ctrl.changeOrderBy('NodeName')">
|
<a ng-click="$ctrl.changeOrderBy('NodeName')">
|
||||||
Host
|
Host
|
||||||
|
@ -313,7 +306,6 @@
|
||||||
<td ng-show="$ctrl.columnVisibility.columns.created.display">
|
<td ng-show="$ctrl.columnVisibility.columns.created.display">
|
||||||
{{ item.Created | getisodatefromtimestamp }}
|
{{ item.Created | getisodatefromtimestamp }}
|
||||||
</td>
|
</td>
|
||||||
<td ng-show="$ctrl.columnVisibility.columns.ip.display">{{ item.IP ? item.IP : '-' }}</td>
|
|
||||||
<td ng-if="$ctrl.showHostColumn" ng-show="$ctrl.columnVisibility.columns.host.display">{{ item.NodeName ? item.NodeName : '-' }}</td>
|
<td ng-if="$ctrl.showHostColumn" ng-show="$ctrl.columnVisibility.columns.host.display">{{ item.NodeName ? item.NodeName : '-' }}</td>
|
||||||
<td ng-show="$ctrl.columnVisibility.columns.ports.display">
|
<td ng-show="$ctrl.columnVisibility.columns.ports.display">
|
||||||
<a
|
<a
|
||||||
|
|
|
@ -20,13 +20,13 @@
|
||||||
<span style="margin-left: 10px;" class="label label-info image-tag space-left" ng-if="item.ResourceControl.System">System</span>
|
<span style="margin-left: 10px;" class="label label-info image-tag space-left" ng-if="item.ResourceControl.System">System</span>
|
||||||
</td>
|
</td>
|
||||||
<td>{{ item.StackName ? item.StackName : '-' }}</td>
|
<td>{{ item.StackName ? item.StackName : '-' }}</td>
|
||||||
<td>{{ item.Scope }}</td>
|
|
||||||
<td>{{ item.Driver }}</td>
|
<td>{{ item.Driver }}</td>
|
||||||
<td>{{ item.Attachable }}</td>
|
<td>{{ item.Attachable }}</td>
|
||||||
<td>{{ item.Internal }}</td>
|
|
||||||
<td>{{ item.IPAM.Driver }}</td>
|
<td>{{ item.IPAM.Driver }}</td>
|
||||||
<td>{{ item.IPAM.Config[0].Subnet ? item.IPAM.Config[0].Subnet : '-' }}</td>
|
<td>{{ item.IPAM.IPV4Configs[0].Subnet ? item.IPAM.IPV4Configs[0].Subnet : '-' }}</td>
|
||||||
<td>{{ item.IPAM.Config[0].Gateway ? item.IPAM.Config[0].Gateway : '-' }}</td>
|
<td>{{ item.IPAM.IPV4Configs[0].Gateway ? item.IPAM.IPV4Configs[0].Gateway : '-' }}</td>
|
||||||
|
<td>{{ item.IPAM.IPV6Configs[0].Subnet ? item.IPAM.IPV6Configs[0].Subnet : '-' }}</td>
|
||||||
|
<td>{{ item.IPAM.IPV6Configs[0].Gateway ? item.IPAM.IPV6Configs[0].Gateway : '-' }}</td>
|
||||||
<td ng-if="parentCtrl.showHostColumn">{{ item.NodeName ? item.NodeName : '-' }}</td>
|
<td ng-if="parentCtrl.showHostColumn">{{ item.NodeName ? item.NodeName : '-' }}</td>
|
||||||
<td ng-if="parentCtrl.showOwnershipColumn">
|
<td ng-if="parentCtrl.showOwnershipColumn">
|
||||||
<span>
|
<span>
|
||||||
|
|
|
@ -95,13 +95,6 @@
|
||||||
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'StackName' && $ctrl.state.reverseOrder"></i>
|
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'StackName' && $ctrl.state.reverseOrder"></i>
|
||||||
</a>
|
</a>
|
||||||
</th>
|
</th>
|
||||||
<th>
|
|
||||||
<a ng-click="$ctrl.changeOrderBy('Scope')">
|
|
||||||
Scope
|
|
||||||
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Scope' && !$ctrl.state.reverseOrder"></i>
|
|
||||||
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Scope' && $ctrl.state.reverseOrder"></i>
|
|
||||||
</a>
|
|
||||||
</th>
|
|
||||||
<th>
|
<th>
|
||||||
<a ng-click="$ctrl.changeOrderBy('Driver')">
|
<a ng-click="$ctrl.changeOrderBy('Driver')">
|
||||||
Driver
|
Driver
|
||||||
|
@ -116,13 +109,6 @@
|
||||||
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Attachable' && $ctrl.state.reverseOrder"></i>
|
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Attachable' && $ctrl.state.reverseOrder"></i>
|
||||||
</a>
|
</a>
|
||||||
</th>
|
</th>
|
||||||
<th>
|
|
||||||
<a ng-click="$ctrl.changeOrderBy('Internal')">
|
|
||||||
Internal
|
|
||||||
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Internal' && !$ctrl.state.reverseOrder"></i>
|
|
||||||
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Internal' && $ctrl.state.reverseOrder"></i>
|
|
||||||
</a>
|
|
||||||
</th>
|
|
||||||
<th>
|
<th>
|
||||||
<a ng-click="$ctrl.changeOrderBy('IPAM.Driver')">
|
<a ng-click="$ctrl.changeOrderBy('IPAM.Driver')">
|
||||||
IPAM Driver
|
IPAM Driver
|
||||||
|
@ -131,17 +117,31 @@
|
||||||
</a>
|
</a>
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
<a ng-click="$ctrl.changeOrderBy('IPAM.Config[0].Subnet')">
|
<a ng-click="$ctrl.changeOrderBy('IPAM.IPV4Configs[0].Subnet')">
|
||||||
IPAM Subnet
|
IPV4 IPAM Subnet
|
||||||
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'IPAM.Config[0].Subnet' && !$ctrl.state.reverseOrder"></i>
|
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'IPAM.IPV4Configs[0].Subnet' && !$ctrl.state.reverseOrder"></i>
|
||||||
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'IPAM.Config[0].Subnet' && $ctrl.state.reverseOrder"></i>
|
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'IPAM.IPV4Configs[0].Subnet' && $ctrl.state.reverseOrder"></i>
|
||||||
</a>
|
</a>
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
<a ng-click="$ctrl.changeOrderBy('IPAM.Config[0].Gateway')">
|
<a ng-click="$ctrl.changeOrderBy('IPAM.IPV4Configs[0].Gateway')">
|
||||||
IPAM Gateway
|
IPV4 IPAM Gateway
|
||||||
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'IPAM.Config[0].Gateway' && !$ctrl.state.reverseOrder"></i>
|
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'IPAM.IPV4Configs[0].Gateway' && !$ctrl.state.reverseOrder"></i>
|
||||||
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'IPAM.Config[0].Gateway' && $ctrl.state.reverseOrder"></i>
|
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'IPAM.IPV4Configs[0].Gateway' && $ctrl.state.reverseOrder"></i>
|
||||||
|
</a>
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
<a ng-click="$ctrl.changeOrderBy('IPAM.IPV6Configs[0].Subnet')">
|
||||||
|
IPV6 IPAM Subnet
|
||||||
|
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'IPAM.IPV6Configs[0].Subnet' && !$ctrl.state.reverseOrder"></i>
|
||||||
|
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'IPAM.IPV6Configs[0].Subnet' && $ctrl.state.reverseOrder"></i>
|
||||||
|
</a>
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
<a ng-click="$ctrl.changeOrderBy('IPAM.IPV6Configs[0].Gateway')">
|
||||||
|
IPV6 IPAM Gateway
|
||||||
|
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'IPAM.IPV6Configs[0].Gateway' && !$ctrl.state.reverseOrder"></i>
|
||||||
|
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'IPAM.IPV6Configs[0].Gateway' && $ctrl.state.reverseOrder"></i>
|
||||||
</a>
|
</a>
|
||||||
</th>
|
</th>
|
||||||
<th ng-if="$ctrl.showHostColumn">
|
<th ng-if="$ctrl.showHostColumn">
|
||||||
|
|
|
@ -135,10 +135,21 @@ angular.module('portainer.docker').factory('ContainerHelper', [
|
||||||
let startHostPort = 0;
|
let startHostPort = 0;
|
||||||
let endHostPort = 0;
|
let endHostPort = 0;
|
||||||
if (hostPort) {
|
if (hostPort) {
|
||||||
if (hostPort.indexOf(':') > -1) {
|
if (hostPort.indexOf('[') > -1) {
|
||||||
const hostAndPort = _.split(hostPort, ':');
|
const hostAndPort = _.split(hostPort, ']:');
|
||||||
hostIp = hostAndPort[0];
|
|
||||||
|
if (hostAndPort.length < 2) {
|
||||||
|
throw new Error('Invalid port specification: ' + portBinding.containerPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
hostIp = hostAndPort[0].replace('[', '');
|
||||||
hostPort = hostAndPort[1];
|
hostPort = hostAndPort[1];
|
||||||
|
} else {
|
||||||
|
if (hostPort.indexOf(':') > -1) {
|
||||||
|
const hostAndPort = _.split(hostPort, ':');
|
||||||
|
hostIp = hostAndPort[0];
|
||||||
|
hostPort = hostAndPort[1];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const hostPortRange = parsePortRange(hostPort);
|
const hostPortRange = parsePortRange(hostPort);
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
import _ from 'lodash-es';
|
||||||
|
|
||||||
|
class DockerNetworkHelper {
|
||||||
|
static getIPV4Configs(configs) {
|
||||||
|
return _.filter(configs, (config) => /^([0-9]{1,3}\.){3}[0-9]{1,3}(\/([0-9]|[1-2][0-9]|3[0-2]))?$/.test(config.Subnet));
|
||||||
|
}
|
||||||
|
|
||||||
|
static getIPV6Configs(configs) {
|
||||||
|
return _.without(configs, ...DockerNetworkHelper.getIPV4Configs(configs));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DockerNetworkHelper;
|
|
@ -459,7 +459,7 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="container_dns_primary" class="col-sm-2 col-lg-1 control-label text-left">Primary DNS Server</label>
|
<label for="container_dns_primary" class="col-sm-2 col-lg-1 control-label text-left">Primary DNS Server</label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input type="text" class="form-control" ng-model="formValues.DnsPrimary" id="container_dns_primary" placeholder="e.g. 1.1.1.1" />
|
<input type="text" class="form-control" ng-model="formValues.DnsPrimary" id="container_dns_primary" placeholder="e.g. 1.1.1.1, 2606:4700:4700::1111" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- !dns-primary-input -->
|
<!-- !dns-primary-input -->
|
||||||
|
@ -467,7 +467,7 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="container_dns_secondary" class="col-sm-2 col-lg-1 control-label text-left">Secondary DNS Server</label>
|
<label for="container_dns_secondary" class="col-sm-2 col-lg-1 control-label text-left">Secondary DNS Server</label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input type="text" class="form-control" ng-model="formValues.DnsSecondary" id="container_dns_secondary" placeholder="e.g. 1.0.0.1" />
|
<input type="text" class="form-control" ng-model="formValues.DnsSecondary" id="container_dns_secondary" placeholder="e.g. 1.0.0.1, 2606:4700:4700::1001" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- !dns-secondary-input -->
|
<!-- !dns-secondary-input -->
|
||||||
|
|
|
@ -317,6 +317,7 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<container-networks-datatable
|
<container-networks-datatable
|
||||||
|
ng-if="container.NetworkSettings.Networks"
|
||||||
title-text="Connected networks"
|
title-text="Connected networks"
|
||||||
title-icon="fa-sitemap"
|
title-icon="fa-sitemap"
|
||||||
dataset="container.NetworkSettings.Networks"
|
dataset="container.NetworkSettings.Networks"
|
||||||
|
|
|
@ -16,10 +16,18 @@ angular.module('portainer.docker').controller('CreateNetworkController', [
|
||||||
function ($q, $scope, $state, PluginService, Notifications, NetworkService, LabelHelper, Authentication, ResourceControlService, FormValidator, HttpRequestHelper) {
|
function ($q, $scope, $state, PluginService, Notifications, NetworkService, LabelHelper, Authentication, ResourceControlService, FormValidator, HttpRequestHelper) {
|
||||||
$scope.formValues = {
|
$scope.formValues = {
|
||||||
DriverOptions: [],
|
DriverOptions: [],
|
||||||
Subnet: '',
|
IPV4: {
|
||||||
Gateway: '',
|
Subnet: '',
|
||||||
IPRange: '',
|
Gateway: '',
|
||||||
AuxAddress: '',
|
IPRange: '',
|
||||||
|
AuxAddress: ''
|
||||||
|
},
|
||||||
|
IPV6: {
|
||||||
|
Subnet: '',
|
||||||
|
Gateway: '',
|
||||||
|
IPRange: '',
|
||||||
|
AuxAddress: ''
|
||||||
|
},
|
||||||
Labels: [],
|
Labels: [],
|
||||||
AccessControlData: new AccessControlFormData(),
|
AccessControlData: new AccessControlFormData(),
|
||||||
NodeName: null,
|
NodeName: null,
|
||||||
|
@ -38,6 +46,7 @@ angular.module('portainer.docker').controller('CreateNetworkController', [
|
||||||
CheckDuplicate: true,
|
CheckDuplicate: true,
|
||||||
Internal: false,
|
Internal: false,
|
||||||
Attachable: false,
|
Attachable: false,
|
||||||
|
EnableIPv6: false,
|
||||||
// Force IPAM Driver to 'default', should not be required.
|
// Force IPAM Driver to 'default', should not be required.
|
||||||
// See: https://github.com/docker/docker/issues/25735
|
// See: https://github.com/docker/docker/issues/25735
|
||||||
IPAM: {
|
IPAM: {
|
||||||
|
@ -70,20 +79,35 @@ angular.module('portainer.docker').controller('CreateNetworkController', [
|
||||||
};
|
};
|
||||||
|
|
||||||
function prepareIPAMConfiguration(config) {
|
function prepareIPAMConfiguration(config) {
|
||||||
if ($scope.formValues.Subnet) {
|
if ($scope.formValues.IPV4.Subnet) {
|
||||||
var ipamConfig = {};
|
let ipamConfig = {};
|
||||||
ipamConfig.Subnet = $scope.formValues.Subnet;
|
ipamConfig.Subnet = $scope.formValues.IPV4.Subnet;
|
||||||
if ($scope.formValues.Gateway) {
|
if ($scope.formValues.IPV4.Gateway) {
|
||||||
ipamConfig.Gateway = $scope.formValues.Gateway;
|
ipamConfig.Gateway = $scope.formValues.IPV4.Gateway;
|
||||||
}
|
}
|
||||||
if ($scope.formValues.IPRange) {
|
if ($scope.formValues.IPV4.IPRange) {
|
||||||
ipamConfig.IPRange = $scope.formValues.IPRange;
|
ipamConfig.IPRange = $scope.formValues.IPV4.IPRange;
|
||||||
}
|
}
|
||||||
if ($scope.formValues.AuxAddress) {
|
if ($scope.formValues.IPV4.AuxAddress) {
|
||||||
ipamConfig.AuxAddress = $scope.formValues.AuxAddress;
|
ipamConfig.AuxAddress = $scope.formValues.IPV4.AuxAddress;
|
||||||
}
|
}
|
||||||
config.IPAM.Config.push(ipamConfig);
|
config.IPAM.Config.push(ipamConfig);
|
||||||
}
|
}
|
||||||
|
if ($scope.formValues.IPV6.Subnet) {
|
||||||
|
let ipamConfig = {};
|
||||||
|
ipamConfig.Subnet = $scope.formValues.IPV6.Subnet;
|
||||||
|
if ($scope.formValues.IPV6.Gateway) {
|
||||||
|
ipamConfig.Gateway = $scope.formValues.IPV6.Gateway;
|
||||||
|
}
|
||||||
|
if ($scope.formValues.IPV6.IPRange) {
|
||||||
|
ipamConfig.IPRange = $scope.formValues.IPV6.IPRange;
|
||||||
|
}
|
||||||
|
if ($scope.formValues.IPV6.AuxAddress) {
|
||||||
|
ipamConfig.AuxAddress = $scope.formValues.IPV6.AuxAddress;
|
||||||
|
}
|
||||||
|
config.EnableIPv6 = true;
|
||||||
|
config.IPAM.Config.push(ipamConfig);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function prepareDriverOptions(config) {
|
function prepareDriverOptions(config) {
|
||||||
|
|
|
@ -68,29 +68,58 @@
|
||||||
<!-- !macvlan-management -->
|
<!-- !macvlan-management -->
|
||||||
<div ng-hide="config.Driver === 'macvlan' && formValues.Macvlan.Scope === 'swarm'">
|
<div ng-hide="config.Driver === 'macvlan' && formValues.Macvlan.Scope === 'swarm'">
|
||||||
<div class="col-sm-12 form-section-title">
|
<div class="col-sm-12 form-section-title">
|
||||||
Network configuration
|
IPV4 Network configuration
|
||||||
</div>
|
</div>
|
||||||
<!-- subnet-gateway-inputs -->
|
<!-- subnet-gateway-inputs -->
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="network_subnet" class="col-sm-2 col-lg-1 control-label text-left">Subnet</label>
|
<label for="ipv4_network_subnet" class="col-sm-2 col-lg-1 control-label text-left">Subnet</label>
|
||||||
<div class="col-sm-4 col-lg-5">
|
<div class="col-sm-4 col-lg-5">
|
||||||
<input type="text" class="form-control" ng-model="formValues.Subnet" id="network_subnet" placeholder="e.g. 172.20.0.0/16" />
|
<input type="text" class="form-control" ng-model="formValues.IPV4.Subnet" id="ipv4_network_subnet" placeholder="e.g. 172.20.0.0/16" />
|
||||||
</div>
|
</div>
|
||||||
<label for="network_gateway" class="col-sm-2 col-lg-1 control-label text-left">Gateway</label>
|
<label for="ipv4_network_gateway" class="col-sm-2 col-lg-1 control-label text-left">Gateway</label>
|
||||||
<div class="col-sm-4 col-lg-5">
|
<div class="col-sm-4 col-lg-5">
|
||||||
<input type="text" class="form-control" ng-model="formValues.Gateway" id="network_gateway" placeholder="e.g. 172.20.10.11" />
|
<input type="text" class="form-control" ng-model="formValues.IPV4.Gateway" id="ipv4_network_gateway" placeholder="e.g. 172.20.10.11" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- !subnet-gateway-inputs -->
|
<!-- !subnet-gateway-inputs -->
|
||||||
<!-- iprange-auxaddr-inputs -->
|
<!-- iprange-auxaddr-inputs -->
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="network_iprange" class="col-sm-2 col-lg-1 control-label text-left">IP range</label>
|
<label for="ipv4_network_iprange" class="col-sm-2 col-lg-1 control-label text-left">IP range</label>
|
||||||
<div class="col-sm-4 col-lg-5">
|
<div class="col-sm-4 col-lg-5">
|
||||||
<input type="text" class="form-control" ng-model="formValues.IPRange" id="network_iprange" placeholder="e.g. 172.20.10.128/25" />
|
<input type="text" class="form-control" ng-model="formValues.IPV4.IPRange" id="ipv4_network_iprange" placeholder="e.g. 172.20.10.128/25" />
|
||||||
</div>
|
</div>
|
||||||
<label for="network_auxaddr" class="col-sm-2 col-lg-1 control-label text-left">Exclude IPs</label>
|
<label for="ipv4_network_auxaddr" class="col-sm-2 col-lg-1 control-label text-left">Exclude IPs</label>
|
||||||
<div class="col-sm-4 col-lg-5">
|
<div class="col-sm-4 col-lg-5">
|
||||||
<input type="text" class="form-control" ng-model="formValues.AuxAddress" id="network_auxaddr" placeholder="e.g. my-router=172.20.10.129" />
|
<input type="text" class="form-control" ng-model="formValues.IPV4.AuxAddress" id="ipv4_network_auxaddr" placeholder="e.g. my-router=172.20.10.129" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- !iprange-auxaddr-inputs -->
|
||||||
|
</div>
|
||||||
|
<div ng-show="config.Driver === 'bridge' || (config.Driver === 'macvlan' && formValues.Macvlan.Scope !== 'swarm')">
|
||||||
|
<div class="col-sm-12 form-section-title">
|
||||||
|
IPV6 Network configuration
|
||||||
|
</div>
|
||||||
|
<!-- subnet-gateway-inputs -->
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="ipv6_network_subnet" class="col-sm-2 col-lg-1 control-label text-left">Subnet</label>
|
||||||
|
<div class="col-sm-4 col-lg-5">
|
||||||
|
<input type="text" class="form-control" ng-model="formValues.IPV6.Subnet" id="ipv6_network_subnet" placeholder="e.g. 2001:db8::/48" />
|
||||||
|
</div>
|
||||||
|
<label for="ipv6_network_gateway" class="col-sm-2 col-lg-1 control-label text-left">Gateway</label>
|
||||||
|
<div class="col-sm-4 col-lg-5">
|
||||||
|
<input type="text" class="form-control" ng-model="formValues.IPV6.Gateway" id="ipv6_network_gateway" placeholder="e.g. 2001:db8::1" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- !subnet-gateway-inputs -->
|
||||||
|
<!-- iprange-auxaddr-inputs -->
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="ipv6_network_iprange" class="col-sm-2 col-lg-1 control-label text-left">IP range</label>
|
||||||
|
<div class="col-sm-4 col-lg-5">
|
||||||
|
<input type="text" class="form-control" ng-model="formValues.IPV6.IPRange" id="ipv6_network_iprange" placeholder="e.g. 2001:db8::/64" />
|
||||||
|
</div>
|
||||||
|
<label for="ipv6_network_auxaddr" class="col-sm-2 col-lg-1 control-label text-left">Exclude IPs</label>
|
||||||
|
<div class="col-sm-4 col-lg-5">
|
||||||
|
<input type="text" class="form-control" ng-model="formValues.IPV6.AuxAddress" id="ipv6_network_auxaddr" placeholder="e.g. my-router=2001:db8::1" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- !iprange-auxaddr-inputs -->
|
<!-- !iprange-auxaddr-inputs -->
|
||||||
|
|
|
@ -41,9 +41,21 @@
|
||||||
<td>Internal</td>
|
<td>Internal</td>
|
||||||
<td>{{ network.Internal }}</td>
|
<td>{{ network.Internal }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr ng-if="network.IPAM.Config.length > 0" ng-repeat="config in network.IPAM.Config">
|
<tr ng-if="network.IPAM.IPV4Configs.length > 0" ng-repeat-start="config in network.IPAM.IPV4Configs">
|
||||||
<td>Subnet - {{ config.Subnet }}</td>
|
<td>IPV4 Subnet - {{ config.Subnet }}</td>
|
||||||
<td>Gateway - {{ config.Gateway }}</td>
|
<td>IPV4 Gateway - {{ config.Gateway }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr ng-if="network.IPAM.IPV4Configs.length > 0" ng-repeat-end>
|
||||||
|
<td>IPV4 IP range - {{ config.IPRange }}</td>
|
||||||
|
<td>IPV4 Excluded Ips - {{ config.AuxAddress }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr ng-if="network.IPAM.IPV6Configs.length > 0" ng-repeat-start="config in network.IPAM.IPV6Configs">
|
||||||
|
<td>IPV6 Subnet - {{ config.Subnet }}</td>
|
||||||
|
<td>IPV6 Gateway - {{ config.Gateway }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr ng-if="network.IPAM.IPV6Configs.length > 0" ng-repeat-end>
|
||||||
|
<td>IPV6 IP range - {{ config.IPRange }}</td>
|
||||||
|
<td>IPV6 Excluded Ips - {{ config.AuxAddress }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import DockerNetworkHelper from 'Docker/helpers/networkHelper';
|
||||||
|
|
||||||
angular.module('portainer.docker').controller('NetworkController', [
|
angular.module('portainer.docker').controller('NetworkController', [
|
||||||
'$scope',
|
'$scope',
|
||||||
'$state',
|
'$state',
|
||||||
|
@ -101,6 +103,8 @@ angular.module('portainer.docker').controller('NetworkController', [
|
||||||
if (endpointProvider !== 'VMWARE_VIC') {
|
if (endpointProvider !== 'VMWARE_VIC') {
|
||||||
getContainersInNetwork(data);
|
getContainersInNetwork(data);
|
||||||
}
|
}
|
||||||
|
$scope.network.IPAM.IPV4Configs = DockerNetworkHelper.getIPV4Configs($scope.network.IPAM.Config);
|
||||||
|
$scope.network.IPAM.IPV6Configs = DockerNetworkHelper.getIPV6Configs($scope.network.IPAM.Config);
|
||||||
})
|
})
|
||||||
.catch(function error(err) {
|
.catch(function error(err) {
|
||||||
Notifications.error('Failure', err, 'Unable to retrieve network info');
|
Notifications.error('Failure', err, 'Unable to retrieve network info');
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import _ from 'lodash-es';
|
import _ from 'lodash-es';
|
||||||
|
import DockerNetworkHelper from 'Docker/helpers/networkHelper';
|
||||||
|
|
||||||
angular.module('portainer.docker').controller('NetworksController', [
|
angular.module('portainer.docker').controller('NetworksController', [
|
||||||
'$q',
|
'$q',
|
||||||
|
@ -71,6 +72,11 @@ angular.module('portainer.docker').controller('NetworksController', [
|
||||||
} else {
|
} else {
|
||||||
$scope.networks = networks;
|
$scope.networks = networks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_.forEach($scope.networks, (network) => {
|
||||||
|
network.IPAM.IPV4Configs = DockerNetworkHelper.getIPV4Configs(network.IPAM.Config);
|
||||||
|
network.IPAM.IPV6Configs = DockerNetworkHelper.getIPV6Configs(network.IPAM.Config);
|
||||||
|
});
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
$scope.networks = [];
|
$scope.networks = [];
|
||||||
|
|
|
@ -214,9 +214,9 @@ angular.module('portainer.docker').controller('CreateServiceController', [
|
||||||
}
|
}
|
||||||
|
|
||||||
function preparePortsConfig(config, input) {
|
function preparePortsConfig(config, input) {
|
||||||
var ports = [];
|
let ports = [];
|
||||||
input.Ports.forEach(function (binding) {
|
input.Ports.forEach(function (binding) {
|
||||||
var port = {
|
const port = {
|
||||||
Protocol: binding.Protocol,
|
Protocol: binding.Protocol,
|
||||||
PublishMode: binding.PublishMode,
|
PublishMode: binding.PublishMode,
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue