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