1/**
2 * Controller for network
3 *
4 * @module app/configuration
5 * @exports networkController
6 * @name networkController
7 */
8
9window.angular && (function(angular) {
10  'use strict';
11
12  angular.module('app.configuration').controller('networkController', [
13    '$scope', '$window', 'APIUtils', 'dataService', '$timeout', '$route', '$q',
14    'toastService',
15    function(
16        $scope, $window, APIUtils, dataService, $timeout, $route, $q,
17        toastService) {
18      $scope.dataService = dataService;
19      $scope.network = {};
20      $scope.oldInterface = {};
21      $scope.interface = {};
22      $scope.networkDevice = false;
23      $scope.hostname = '';
24      $scope.defaultGateway = '';
25      $scope.selectedInterface = '';
26      $scope.confirmSettings = false;
27      $scope.loading = false;
28      $scope.ipv4sToDelete = [];
29
30      loadNetworkInfo();
31
32      $scope.selectInterface = function(interfaceId) {
33        $scope.interface = $scope.network.interfaces[interfaceId];
34        // Copy the interface so we know later if changes were made to the page
35        $scope.oldInterface = JSON.parse(JSON.stringify($scope.interface));
36        $scope.selectedInterface = interfaceId;
37        $scope.networkDevice = false;
38      };
39
40      $scope.addDNSField = function() {
41        $scope.interface.Nameservers.push('');
42      };
43
44      $scope.removeDNSField = function(index) {
45        $scope.interface.Nameservers.splice(index, 1);
46      };
47
48      $scope.addIpv4Field = function() {
49        $scope.interface.ipv4.values.push(
50            {Address: '', PrefixLength: '', Gateway: ''});
51      };
52
53      $scope.removeIpv4Address = function(index) {
54        // Check if the IPV4 being removed has an id. This indicates that it is
55        // an existing address and needs to be removed in the back end.
56        if ($scope.interface.ipv4.values[index].id) {
57          $scope.ipv4sToDelete.push($scope.interface.ipv4.values[index]);
58        }
59        $scope.interface.ipv4.values.splice(index, 1);
60      };
61
62      $scope.setNetworkSettings = function() {
63        // Hides the confirm network settings modal
64        $scope.confirmSettings = false;
65        $scope.loading = true;
66        var promises = [];
67
68        // MAC Address are case-insensitive
69        if ($scope.interface.MACAddress.toLowerCase() !=
70            dataService.mac_address.toLowerCase()) {
71          promises.push(setMACAddress());
72        }
73        if ($scope.defaultGateway != dataService.defaultgateway) {
74          promises.push(setDefaultGateway());
75        }
76        if ($scope.hostname != dataService.hostname) {
77          promises.push(setHostname());
78        }
79        if ($scope.interface.DHCPEnabled != $scope.oldInterface.DHCPEnabled) {
80          promises.push(setDHCPEnabled());
81        }
82
83        // Remove any empty strings from the array. Important because we add an
84        // empty string to the end so the user can add a new DNS server, if the
85        // user doesn't fill out the field, we don't want to add.
86        $scope.interface.Nameservers =
87            $scope.interface.Nameservers.filter(Boolean);
88        // toString() is a cheap way to compare 2 string arrays
89        if ($scope.interface.Nameservers.toString() !=
90            $scope.oldInterface.Nameservers.toString()) {
91          promises.push(setNameservers());
92        }
93
94        // Set IPV4 IP Addresses, Netmask Prefix Lengths, and Gateways
95        if (!$scope.interface.DHCPEnabled) {
96          // Delete existing IPV4 addresses that were removed
97          promises.push(removeIPV4s());
98          // Update any changed IPV4 addresses and add new
99          for (var i in $scope.interface.ipv4.values) {
100            if (!APIUtils.validIPV4IP(
101                    $scope.interface.ipv4.values[i].Address)) {
102              toastService.error(
103                  $scope.interface.ipv4.values[i].Address +
104                  ' invalid IP parameter');
105              $scope.loading = false;
106              return;
107            }
108            if ($scope.interface.ipv4.values[i].Gateway &&
109                !APIUtils.validIPV4IP(
110                    $scope.interface.ipv4.values[i].Gateway)) {
111              toastService.error(
112                  $scope.interface.ipv4.values[i].Address +
113                  ' invalid gateway parameter');
114              $scope.loading = false;
115              return;
116            }
117            // The netmask prefix length will be undefined if outside range
118            if (!$scope.interface.ipv4.values[i].PrefixLength) {
119              toastService.error(
120                  $scope.interface.ipv4.values[i].Address +
121                  ' invalid Prefix Length parameter');
122              $scope.loading = false;
123              return;
124            }
125            if ($scope.interface.ipv4.values[i].updateAddress ||
126                $scope.interface.ipv4.values[i].updateGateway ||
127                $scope.interface.ipv4.values[i].updatePrefix) {
128              // If IPV4 has an id it means it already exists in the back end,
129              // and in order to update it is required to remove previous IPV4
130              // address and add new one. See openbmc/openbmc/issues/2163.
131              // TODO: update to use PUT once issue 2163 is resolved.
132              if ($scope.interface.ipv4.values[i].id) {
133                promises.push(updateIPV4(i));
134              } else {
135                promises.push(addIPV4(i));
136              }
137            }
138          }
139        }
140
141        if (promises.length) {
142          $q.all(promises).then(
143              function(response) {
144                // Since an IPV4 interface (e.g. IP address, gateway, or
145                // netmask) edit is a delete then an add and the GUI can't
146                // calculate the interface id (e.g. 5c083707) beforehand and it
147                // is not returned by the REST call, openbmc#3227, reload the
148                // page after an edit, which makes a 2nd REST call. Do this for
149                // all network changes due to the possibility of a set network
150                // failing even though it returned success, openbmc#1641, and to
151                // update dataService and oldInterface to know which data has
152                // changed if the user continues to edit network settings.
153                // TODO: The reload is not ideal. Revisit this.
154                $timeout(function() {
155                  loadNetworkInfo();
156                  $scope.loading = false;
157                  toastService.success('Network settings saved');
158                }, 4000);
159              },
160              function(error) {
161                $scope.loading = false;
162                toastService.error('Network settings could not be saved');
163              })
164        } else {
165          $scope.loading = false;
166        }
167      };
168
169      function setMACAddress() {
170        return APIUtils
171            .setMACAddress(
172                $scope.selectedInterface, $scope.interface.MACAddress)
173            .then(
174                function(data) {},
175                function(error) {
176                  console.log(JSON.stringify(error));
177                  return $q.reject();
178                });
179      }
180
181      function setDefaultGateway() {
182        return APIUtils.setDefaultGateway($scope.defaultGateway)
183            .then(
184                function(data) {},
185                function(error) {
186                  console.log(JSON.stringify(error));
187                  return $q.reject();
188                });
189      }
190
191      function setHostname() {
192        return APIUtils.setHostname($scope.hostname)
193            .then(
194                function(data) {},
195                function(error) {
196                  console.log(JSON.stringify(error));
197                  return $q.reject();
198                });
199      }
200
201      function setDHCPEnabled() {
202        return APIUtils
203            .setDHCPEnabled(
204                $scope.selectedInterface, $scope.interface.DHCPEnabled)
205            .then(
206                function(data) {},
207                function(error) {
208                  console.log(JSON.stringify(error));
209                  return $q.reject();
210                });
211      }
212
213      function setNameservers() {
214        return APIUtils
215            .setNameservers(
216                $scope.selectedInterface, $scope.interface.Nameservers)
217            .then(
218                function(data) {},
219                function(error) {
220                  console.log(JSON.stringify(error));
221                  return $q.reject();
222                });
223      }
224
225      function removeIPV4s() {
226        return $scope.ipv4sToDelete.map(function(ipv4) {
227          return APIUtils.deleteIPV4($scope.selectedInterface, ipv4.id)
228              .then(
229                  function(data) {},
230                  function(error) {
231                    console.log(JSON.stringify(error));
232                    return $q.reject();
233                  })
234        });
235      }
236
237      function addIPV4(index) {
238        return APIUtils
239            .addIPV4(
240                $scope.selectedInterface,
241                $scope.interface.ipv4.values[index].Address,
242                $scope.interface.ipv4.values[index].PrefixLength,
243                $scope.interface.ipv4.values[index].Gateway)
244            .then(
245                function(data) {},
246                function(error) {
247                  console.log(JSON.stringify(error));
248                  return $q.reject();
249                })
250      }
251
252      function updateIPV4(index) {
253        // The correct way to edit an IPV4 interface is to remove it and then
254        // add a new one
255        return APIUtils
256            .deleteIPV4(
257                $scope.selectedInterface,
258                $scope.interface.ipv4.values[index].id)
259            .then(
260                function(data) {
261                  return APIUtils
262                      .addIPV4(
263                          $scope.selectedInterface,
264                          $scope.interface.ipv4.values[index].Address,
265                          $scope.interface.ipv4.values[index].PrefixLength,
266                          $scope.interface.ipv4.values[index].Gateway)
267                      .then(
268                          function(data) {},
269                          function(error) {
270                            console.log(JSON.stringify(error));
271                            return $q.reject();
272                          });
273                },
274                function(error) {
275                  console.log(JSON.stringify(error));
276                  return $q.reject();
277                });
278      }
279
280      $scope.refresh = function() {
281        loadNetworkInfo();
282      };
283
284      function loadNetworkInfo() {
285        APIUtils.getNetworkInfo().then(function(data) {
286          dataService.setNetworkInfo(data);
287          $scope.network = data.formatted_data;
288          $scope.hostname = data.hostname;
289          $scope.defaultGateway = data.defaultgateway;
290          if ($scope.network.interface_ids.length) {
291            // Use the first network interface if the user hasn't chosen one
292            if (!$scope.selectedInterface ||
293                !$scope.network.interfaces[$scope.selectedInterface]) {
294              $scope.selectedInterface = $scope.network.interface_ids[0];
295            }
296            $scope.interface =
297                $scope.network.interfaces[$scope.selectedInterface];
298            // Copy the interface so we know later if changes were made to the
299            // page
300            $scope.oldInterface = JSON.parse(JSON.stringify($scope.interface));
301          }
302          // Add id values and update flags to corresponding IPV4 objects
303          for (var i = 0; i < $scope.interface.ipv4.values.length; i++) {
304            $scope.interface.ipv4.values[i].id = $scope.interface.ipv4.ids[i];
305            $scope.interface.ipv4.values[i].updateAddress = false;
306            $scope.interface.ipv4.values[i].updateGateway = false;
307            $scope.interface.ipv4.values[i].updatePrefix = false;
308          }
309        });
310      }
311    }
312  ]);
313})(angular);
314