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 (!APIUtils.validIPV4IP(
109                    $scope.interface.ipv4.values[i].Gateway)) {
110              toastService.error(
111                  $scope.interface.ipv4.values[i].Address +
112                  ' invalid gateway parameter');
113              $scope.loading = false;
114              return;
115            }
116            // The netmask prefix length will be undefined if outside range
117            if (!$scope.interface.ipv4.values[i].PrefixLength) {
118              toastService.error(
119                  $scope.interface.ipv4.values[i].Address +
120                  ' invalid Prefix Length parameter');
121              $scope.loading = false;
122              return;
123            }
124            if ($scope.interface.ipv4.values[i].updateAddress ||
125                $scope.interface.ipv4.values[i].updateGateway ||
126                $scope.interface.ipv4.values[i].updatePrefix) {
127              // If IPV4 has an id it means it already exists in the back end,
128              // and in order to update it is required to remove previous IPV4
129              // address and add new one. See openbmc/openbmc/issues/2163.
130              // TODO: update to use PUT once issue 2163 is resolved.
131              if ($scope.interface.ipv4.values[i].id) {
132                promises.push(updateIPV4(i));
133              } else {
134                promises.push(addIPV4(i));
135              }
136            }
137          }
138        }
139
140        if (promises.length) {
141          $q.all(promises).then(
142              function(response) {
143                // Since an IPV4 interface (e.g. IP address, gateway, or
144                // netmask) edit is a delete then an add and the GUI can't
145                // calculate the interface id (e.g. 5c083707) beforehand and it
146                // is not returned by the REST call, openbmc#3227, reload the
147                // page after an edit, which makes a 2nd REST call. Do this for
148                // all network changes due to the possibility of a set network
149                // failing even though it returned success, openbmc#1641, and to
150                // update dataService and oldInterface to know which data has
151                // changed if the user continues to edit network settings.
152                // TODO: The reload is not ideal. Revisit this.
153                $timeout(function() {
154                  loadNetworkInfo();
155                  $scope.loading = false;
156                  toastService.success('Network settings saved');
157                }, 4000);
158              },
159              function(error) {
160                $scope.loading = false;
161                toastService.error('Network settings could not be saved');
162              })
163        } else {
164          $scope.loading = false;
165        }
166      };
167
168      function setMACAddress() {
169        return APIUtils
170            .setMACAddress(
171                $scope.selectedInterface, $scope.interface.MACAddress)
172            .then(
173                function(data) {},
174                function(error) {
175                  console.log(JSON.stringify(error));
176                  return $q.reject();
177                });
178      }
179
180      function setDefaultGateway() {
181        return APIUtils.setDefaultGateway($scope.defaultGateway)
182            .then(
183                function(data) {},
184                function(error) {
185                  console.log(JSON.stringify(error));
186                  return $q.reject();
187                });
188      }
189
190      function setHostname() {
191        return APIUtils.setHostname($scope.hostname)
192            .then(
193                function(data) {},
194                function(error) {
195                  console.log(JSON.stringify(error));
196                  return $q.reject();
197                });
198      }
199
200      function setDHCPEnabled() {
201        return APIUtils
202            .setDHCPEnabled(
203                $scope.selectedInterface, $scope.interface.DHCPEnabled)
204            .then(
205                function(data) {},
206                function(error) {
207                  console.log(JSON.stringify(error));
208                  return $q.reject();
209                });
210      }
211
212      function setNameservers() {
213        // Nameservers does not allow an empty array, since we remove all empty
214        // strings above, could have an empty array. TODO: openbmc/openbmc#3240
215        if ($scope.interface.Nameservers.length == 0) {
216          $scope.interface.Nameservers.push('');
217        }
218        return APIUtils
219            .setNameservers(
220                $scope.selectedInterface, $scope.interface.Nameservers)
221            .then(
222                function(data) {},
223                function(error) {
224                  console.log(JSON.stringify(error));
225                  return $q.reject();
226                });
227      }
228
229      function removeIPV4s() {
230        return $scope.ipv4sToDelete.map(function(ipv4) {
231          return APIUtils.deleteIPV4($scope.selectedInterface, ipv4.id)
232              .then(
233                  function(data) {},
234                  function(error) {
235                    console.log(JSON.stringify(error));
236                    return $q.reject();
237                  })
238        });
239      }
240
241      function addIPV4(index) {
242        return APIUtils
243            .addIPV4(
244                $scope.selectedInterface,
245                $scope.interface.ipv4.values[index].Address,
246                $scope.interface.ipv4.values[index].PrefixLength,
247                $scope.interface.ipv4.values[index].Gateway)
248            .then(
249                function(data) {},
250                function(error) {
251                  console.log(JSON.stringify(error));
252                  return $q.reject();
253                })
254      }
255
256      function updateIPV4(index) {
257        // The correct way to edit an IPV4 interface is to remove it and then
258        // add a new one
259        return APIUtils
260            .deleteIPV4(
261                $scope.selectedInterface,
262                $scope.interface.ipv4.values[index].id)
263            .then(
264                function(data) {
265                  return APIUtils
266                      .addIPV4(
267                          $scope.selectedInterface,
268                          $scope.interface.ipv4.values[index].Address,
269                          $scope.interface.ipv4.values[index].PrefixLength,
270                          $scope.interface.ipv4.values[index].Gateway)
271                      .then(
272                          function(data) {},
273                          function(error) {
274                            console.log(JSON.stringify(error));
275                            return $q.reject();
276                          });
277                },
278                function(error) {
279                  console.log(JSON.stringify(error));
280                  return $q.reject();
281                });
282      }
283
284      $scope.refresh = function() {
285        loadNetworkInfo();
286      };
287
288      function loadNetworkInfo() {
289        APIUtils.getNetworkInfo().then(function(data) {
290          dataService.setNetworkInfo(data);
291          $scope.network = data.formatted_data;
292          $scope.hostname = data.hostname;
293          $scope.defaultGateway = data.defaultgateway;
294          if ($scope.network.interface_ids.length) {
295            // Use the first network interface if the user hasn't chosen one
296            if (!$scope.selectedInterface ||
297                !$scope.network.interfaces[$scope.selectedInterface]) {
298              $scope.selectedInterface = $scope.network.interface_ids[0];
299            }
300            $scope.interface =
301                $scope.network.interfaces[$scope.selectedInterface];
302            // Copy the interface so we know later if changes were made to the
303            // page
304            $scope.oldInterface = JSON.parse(JSON.stringify($scope.interface));
305          }
306          // Add id values and update flags to corresponding IPV4 objects
307          for (var i = 0; i < $scope.interface.ipv4.values.length; i++) {
308            $scope.interface.ipv4.values[i].id = $scope.interface.ipv4.ids[i];
309            $scope.interface.ipv4.values[i].updateAddress = false;
310            $scope.interface.ipv4.values[i].updateGateway = false;
311            $scope.interface.ipv4.values[i].updatePrefix = false;
312          }
313        });
314      }
315    }
316  ]);
317})(angular);
318