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    function($scope, $window, APIUtils, dataService, $timeout, $route, $q) {
15      $scope.dataService = dataService;
16      $scope.network = {};
17      $scope.oldInterface = {};
18      $scope.interface = {};
19      $scope.networkDevice = false;
20      $scope.hostname = '';
21      $scope.defaultGateway = '';
22      $scope.setNetworkError = '';
23      $scope.setNetworkSuccess = false;
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.setNetworkError = '';
65        $scope.setNetworkSuccess = false;
66        $scope.loading = true;
67        var promises = [];
68
69        // MAC Address are case-insensitive
70        if ($scope.interface.MACAddress.toLowerCase() !=
71            dataService.mac_address.toLowerCase()) {
72          promises.push(setMACAddress());
73        }
74        if ($scope.defaultGateway != dataService.defaultgateway) {
75          promises.push(setDefaultGateway());
76        }
77        if ($scope.hostname != dataService.hostname) {
78          promises.push(setHostname());
79        }
80        if ($scope.interface.DHCPEnabled != $scope.oldInterface.DHCPEnabled) {
81          promises.push(setDHCPEnabled());
82        }
83
84        // Remove any empty strings from the array. Important because we add an
85        // empty string to the end so the user can add a new DNS server, if the
86        // user doesn't fill out the field, we don't want to add.
87        $scope.interface.Nameservers =
88            $scope.interface.Nameservers.filter(Boolean);
89        // toString() is a cheap way to compare 2 string arrays
90        if ($scope.interface.Nameservers.toString() !=
91            $scope.oldInterface.Nameservers.toString()) {
92          promises.push(setNameservers());
93        }
94
95        // Set IPV4 IP Addresses, Netmask Prefix Lengths, and Gateways
96        if (!$scope.interface.DHCPEnabled) {
97          // Delete existing IPV4 addresses that were removed
98          promises.push(removeIPV4s());
99          // Update any changed IPV4 addresses and add new
100          for (var i in $scope.interface.ipv4.values) {
101            if (!APIUtils.validIPV4IP(
102                    $scope.interface.ipv4.values[i].Address)) {
103              $scope.setNetworkError = $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              $scope.setNetworkError = $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              $scope.setNetworkError = $scope.interface.ipv4.values[i].Address +
118                  ' invalid Prefix Length parameter';
119              $scope.loading = false;
120              return;
121            }
122            if ($scope.interface.ipv4.values[i].updateAddress ||
123                $scope.interface.ipv4.values[i].updateGateway ||
124                $scope.interface.ipv4.values[i].updatePrefix) {
125              // If IPV4 has an id it means it already exists in the back end,
126              // and in order to update it is required to remove previous IPV4
127              // address and add new one. See openbmc/openbmc/issues/2163.
128              // TODO: update to use PUT once issue 2163 is resolved.
129              if ($scope.interface.ipv4.values[i].id) {
130                promises.push(updateIPV4(i));
131              } else {
132                promises.push(addIPV4(i));
133              }
134            }
135          }
136        }
137
138        if (promises.length) {
139          $q.all(promises).finally(function() {
140            $scope.loading = false;
141            if (!$scope.setNetworkError) {
142              $scope.setNetworkSuccess = true;
143              // Since an IPV4 interface (e.g. IP address, gateway, or netmask)
144              // edit is a delete then an add and the GUI can't calculate the
145              // interface id (e.g. 5c083707) beforehand and it is not returned
146              // by the REST call, openbmc#3227, reload the page after an edit,
147              // which makes a 2nd REST call.
148              // Do this for all network changes due to the possibility of a set
149              // network failing even though it returned success, openbmc#1641,
150              // and to update dataService and oldInterface to know which
151              // data has changed if the user continues to edit network
152              // settings.
153              // TODO: The reload is not ideal. Revisit this.
154              $timeout(function() {
155                loadNetworkInfo();
156              }, 4000);
157            }
158          });
159        } else {
160          $scope.loading = false;
161        }
162      };
163
164      function setMACAddress() {
165        return APIUtils
166            .setMACAddress(
167                $scope.selectedInterface, $scope.interface.MACAddress)
168            .then(
169                function(data) {},
170                function(error) {
171                  console.log(JSON.stringify(error));
172                  $scope.setNetworkError = 'MAC Address';
173                });
174      }
175
176      function setDefaultGateway() {
177        return APIUtils.setDefaultGateway($scope.defaultGateway)
178            .then(
179                function(data) {},
180                function(error) {
181                  console.log(JSON.stringify(error));
182                  $scope.setNetworkError = 'Default Gateway';
183                });
184      }
185
186      function setHostname() {
187        return APIUtils.setHostname($scope.hostname)
188            .then(
189                function(data) {},
190                function(error) {
191                  console.log(JSON.stringify(error));
192                  $scope.setNetworkError = 'Hostname';
193                });
194      }
195
196      function setDHCPEnabled() {
197        return APIUtils
198            .setDHCPEnabled(
199                $scope.selectedInterface, $scope.interface.DHCPEnabled)
200            .then(
201                function(data) {},
202                function(error) {
203                  console.log(JSON.stringify(error));
204                  $scope.setNetworkError = 'DHCP';
205                });
206      }
207
208      function setNameservers() {
209        // Nameservers does not allow an empty array, since we remove all empty
210        // strings above, could have an empty array. TODO: openbmc/openbmc#3240
211        if ($scope.interface.Nameservers.length == 0) {
212          $scope.interface.Nameservers.push('');
213        }
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                  $scope.setNetworkError = 'DNS Servers';
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                    $scope.setNetworkError = ipv4.Address;
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                  $scope.setNetworkError =
249                      $scope.interface.ipv4.values[index].Address;
250                })
251      }
252
253      function updateIPV4(index) {
254        // The correct way to edit an IPV4 interface is to remove it and then
255        // add a new one
256        return APIUtils
257            .deleteIPV4(
258                $scope.selectedInterface,
259                $scope.interface.ipv4.values[index].id)
260            .then(
261                function(data) {
262                  return APIUtils
263                      .addIPV4(
264                          $scope.selectedInterface,
265                          $scope.interface.ipv4.values[index].Address,
266                          $scope.interface.ipv4.values[index].PrefixLength,
267                          $scope.interface.ipv4.values[index].Gateway)
268                      .then(
269                          function(data) {},
270                          function(error) {
271                            console.log(JSON.stringify(error));
272                            $scope.setNetworkError =
273                                $scope.interface.ipv4.values[index].Address;
274                          });
275                },
276                function(error) {
277                  console.log(JSON.stringify(error));
278                  $scope.setNetworkError =
279                      $scope.interface.ipv4.values[index].Address;
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