73e7143ae33c301067d3f705fd4efe77c180e82c
[so.git] / adapters / mso-openstack-adapters / src / main / java / org / onap / so / adapters / network / MsoNetworkAdapterImpl.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP - SO
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * Copyright (C) 2017 Huawei Technologies Co., Ltd. All rights reserved.
7  * ================================================================================
8  * Modifications Copyright (C) 2018 IBM.
9  * Modifications Copyright (c) 2019 Samsung
10  * ================================================================================
11  * Licensed under the Apache License, Version 2.0 (the "License");
12  * you may not use this file except in compliance with the License.
13  * You may obtain a copy of the License at
14  *
15  *      http://www.apache.org/licenses/LICENSE-2.0
16  *
17  * Unless required by applicable law or agreed to in writing, software
18  * distributed under the License is distributed on an "AS IS" BASIS,
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  * See the License for the specific language governing permissions and
21  * limitations under the License.
22  * ============LICENSE_END=========================================================
23  */
24
25 package org.onap.so.adapters.network;
26
27 import com.fasterxml.jackson.databind.JsonNode;
28 import com.fasterxml.jackson.databind.ObjectMapper;
29 import java.util.ArrayList;
30 import java.util.HashMap;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Optional;
34 import javax.jws.WebService;
35 import javax.xml.ws.Holder;
36 import org.onap.so.adapters.network.beans.ContrailPolicyRef;
37 import org.onap.so.adapters.network.beans.ContrailPolicyRefSeq;
38 import org.onap.so.adapters.network.beans.ContrailSubnet;
39 import org.onap.so.adapters.network.exceptions.NetworkException;
40 import org.onap.so.adapters.network.mappers.ContrailSubnetMapper;
41 import org.onap.so.cloud.CloudConfig;
42 import org.onap.so.db.catalog.beans.CloudSite;
43 import org.onap.so.db.catalog.beans.CollectionNetworkResourceCustomization;
44 import org.onap.so.db.catalog.beans.HeatTemplate;
45 import org.onap.so.db.catalog.beans.NetworkResource;
46 import org.onap.so.db.catalog.beans.NetworkResourceCustomization;
47 import org.onap.so.db.catalog.data.repository.CollectionNetworkResourceCustomizationRepository;
48 import org.onap.so.db.catalog.data.repository.NetworkResourceCustomizationRepository;
49 import org.onap.so.db.catalog.data.repository.NetworkResourceRepository;
50 import org.onap.so.db.catalog.utils.MavenLikeVersioning;
51 import org.onap.so.entity.MsoRequest;
52 import org.onap.so.logger.ErrorCode;
53 import org.onap.so.logger.MessageEnum;
54 import org.onap.so.openstack.beans.HeatStatus;
55 import org.onap.so.openstack.beans.NetworkInfo;
56 import org.onap.so.openstack.beans.NetworkRollback;
57 import org.onap.so.openstack.beans.NetworkStatus;
58 import org.onap.so.openstack.beans.Pool;
59 import org.onap.so.openstack.beans.RouteTarget;
60 import org.onap.so.openstack.beans.StackInfo;
61 import org.onap.so.openstack.beans.Subnet;
62 import org.onap.so.openstack.exceptions.MsoAdapterException;
63 import org.onap.so.openstack.exceptions.MsoException;
64 import org.onap.so.openstack.exceptions.MsoExceptionCategory;
65 import org.onap.so.openstack.utils.MsoCommonUtils;
66 import org.onap.so.openstack.utils.MsoHeatUtils;
67 import org.onap.so.openstack.utils.MsoHeatUtilsWithUpdate;
68 import org.onap.so.openstack.utils.MsoNeutronUtils;
69 import org.onap.so.openstack.utils.MsoNeutronUtils.NetworkType;
70 import org.slf4j.Logger;
71 import org.slf4j.LoggerFactory;
72 import org.springframework.beans.factory.annotation.Autowired;
73 import org.springframework.core.env.Environment;
74 import org.springframework.stereotype.Component;
75 import org.springframework.transaction.annotation.Transactional;
76
77 @Component
78 @Transactional
79 @WebService(serviceName = "NetworkAdapter", endpointInterface = "org.onap.so.adapters.network.MsoNetworkAdapter", targetNamespace = "http://org.onap.so/network")
80 public class MsoNetworkAdapterImpl implements MsoNetworkAdapter {
81
82         private static final String AIC3_NW_PROPERTY= "org.onap.so.adapters.network.aic3nw";
83         private static final String AIC3_NW="OS::ContrailV2::VirtualNetwork";
84     private static final String VLANS = "vlans";
85     private static final String PHYSICAL_NETWORK = "physical_network";
86     private static final String UPDATE_NETWORK_CONTEXT = "UpdateNetwork";
87     private static final String NETWORK_ID = "network_id";
88     private static final String NETWORK_FQDN = "network_fqdn";
89     private static final String CREATE_NETWORK_CONTEXT = "CreateNetwork";
90     private static final String MSO_CONFIGURATION_ERROR = "MsoConfigurationError";
91     private static final String NEUTRON_MODE = "NEUTRON";
92
93     private static final Logger logger = LoggerFactory.getLogger(MsoNetworkAdapterImpl.class);
94
95     @Autowired
96     private CloudConfig cloudConfig;
97     @Autowired
98     private Environment environment;
99     @Autowired
100     private MsoNeutronUtils neutron;
101     @Autowired
102     private MsoHeatUtils heat;
103     @Autowired
104     private MsoHeatUtilsWithUpdate heatWithUpdate;
105     @Autowired
106     private MsoCommonUtils commonUtils;
107
108     @Autowired
109     private NetworkResourceCustomizationRepository  networkCustomRepo;
110
111     @Autowired
112     private CollectionNetworkResourceCustomizationRepository collectionNetworkCustomRepo;
113
114     @Autowired
115     private NetworkResourceRepository  networkResourceRepo;
116     /**
117      * Health Check web method. Does nothing but return to show the adapter is deployed.
118      */
119     @Override
120     public void healthCheck () {
121         logger.debug ("Health check call in Network Adapter");
122     }
123
124     /**
125      * Do not use this constructor or the msoPropertiesFactory will be NULL.
126      *
127          * @see MsoNetworkAdapterImpl#MsoNetworkAdapterImpl(MsoPropertiesFactory)
128      */
129     public MsoNetworkAdapterImpl() {
130     }
131
132     @Override
133     public void createNetwork (String cloudSiteId,
134                                String tenantId,
135                                String networkType,
136                                String modelCustomizationUuid,
137                                String networkName,
138                                String physicalNetworkName,
139                                List <Integer> vlans,
140                                String shared,
141                                String external,
142                                Boolean failIfExists,
143                                Boolean backout,
144                                List <Subnet> subnets,
145                                Map<String, String> networkParams,
146                                MsoRequest msoRequest,
147                                Holder <String> networkId,
148                                Holder <String> neutronNetworkId,
149                                Holder <Map <String, String>> subnetIdMap,
150                                Holder <NetworkRollback> rollback) throws NetworkException {
151         Holder <String> networkFqdn = new Holder <> ();
152         createNetwork (cloudSiteId,
153                        tenantId,
154                        networkType,
155                        modelCustomizationUuid,
156                        networkName,
157                        physicalNetworkName,
158                        vlans,
159                        null,
160                        shared,
161                        external,
162                        failIfExists,
163                        backout,
164                        subnets,
165                        null,
166                        null,
167                        msoRequest,
168                        networkId,
169                        neutronNetworkId,
170                        networkFqdn,
171                        subnetIdMap,
172                        rollback);
173     }
174
175     @Override
176     public void createNetworkContrail (String cloudSiteId,
177                                        String tenantId,
178                                        String networkType,
179                                        String modelCustomizationUuid,
180                                        String networkName,
181                                        List <RouteTarget> routeTargets,
182                                        String shared,
183                                        String external,
184                                        Boolean failIfExists,
185                                        Boolean backout,
186                                        List <Subnet> subnets,
187                                        Map<String, String> networkParams,
188                                        List <String> policyFqdns,
189                                        List<String> routeTableFqdns,
190                                        MsoRequest msoRequest,
191                                        Holder <String> networkId,
192                                        Holder <String> neutronNetworkId,
193                                        Holder <String> networkFqdn,
194                                        Holder <Map <String, String>> subnetIdMap,
195                                        Holder <NetworkRollback> rollback) throws NetworkException {
196         createNetwork (cloudSiteId,
197                        tenantId,
198                        networkType,
199                        modelCustomizationUuid,
200                        networkName,
201                        null,
202                        null,
203                        routeTargets,
204                        shared,
205                        external,
206                        failIfExists,
207                        backout,
208                        subnets,
209                        policyFqdns,
210                        routeTableFqdns,
211                        msoRequest,
212                        networkId,
213                        neutronNetworkId,
214                        networkFqdn,
215                        subnetIdMap,
216                        rollback);
217     }
218
219     /**
220      * This is the "Create Network" web service implementation.
221      * It will create a new Network of the requested type in the specified cloud
222      * and tenant. The tenant must exist at the time this service is called.
223      *
224      * If a network with the same name already exists, this can be considered a
225      * success or failure, depending on the value of the 'failIfExists' parameter.
226      *
227      * There will be a pre-defined set of network types defined in the MSO Catalog.
228      * All such networks will have a similar configuration, based on the allowable
229      * Openstack networking definitions. This includes basic networks, provider
230      * networks (with a single VLAN), and multi-provider networks (one or more VLANs)
231      *
232      * Initially, all provider networks must be "vlan" type, and multiple segments in
233      * a multi-provider network must be multiple VLANs on the same physical network.
234      *
235      * This service supports two modes of Network creation/update:
236      * - via Heat Templates
237      * - via Neutron API
238      * The network orchestration mode for each network type is declared in its
239      * catalog definition. All Heat-based templates must support some subset of
240      * the same input parameters: network_name, physical_network, vlan(s).
241      *
242      * The method returns the network ID and a NetworkRollback object. This latter
243      * object can be passed as-is to the rollbackNetwork operation to undo everything
244      * that was created. This is useful if a network is successfully created but
245      * the orchestration fails on a subsequent operation.
246      */
247
248     private void createNetwork (String cloudSiteId,
249                                String tenantId,
250                                String networkType,
251                                String modelCustomizationUuid,
252                                String networkName,
253                                String physicalNetworkName,
254                                List <Integer> vlans,
255                                List <RouteTarget> routeTargets,
256                                String shared,
257                                String external,
258                                Boolean failIfExists,
259                                Boolean backout,
260                                List <Subnet> subnets,
261                                List <String> policyFqdns,
262                                List <String> routeTableFqdns,
263                                MsoRequest msoRequest,
264                                Holder <String> networkId,
265                                Holder <String> neutronNetworkId,
266                                Holder <String> networkFqdn,
267                                Holder <Map <String, String>> subnetIdMap,
268                                Holder <NetworkRollback> rollback) throws NetworkException {
269         logger.debug("*** CREATE Network: {} of type {} in {}/{}", networkName, networkType, cloudSiteId, tenantId);
270
271         // Will capture execution time for metrics
272         long startTime = System.currentTimeMillis ();
273
274         // Build a default rollback object (no actions performed)
275         NetworkRollback networkRollback = new NetworkRollback ();
276         networkRollback.setCloudId (cloudSiteId);
277         networkRollback.setTenantId (tenantId);
278         networkRollback.setMsoRequest (msoRequest);
279         networkRollback.setModelCustomizationUuid(modelCustomizationUuid);
280
281         // tenant query is not required here.
282         // If the tenant doesn't exist, the Heat calls will fail anyway (when the HeatUtils try to obtain a token).
283         // So this is just catching that error in a bit more obvious way up front.
284
285         Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
286         if (!cloudSiteOpt.isPresent())
287         {
288             String error = String
289                 .format("Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
290                     networkName, cloudSiteId, tenantId);
291             logger.error("{} {} {}", MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
292             // Set the detailed error as the Exception 'message'
293             throw new NetworkException(error, MsoExceptionCategory.USERDATA);
294         }
295
296
297             NetworkResource networkResource = networkCheck (startTime,
298                                                             networkType,
299                                                             modelCustomizationUuid,
300                                                             networkName,
301                                                             physicalNetworkName,
302                                                             vlans,
303                                                             routeTargets,
304                                                             cloudSiteId,
305                                                             cloudSiteOpt.get());
306             String mode = networkResource.getOrchestrationMode ();
307             NetworkType neutronNetworkType = NetworkType.valueOf (networkResource.getNeutronNetworkType ());
308
309             if (NEUTRON_MODE.equals (mode)) {
310
311                 // Use an MsoNeutronUtils for all neutron commands
312
313                 // See if the Network already exists (by name)
314                 NetworkInfo netInfo = null;
315                 long queryNetworkStarttime = System.currentTimeMillis ();
316                 try {
317                     netInfo = neutron.queryNetwork (networkName, tenantId, cloudSiteId);
318                 } catch (MsoException me) {
319                     logger.error(
320                         "{} {} Exception while querying network {} for CloudSite {} from Tenant {} from OpenStack ",
321                         MessageEnum.RA_QUERY_NETWORK_EXC, ErrorCode.BusinessProcesssError.getValue(),
322                         networkName, cloudSiteId, tenantId, me);
323                     me.addContext (CREATE_NETWORK_CONTEXT);
324                     throw new NetworkException (me);
325                 }
326
327                 if (netInfo != null) {
328                     // Exists. If that's OK, return success with the network ID.
329                     // Otherwise, return an exception.
330                     if (failIfExists != null && failIfExists) {
331                         String error = String
332                             .format("Create Nework: Network %s already exists in %s/%s with ID %s", networkName,
333                                 cloudSiteId, tenantId, netInfo.getId());
334                         logger.error("{} {} {}", MessageEnum.RA_NETWORK_ALREADY_EXIST,
335                             ErrorCode.DataError.getValue(), error);
336                         throw new NetworkException(error, MsoExceptionCategory.USERDATA);
337                     } else {
338                         // Populate the outputs from the existing network.
339                         networkId.value = netInfo.getId ();
340                         neutronNetworkId.value = netInfo.getId ();
341                         rollback.value = networkRollback; // Default rollback - no updates performed
342                         logger.warn("{} {} Found Existing network, status={} for Neutron mode ",
343                             MessageEnum.RA_NETWORK_ALREADY_EXIST, ErrorCode.DataError.getValue(),
344                             netInfo.getStatus());
345                     }
346                     return;
347                 }
348
349                 long createNetworkStarttime = System.currentTimeMillis ();
350                 try {
351                     netInfo = neutron.createNetwork (cloudSiteId,
352                                                      tenantId,
353                                                      neutronNetworkType,
354                                                      networkName,
355                                                      physicalNetworkName,
356                                                      vlans);
357                 } catch (MsoException me) {
358                     me.addContext(CREATE_NETWORK_CONTEXT);
359                     logger.error("{} {} Create Network: type {} in {}/{}: ", MessageEnum.RA_CREATE_NETWORK_EXC,
360                         ErrorCode.DataError.getValue(), neutronNetworkType, cloudSiteId, tenantId, me);
361
362                     throw new NetworkException (me);
363                 }
364
365                 // Note: ignoring MsoNetworkAlreadyExists because we already checked.
366
367                 // If reach this point, network creation is successful.
368                 // Since directly created via Neutron, networkId tracked by MSO is the same
369                 // as the neutron network ID.
370                 networkId.value = netInfo.getId ();
371                 neutronNetworkId.value = netInfo.getId ();
372
373                 networkRollback.setNetworkCreated (true);
374                 networkRollback.setNetworkId (netInfo.getId ());
375                 networkRollback.setNeutronNetworkId (netInfo.getId ());
376                 networkRollback.setNetworkType (networkType);
377
378                 logger.debug("Network {} created, id = {}", networkName, netInfo.getId());
379             } else if ("HEAT".equals (mode)) {
380
381                 HeatTemplate heatTemplate = networkResource.getHeatTemplate();
382                 if (heatTemplate == null) {
383                     String error = String
384                         .format("Network error - undefined Heat Template. Network Type = %s", networkType);
385                     logger.error("{} {} {}", MessageEnum.RA_PARAM_NOT_FOUND, ErrorCode.DataError.getValue(),
386                         error);
387                     throw new NetworkException (error, MsoExceptionCategory.INTERNAL);
388                 }
389
390                 logger.debug("Got HEAT Template from DB: {}", heatTemplate.toString());
391
392                 // "Fix" the template if it has CR/LF (getting this from Oracle)
393                 String template = heatTemplate.getHeatTemplate ();
394                 template = template.replaceAll ("\r\n", "\n");
395
396                 boolean aic3template=false;
397                 String aic3nw = AIC3_NW;
398
399                 aic3nw = environment.getProperty(AIC3_NW_PROPERTY, AIC3_NW);
400
401                 if (template.contains(aic3nw))
402                         aic3template = true;
403
404                 // First, look up to see if the Network already exists (by name).
405                 // For HEAT orchestration of networks, the stack name will always match the network name
406                 StackInfo heatStack = null;
407                 long queryNetworkStarttime = System.currentTimeMillis ();
408                 try {
409                     heatStack = heat.queryStack (cloudSiteId, "CloudOwner", tenantId, networkName);
410                 } catch (MsoException me) {
411                     me.addContext (CREATE_NETWORK_CONTEXT);
412                     logger.error("{} {} Create Network (heat): query network {} in {}/{}: ",
413                         MessageEnum.RA_QUERY_NETWORK_EXC, ErrorCode.DataError.getValue(), networkName,
414                         cloudSiteId, tenantId, me);
415                     throw new NetworkException (me);
416                 }
417
418                 if (heatStack != null && (heatStack.getStatus () != HeatStatus.NOTFOUND)) {
419                     // Stack exists. Return success or error depending on input directive
420                     if (failIfExists != null && failIfExists) {
421                         String error = String
422                             .format("CreateNetwork: Stack %s already exists in %s/%s as %s", networkName, cloudSiteId,
423                                 tenantId, heatStack.getCanonicalName());
424                         logger.error("{} {} {}", MessageEnum.RA_NETWORK_ALREADY_EXIST,
425                             ErrorCode.DataError.getValue(), error);
426                         throw new NetworkException(error, MsoExceptionCategory.USERDATA);
427                     } else {
428                         // Populate the outputs from the existing stack.
429                         networkId.value = heatStack.getCanonicalName ();
430                         neutronNetworkId.value = (String) heatStack.getOutputs ().get (NETWORK_ID);
431                         rollback.value = networkRollback; // Default rollback - no updates performed
432                         if (aic3template)
433                         {
434                                 networkFqdn.value = (String) heatStack.getOutputs().get(NETWORK_FQDN);
435                         }
436                         Map <String, Object> outputs = heatStack.getOutputs ();
437                         Map <String, String> sMap = new HashMap <> ();
438                         if (outputs != null) {
439                                 for (Map.Entry<String, Object> entry : outputs.entrySet()) {
440                                         String key=entry.getKey();
441                                         if (key != null && key.startsWith ("subnet")) {
442                                                 if (aic3template) //one subnet_id output
443                                                 {
444                                                          Map <String, String> map = getSubnetUUId(key, outputs, subnets);
445                                                          sMap.putAll(map);
446                                                 }
447                                                 else //multiples subnet_%aaid% outputs
448                                                 {
449                                                         String subnetUUId = (String) outputs.get(key);
450                                                         sMap.put (key.substring("subnet_id_".length()), subnetUUId);
451                                                 }
452                                         }
453                                 }
454                         }
455                         subnetIdMap.value = sMap;
456                         logger.warn("{} {} Found Existing network stack, status={} networkName={} for {}/{}",
457                             MessageEnum.RA_NETWORK_ALREADY_EXIST, ErrorCode.DataError.getValue(),
458                             heatStack.getStatus(), networkName, cloudSiteId, tenantId);
459                     }
460                     return;
461                 }
462
463                 // Ready to deploy the new Network
464                 // Build the common set of HEAT template parameters
465                 Map <String, Object> stackParams = populateNetworkParams (neutronNetworkType,
466                                                                           networkName,
467                                                                           physicalNetworkName,
468                                                                           vlans,
469                                                                           routeTargets,
470                                                                           shared,
471                                                                           external,
472                                                                           aic3template);
473
474                 // Validate (and update) the input parameters against the DB definition
475                 // Shouldn't happen unless DB config is wrong, since all networks use same inputs
476                 // and inputs were already validated.
477                 try {
478                     stackParams = heat.validateStackParams (stackParams, heatTemplate);
479                 } catch (IllegalArgumentException e) {
480                     String error = "Create Network: Configuration Error: " + e.getMessage ();
481                     logger.error("{} {} {} ", MessageEnum.RA_CONFIG_EXC,
482                         ErrorCode.DataError.getValue(), error,e);
483                     // Input parameters were not valid
484                     throw new NetworkException (error, MsoExceptionCategory.INTERNAL);
485                 }
486
487                 if (subnets != null) {
488                         try {
489                                 if (aic3template)
490                                 {
491                                         template = mergeSubnetsAIC3 (template, subnets, stackParams);
492                                 }
493                                 else
494                                 {
495                                         template = mergeSubnets (template, subnets);
496                                 }
497                         } catch (MsoException me) {
498                                 me.addContext (CREATE_NETWORK_CONTEXT);
499                       logger
500                           .error("{} {} Exception Create Network, merging subnets for network (heat) type {} in {}/{} ",
501                               MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
502                               neutronNetworkType.toString(), cloudSiteId, tenantId, me);
503                                 throw new NetworkException (me);
504                         }
505                 }
506
507                 if (policyFqdns != null && !policyFqdns.isEmpty() && aic3template) {
508                     try {
509                         mergePolicyRefs (policyFqdns, stackParams);
510                     } catch (MsoException me) {
511                         me.addContext (CREATE_NETWORK_CONTEXT);
512                         logger.error("{} {} Exception Create Network, merging policyRefs type {} in {}/{} ",
513                             MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
514                             neutronNetworkType.toString(), cloudSiteId, tenantId, me);
515                         throw new NetworkException (me);
516                     }
517                 }
518
519                 if (routeTableFqdns != null && !routeTableFqdns.isEmpty() && aic3template) {
520                     try {
521                         mergeRouteTableRefs (routeTableFqdns, stackParams);
522                     } catch (MsoException me) {
523                         me.addContext (CREATE_NETWORK_CONTEXT);
524                         logger.error("{} {} Exception Create Network, merging routeTableRefs type {} in {}/{} ",
525                             MessageEnum.RA_CREATE_NETWORK_EXC, ErrorCode.DataError.getValue(),
526                             neutronNetworkType.toString(), cloudSiteId, tenantId, me);
527                         throw new NetworkException (me);
528                     }
529                 }
530
531                 // Deploy the network stack
532                 // Ignore MsoStackAlreadyExists exception because we already checked.
533                 try {
534                         if (backout == null)
535                                 backout = true;
536                     heatStack = heat.createStack (cloudSiteId,
537                                                   "CloudOwner",
538                                                   tenantId,
539                                                   networkName,
540                                                   null,
541                                                   template,
542                                                   stackParams,
543                                                   true,
544                                                   heatTemplate.getTimeoutMinutes (),
545                                                   null,
546                                                   null,
547                                                   null,
548                                                   backout.booleanValue());
549                 } catch (MsoException me) {
550                     me.addContext (CREATE_NETWORK_CONTEXT);
551                     logger
552                         .error("{} {} Exception creating network type {} in {}/{} ", MessageEnum.RA_CREATE_NETWORK_EXC,
553                             ErrorCode.DataError.getValue(), networkName, cloudSiteId, tenantId, me);
554                     throw new NetworkException (me);
555                 }
556
557                 // Reach this point if createStack is successful.
558
559                 // For Heat-based orchestration, the MSO-tracked network ID is the heat stack,
560                 // and the neutronNetworkId is the network UUID returned in stack outputs.
561                 networkId.value = heatStack.getCanonicalName ();
562                 neutronNetworkId.value = (String) heatStack.getOutputs ().get (NETWORK_ID);
563                 if (aic3template)
564                 {
565                         networkFqdn.value = (String) heatStack.getOutputs().get(NETWORK_FQDN);
566                 }
567                 Map <String, Object> outputs = heatStack.getOutputs ();
568                 Map <String, String> sMap = new HashMap <> ();
569                 if (outputs != null) {
570                     for (Map.Entry<String, Object> entry : outputs.entrySet()) {
571                         String key = entry.getKey();
572                         if (key != null && key.startsWith ("subnet")) {
573                                 if (aic3template) //one subnet output expected
574                                         {
575                                                  Map <String, String> map = getSubnetUUId(key, outputs, subnets);
576                                                  sMap.putAll(map);
577                                         }
578                                         else //multiples subnet_%aaid% outputs allowed
579                                         {
580                                                 String subnetUUId = (String) outputs.get(key);
581                                                 sMap.put (key.substring("subnet_id_".length()), subnetUUId);
582                                         }
583                         }
584                     }
585                 }
586                 subnetIdMap.value = sMap;
587
588                 rollback.value = networkRollback;
589                 // Populate remaining rollback info and response parameters.
590                 networkRollback.setNetworkStackId (heatStack.getCanonicalName ());
591                 networkRollback.setNeutronNetworkId ((String) heatStack.getOutputs ().get (NETWORK_ID));
592                 networkRollback.setNetworkCreated (true);
593                 networkRollback.setNetworkType (networkType);
594
595                 logger.debug("Network {} successfully created via HEAT", networkName);
596             }
597
598         return;
599     }
600
601     @Override
602     public void updateNetwork (String cloudSiteId,
603                                String tenantId,
604                                String networkType,
605                                String modelCustomizationUuid,
606                                String networkId,
607                                String networkName,
608                                String physicalNetworkName,
609                                List <Integer> vlans,
610                                String shared,
611                                String external,
612                                List <Subnet> subnets,
613                                Map<String,String> networkParams,
614                                MsoRequest msoRequest,
615                                Holder <Map <String, String>> subnetIdMap,
616                                Holder <NetworkRollback> rollback) throws NetworkException {
617         updateNetwork (cloudSiteId,
618                        tenantId,
619                        networkType,
620                        modelCustomizationUuid,
621                        networkId,
622                        networkName,
623                        physicalNetworkName,
624                        vlans,
625                        null,
626                        shared,
627                        external,
628                        subnets,
629                        null,
630                        null,
631                        msoRequest,
632                        subnetIdMap,
633                        rollback);
634
635     }
636
637     @Override
638     public void updateNetworkContrail (String cloudSiteId,
639                                        String tenantId,
640                                        String networkType,
641                                        String modelCustomizationUuid,
642                                        String networkId,
643                                        String networkName,
644                                        List <RouteTarget> routeTargets,
645                                        String shared,
646                                        String external,
647                                        List <Subnet> subnets,
648                                        Map<String, String> networkParams,
649                                        List <String> policyFqdns,
650                                        List<String> routeTableFqdns,
651                                        MsoRequest msoRequest,
652                                        Holder <Map <String, String>> subnetIdMap,
653                                        Holder <NetworkRollback> rollback) throws NetworkException {
654         updateNetwork (cloudSiteId,
655                        tenantId,
656                        networkType,
657                        modelCustomizationUuid,
658                        networkId,
659                        networkName,
660                        null,
661                        null,
662                        routeTargets,
663                        shared,
664                        external,
665                        subnets,
666                        policyFqdns,
667                        routeTableFqdns,
668                        msoRequest,
669                        subnetIdMap,
670                        rollback);
671     }
672
673     /**
674      * This is the "Update Network" web service implementation.
675      * It will update an existing Network of the requested type in the specified cloud
676      * and tenant. The typical use will be to replace the VLANs with the supplied
677      * list (to add or remove a VLAN), but other properties may be updated as well.
678      *
679      * There will be a pre-defined set of network types defined in the MSO Catalog.
680      * All such networks will have a similar configuration, based on the allowable
681      * Openstack networking definitions. This includes basic networks, provider
682      * networks (with a single VLAN), and multi-provider networks (one or more VLANs).
683      *
684      * Initially, all provider networks must currently be "vlan" type, and multi-provider
685      * networks must be multiple VLANs on the same physical network.
686      *
687      * This service supports two modes of Network update:
688      * - via Heat Templates
689      * - via Neutron API
690      * The network orchestration mode for each network type is declared in its
691      * catalog definition. All Heat-based templates must support some subset of
692      * the same input parameters: network_name, physical_network, vlan, segments.
693      *
694      * The method returns a NetworkRollback object. This object can be passed
695      * as-is to the rollbackNetwork operation to undo everything that was updated.
696      * This is useful if a network is successfully updated but orchestration
697      * fails on a subsequent operation.
698      */
699     private void updateNetwork (String cloudSiteId,
700                                String tenantId,
701                                String networkType,
702                                String modelCustomizationUuid,
703                                String networkId,
704                                String networkName,
705                                String physicalNetworkName,
706                                List <Integer> vlans,
707                                List <RouteTarget> routeTargets,
708                                String shared,
709                                String external,
710                                List <Subnet> subnets,
711                                List <String> policyFqdns,
712                                List<String> routeTableFqdns,
713                                MsoRequest msoRequest,
714                                Holder <Map <String, String>> subnetIdMap,
715                                Holder <NetworkRollback> rollback) throws NetworkException {
716
717         logger.debug("***UPDATE Network adapter with Network: {} of type {} in {}/{}", networkName, networkType,
718             cloudSiteId, tenantId);
719
720         // Will capture execution time for metrics
721         long startTime = System.currentTimeMillis ();
722
723         // Build a default rollback object (no actions performed)
724         NetworkRollback networkRollback = new NetworkRollback ();
725         networkRollback.setCloudId (cloudSiteId);
726         networkRollback.setTenantId (tenantId);
727         networkRollback.setMsoRequest (msoRequest);
728
729         Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite (cloudSiteId);
730         if (!cloudSiteOpt.isPresent()) {
731             String error = String.format(
732                 "UpdateNetwork: Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
733                 networkName, cloudSiteId, tenantId);
734             logger.error("{} {} {}", MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
735             // Set the detailed error as the Exception 'message'
736             throw new NetworkException(error, MsoExceptionCategory.USERDATA);
737         }
738
739
740
741
742             NetworkResource networkResource = networkCheck(
743                     startTime,
744                     networkType,
745                     modelCustomizationUuid,
746                     networkName,
747                     physicalNetworkName,
748                     vlans,
749                     routeTargets,
750                     cloudSiteId,
751                     cloudSiteOpt.get());
752             String mode = networkResource.getOrchestrationMode();
753             NetworkType neutronNetworkType = NetworkType.valueOf(networkResource.getNeutronNetworkType());
754
755             // Use an MsoNeutronUtils for all Neutron commands
756
757             if (NEUTRON_MODE.equals(mode)) {
758
759                 // Verify that the Network exists
760                 // For Neutron-based orchestration, the networkId is the Neutron Network UUID.
761                 NetworkInfo netInfo = null;
762                 long queryNetworkStarttime = System.currentTimeMillis();
763                 try {
764                     netInfo = neutron.queryNetwork(networkId, tenantId, cloudSiteId);
765                 } catch (MsoException me) {
766                     me.addContext(UPDATE_NETWORK_CONTEXT);
767                     logger.error("{} {} Exception - queryNetwork query {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
768                         ErrorCode.BusinessProcesssError.getValue(), networkId, cloudSiteId, tenantId, me);
769                     throw new NetworkException(me);
770                 }
771
772                 if (netInfo == null) {
773                     String error = String
774                         .format("Update Nework: Network %s does not exist in %s/%s", networkId, cloudSiteId, tenantId);
775                     logger.error("{} {} {}", MessageEnum.RA_NETWORK_NOT_FOUND,
776                         ErrorCode.BusinessProcesssError.getValue(), error);
777                     // Does not exist. Throw an exception (can't update a non-existent network)
778                     throw new NetworkException(error, MsoExceptionCategory.USERDATA);
779                 }
780                 long updateNetworkStarttime = System.currentTimeMillis();
781                 try {
782                     netInfo = neutron.updateNetwork(cloudSiteId,
783                             tenantId,
784                             networkId,
785                             neutronNetworkType,
786                             physicalNetworkName,
787                             vlans);
788                 } catch (MsoException me) {
789                     me.addContext(UPDATE_NETWORK_CONTEXT);
790                     logger.error("{} {} Exception - updateNetwork {} in {}/{} ", MessageEnum.RA_UPDATE_NETWORK_ERR,
791                         ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
792                     throw new NetworkException(me);
793                 }
794
795                 // Add the network ID and previously queried vlans to the rollback object
796                 networkRollback.setNetworkId(netInfo.getId());
797                 networkRollback.setNeutronNetworkId(netInfo.getId());
798                 networkRollback.setNetworkType(networkType);
799                 // Save previous parameters
800                 networkRollback.setNetworkName(netInfo.getName());
801                 networkRollback.setPhysicalNetwork(netInfo.getProvider());
802                 networkRollback.setVlans(netInfo.getVlans());
803
804                 logger.debug("Network {} updated, id = {}", networkId, netInfo.getId());
805             } else if ("HEAT".equals(mode)) {
806
807                 // First, look up to see that the Network already exists.
808                 // For Heat-based orchestration, the networkId is the network Stack ID.
809                 StackInfo heatStack = null;
810                 long queryStackStarttime = System.currentTimeMillis();
811                 try {
812                     heatStack = heat.queryStack(cloudSiteId, "CloudOwner", tenantId, networkName);
813                 } catch (MsoException me) {
814                     me.addContext(UPDATE_NETWORK_CONTEXT);
815                     logger.error("{} {} Exception - QueryStack query {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
816                         ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
817                     throw new NetworkException(me);
818                 }
819
820                 if (heatStack == null || (heatStack.getStatus() == HeatStatus.NOTFOUND)) {
821                     String error = String
822                         .format("UpdateNetwork: Stack %s does not exist in %s/%s", networkName, cloudSiteId, tenantId);
823                     logger.error("{} {} {}", MessageEnum.RA_NETWORK_NOT_FOUND, ErrorCode.DataError.getValue(),
824                         error);
825                     // Network stack does not exist. Return an error
826                     throw new NetworkException(error, MsoExceptionCategory.USERDATA);
827                 }
828
829                 // Get the previous parameters for rollback
830                 Map<String, Object> heatParams = heatStack.getParameters();
831
832                 String previousNetworkName = (String) heatParams.get("network_name");
833                 String previousPhysicalNetwork = (String) heatParams.get(PHYSICAL_NETWORK);
834
835                 List<Integer> previousVlans = new ArrayList<>();
836                 String vlansParam = (String) heatParams.get(VLANS);
837                 if (vlansParam != null) {
838                     for (String vlan : vlansParam.split(",")) {
839                         try {
840                             previousVlans.add(Integer.parseInt(vlan));
841                         } catch (NumberFormatException e) {
842                             logger.warn("{} {} Exception - VLAN parse for params {} ", MessageEnum.RA_VLAN_PARSE,
843                                 ErrorCode.DataError.getValue(), vlansParam, e);
844                         }
845                     }
846                 }
847                 logger.debug("Update Stack:  Previous VLANS: {}", previousVlans);
848
849                 // Ready to deploy the updated Network via Heat
850
851
852                 HeatTemplate heatTemplate = networkResource.getHeatTemplate();
853                 if (heatTemplate == null) {
854                     String error = "Network error - undefined Heat Template. Network Type=" + networkType;
855                     logger.error("{} {} {}", MessageEnum.RA_PARAM_NOT_FOUND, ErrorCode.DataError.getValue(),
856                         error);
857                     throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
858                 }
859
860                 logger.debug("Got HEAT Template from DB: {}", heatTemplate.toString());
861
862                 // "Fix" the template if it has CR/LF (getting this from Oracle)
863                 String template = heatTemplate.getHeatTemplate();
864                 template = template.replaceAll("\r\n", "\n");
865
866                 boolean aic3template = false;
867                 String aic3nw = AIC3_NW;
868
869                 aic3nw = environment.getProperty(AIC3_NW_PROPERTY, AIC3_NW);
870
871                 if (template.contains(aic3nw))
872                     aic3template = true;
873
874                 // Build the common set of HEAT template parameters
875                 Map<String, Object> stackParams = populateNetworkParams(neutronNetworkType,
876                         networkName,
877                         physicalNetworkName,
878                         vlans,
879                         routeTargets,
880                         shared,
881                         external,
882                         aic3template);
883
884                 // Validate (and update) the input parameters against the DB definition
885                 // Shouldn't happen unless DB config is wrong, since all networks use same inputs
886                 try {
887                     stackParams = heat.validateStackParams(stackParams, heatTemplate);
888                 } catch (IllegalArgumentException e) {
889                     String error = "UpdateNetwork: Configuration Error: Network Type=" + networkType;
890                     logger.error("{} {} {} ", MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
891                     throw new NetworkException(error, MsoExceptionCategory.INTERNAL, e);
892                 }
893
894                 if (subnets != null) {
895                     try {
896                         if (aic3template) {
897                             template = mergeSubnetsAIC3(template, subnets, stackParams);
898                         } else {
899                             template = mergeSubnets(template, subnets);
900                         }
901                     } catch (MsoException me) {
902                         me.addContext(UPDATE_NETWORK_CONTEXT);
903                         logger.error("{} {} Exception - UpdateNetwork mergeSubnets for network type {} in {}/{} ",
904                             MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
905                             neutronNetworkType.toString(), cloudSiteId, tenantId, me);
906                         throw new NetworkException(me);
907                     }
908                 }
909
910                 if (policyFqdns != null && aic3template) {
911                     try {
912                         mergePolicyRefs(policyFqdns, stackParams);
913                     } catch (MsoException me) {
914                         me.addContext(UPDATE_NETWORK_CONTEXT);
915                         logger.error("{} {} Exception - UpdateNetwork mergePolicyRefs type {} in {}/{} ",
916                             MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
917                             neutronNetworkType.toString(), cloudSiteId, tenantId, me);
918                         throw new NetworkException(me);
919                     }
920                 }
921
922                 if (routeTableFqdns != null && !routeTableFqdns.isEmpty() && aic3template) {
923                     try {
924                         mergeRouteTableRefs(routeTableFqdns, stackParams);
925                     } catch (MsoException me) {
926                         me.addContext(UPDATE_NETWORK_CONTEXT);
927                         logger.error("{} {} Exception - UpdateNetwork mergeRouteTableRefs type {} in {}/{} ",
928                             MessageEnum.RA_UPDATE_NETWORK_ERR, ErrorCode.DataError.getValue(),
929                             neutronNetworkType.toString(), cloudSiteId, tenantId, me);
930                         throw new NetworkException(me);
931                     }
932                 }
933
934                 // Update the network stack
935                 // Ignore MsoStackNotFound exception because we already checked.
936                 long updateStackStarttime = System.currentTimeMillis();
937                 try {
938                     heatStack = heatWithUpdate.updateStack(cloudSiteId,
939                             "CloudOwner",
940                             tenantId,
941                             networkId,
942                             template,
943                             stackParams,
944                             true,
945                             heatTemplate.getTimeoutMinutes());
946                 } catch (MsoException me) {
947                     me.addContext(UPDATE_NETWORK_CONTEXT);
948                     logger.error("{} {} Exception - update network {} in {}/{} ", MessageEnum.RA_UPDATE_NETWORK_ERR,
949                         ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
950                     throw new NetworkException(me);
951                 }
952
953                 Map<String, Object> outputs = heatStack.getOutputs();
954                 Map<String, String> sMap = new HashMap<>();
955                 if (outputs != null) {
956                     for (Map.Entry<String, Object> entry : outputs.entrySet()) {
957                         String key=entry.getKey();
958                         if (key != null && key.startsWith("subnet")) {
959                             if (aic3template) //one subnet output expected
960                             {
961                                 Map<String, String> map = getSubnetUUId(key, outputs, subnets);
962                                 sMap.putAll(map);
963                             } else //multiples subnet_%aaid% outputs allowed
964                             {
965                                 String subnetUUId = (String) outputs.get(key);
966                                 sMap.put(key.substring("subnet_id_".length()), subnetUUId);
967                             }
968                         }
969                     }
970                 }
971                 subnetIdMap.value = sMap;
972
973                 // Reach this point if createStack is successful.
974                 // Populate remaining rollback info and response parameters.
975                 networkRollback.setNetworkStackId(heatStack.getCanonicalName());
976                 if(null != outputs) {
977                     networkRollback.setNeutronNetworkId((String) outputs.get(NETWORK_ID));
978                 }
979                 else {
980                     logger.debug("outputs is NULL");
981                 }
982                 networkRollback.setNetworkType(networkType);
983                 // Save previous parameters
984                 networkRollback.setNetworkName(previousNetworkName);
985                 networkRollback.setPhysicalNetwork(previousPhysicalNetwork);
986                 networkRollback.setVlans(previousVlans);
987
988                 rollback.value = networkRollback;
989
990                 logger.debug("Network {} successfully updated via HEAT", networkId);
991             }
992
993         return;
994     }
995
996     private NetworkResource networkCheck (long startTime,
997                                           String networkType,
998                                           String modelCustomizationUuid,
999                                           String networkName,
1000                                           String physicalNetworkName,
1001                                           List <Integer> vlans,
1002                                           List <RouteTarget> routeTargets,
1003                                           String cloudSiteId,
1004                                           CloudSite cloudSite) throws NetworkException {
1005         // Retrieve the Network Resource definition
1006         NetworkResource networkResource = null;
1007         NetworkResourceCustomization networkCust = null;
1008         CollectionNetworkResourceCustomization collectionNetworkCust = null;
1009                         if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
1010                                 if (!commonUtils.isNullOrEmpty(networkType)) {
1011                                         networkResource = networkResourceRepo.findFirstByModelNameOrderByModelVersionDesc(networkType);
1012                                 }
1013                         } else {
1014                                 networkCust = networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
1015                                 if (networkCust == null) {
1016                                         collectionNetworkCust = collectionNetworkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
1017                                 }
1018                         }
1019                         if(networkCust != null){
1020           logger.debug("Got Network Customization definition from Catalog: {}", networkCust.toString());
1021
1022                                 networkResource = networkCust.getNetworkResource();
1023                         } else if (collectionNetworkCust != null) {
1024           logger.debug("Retrieved Collection Network Resource Customization from Catalog: {}",
1025               collectionNetworkCust.toString());
1026                                 networkResource = collectionNetworkCust.getNetworkResource();
1027                         }
1028                         if (networkResource == null) {
1029           String error = String.format(
1030               "Create/UpdateNetwork: Unable to get network resource with NetworkType: %s or ModelCustomizationUUID:%s",
1031               networkType, modelCustomizationUuid);
1032           logger.error("{} {} {} ", MessageEnum.RA_UNKOWN_PARAM, ErrorCode.DataError.getValue(), error);
1033
1034                                 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
1035                         }
1036         logger.debug("Got Network definition from Catalog: {}", networkResource.toString());
1037
1038                         String mode = networkResource.getOrchestrationMode();
1039                         NetworkType neutronNetworkType = NetworkType
1040                                         .valueOf(networkResource.getNeutronNetworkType());
1041
1042                         // All Networks are orchestrated via HEAT or Neutron
1043                         if (!("HEAT".equals(mode) || NEUTRON_MODE.equals(mode))) {
1044           String error = "CreateNetwork: Configuration Error: Network Type = " + networkType;
1045           logger.error("{} {} {}", MessageEnum.RA_NETWORK_ORCHE_MODE_NOT_SUPPORT,
1046               ErrorCode.DataError.getValue(), error);
1047           throw new NetworkException(error, MsoExceptionCategory.INTERNAL);
1048                         }
1049
1050                         MavenLikeVersioning aicV = new MavenLikeVersioning();
1051                         aicV.setVersion(cloudSite.getCloudVersion());
1052                         if ((aicV.isMoreRecentThan(networkResource.getAicVersionMin()) || aicV
1053                                         .isTheSameVersion(networkResource.getAicVersionMin())) // aic
1054                                                                                                                                                         // >=
1055                                                                                                                                                         // min
1056                                         && (aicV.isTheSameVersion(networkResource
1057                                                         .getAicVersionMax()) || !(aicV
1058                                                         .isMoreRecentThan(networkResource
1059                                                                         .getAicVersionMax())))) // aic <= max
1060                         {
1061           logger.debug("Network Type:{} VersionMin:{} VersionMax:{} supported on Cloud:{} with AIC_Version:{}",
1062               networkType, networkResource.getAicVersionMin(), networkResource.getAicVersionMax(), cloudSiteId,
1063               cloudSite.getCloudVersion());
1064                         } else {
1065           String error = String
1066               .format("Network Type:%s Version_Min:%s Version_Max:%s not supported on Cloud:%s with AIC_Version:%s",
1067                   networkType, networkType, networkResource.getAicVersionMin(),
1068                   networkResource.getAicVersionMax(), cloudSiteId, cloudSite.getCloudVersion());
1069           logger.error("{} {} {} ", MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
1070                                 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
1071                         }
1072
1073                         // Validate the Network parameters.
1074                         String missing = validateNetworkParams(neutronNetworkType,
1075                                         networkName, physicalNetworkName, vlans, routeTargets);
1076                         if (!missing.isEmpty()) {
1077                                 String error = "Create Network: Missing parameters: " + missing;
1078           logger.error("{} {} {}", MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
1079
1080                                 throw new NetworkException(error, MsoExceptionCategory.USERDATA);
1081                         }
1082
1083         return networkResource;
1084     }
1085
1086     @Override
1087     public void queryNetwork (String cloudSiteId,
1088                               String tenantId,
1089                               String networkNameOrId,
1090                               MsoRequest msoRequest,
1091                               Holder <Boolean> networkExists,
1092                               Holder <String> networkId,
1093                               Holder <String> neutronNetworkId,
1094                               Holder <NetworkStatus> status,
1095                               Holder <List <Integer>> vlans,
1096                               Holder <Map <String, String>> subnetIdMap) throws NetworkException {
1097         queryNetwork (cloudSiteId,
1098                       tenantId,
1099                       networkNameOrId,
1100                       msoRequest,
1101                       networkExists,
1102                       networkId,
1103                       neutronNetworkId,
1104                       status,
1105                       vlans,
1106                       null,
1107                       subnetIdMap);
1108     }
1109
1110     @Override
1111     public void queryNetworkContrail (String cloudSiteId,
1112                                       String tenantId,
1113                                       String networkNameOrId,
1114                                       MsoRequest msoRequest,
1115                                       Holder <Boolean> networkExists,
1116                                       Holder <String> networkId,
1117                                       Holder <String> neutronNetworkId,
1118                                       Holder <NetworkStatus> status,
1119                                       Holder <List <RouteTarget>> routeTargets,
1120                                       Holder <Map <String, String>> subnetIdMap) throws NetworkException {
1121         queryNetwork (cloudSiteId,
1122                       tenantId,
1123                       networkNameOrId,
1124                       msoRequest,
1125                       networkExists,
1126                       networkId,
1127                       neutronNetworkId,
1128                       status,
1129                       null,
1130                       routeTargets,
1131                       subnetIdMap);
1132     }
1133
1134     /**
1135      * This is the queryNetwork method. It returns the existence and status of
1136      * the specified network, along with its Neutron UUID and list of VLANs.
1137      * This method attempts to find the network using both Heat and Neutron.
1138      * Heat stacks are first searched based on the provided network name/id.
1139      * If none is found, the Neutron is directly queried.
1140      */
1141     private void queryNetwork (String cloudSiteId,
1142                               String tenantId,
1143                               String networkNameOrId,
1144                               MsoRequest msoRequest,
1145                               Holder <Boolean> networkExists,
1146                               Holder <String> networkId,
1147                               Holder <String> neutronNetworkId,
1148                               Holder <NetworkStatus> status,
1149                               Holder <List <Integer>> vlans,
1150                               Holder <List <RouteTarget>> routeTargets,
1151                               Holder <Map <String, String>> subnetIdMap) throws NetworkException {
1152
1153         logger.debug("*** QUERY Network with Network: {} in {}/{}", networkNameOrId, cloudSiteId, tenantId);
1154
1155         // Will capture execution time for metrics
1156         long startTime = System.currentTimeMillis ();
1157
1158         if (commonUtils.isNullOrEmpty (cloudSiteId)
1159             || commonUtils.isNullOrEmpty(tenantId)
1160             || commonUtils.isNullOrEmpty(networkNameOrId)) {
1161
1162             String error = "Missing mandatory parameter cloudSiteId, tenantId or networkId";
1163             logger.error("{} {} {}", MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
1164             throw new NetworkException (error, MsoExceptionCategory.USERDATA);
1165         }
1166
1167         Optional<CloudSite> cloudSiteOpt = cloudConfig.getCloudSite(cloudSiteId);
1168         if (!cloudSiteOpt.isPresent())
1169         {
1170             String error = String
1171                 .format("Configuration Error. Stack %s in %s/%s: CloudSite does not exist in MSO Configuration",
1172                     networkNameOrId, cloudSiteId, tenantId);
1173             logger.error("{} {} {}", MessageEnum.RA_CONFIG_EXC, ErrorCode.DataError.getValue(), error);
1174                 // Set the detailed error as the Exception 'message'
1175                 throw new NetworkException (error, MsoExceptionCategory.USERDATA);
1176         }
1177
1178         // Use MsoNeutronUtils for all NEUTRON commands
1179
1180         String mode;
1181         String neutronId;
1182         // Try Heat first, since networks may be named the same as the Heat stack
1183         StackInfo heatStack = null;
1184         long queryStackStarttime = System.currentTimeMillis ();
1185         try {
1186             heatStack = heat.queryStack (cloudSiteId, "CloudOwner", tenantId, networkNameOrId);
1187         } catch (MsoException me) {
1188                 me.addContext ("QueryNetwork");
1189             logger.error("{} {} Exception - Query Network (heat): {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
1190                 ErrorCode.DataError.getValue(), networkNameOrId, cloudSiteId, tenantId, me);
1191             throw new NetworkException (me);
1192         }
1193
1194         // Populate the outputs based on the returned Stack information
1195         if (heatStack != null && heatStack.getStatus () != HeatStatus.NOTFOUND) {
1196             // Found it. Get the neutronNetworkId for further query
1197             Map <String, Object> outputs = heatStack.getOutputs ();
1198             neutronId = (String) outputs.get (NETWORK_ID);
1199             mode = "HEAT";
1200
1201             Map <String, String> sMap = new HashMap <> ();
1202             if (outputs != null) {
1203                 for (String key : outputs.keySet ()) {
1204                         if (key != null && key.startsWith ("subnet_id_")) //multiples subnet_%aaid% outputs
1205                         {
1206                                 String subnetUUId = (String) outputs.get(key);
1207                                 sMap.put (key.substring("subnet_id_".length()), subnetUUId);
1208                         }
1209                         else if (key != null && key.startsWith ("subnet")) //one subnet output expected
1210                         {
1211                                 Map <String, String> map = getSubnetUUId(key, outputs, null);
1212                                 sMap.putAll(map);
1213                         }
1214
1215                 }
1216             }
1217             subnetIdMap.value = sMap;
1218         } else {
1219             // Input ID was not a Heat stack ID. Try it directly in Neutron
1220             neutronId = networkNameOrId;
1221             mode = NEUTRON_MODE;
1222         }
1223
1224         // Query directly against the Neutron Network for the details
1225         // no RouteTargets available for ContrailV2 in neutron net-show
1226         // networkId is heatStackId
1227         long queryNetworkStarttime = System.currentTimeMillis ();
1228         try {
1229             NetworkInfo netInfo = neutron.queryNetwork (neutronId, tenantId, cloudSiteId);
1230             if (netInfo != null) {
1231                 // Found. Populate the output elements
1232                 networkExists.value = Boolean.TRUE;
1233                 if ("HEAT".equals (mode)) {
1234                     networkId.value = heatStack.getCanonicalName ();
1235                 } else {
1236                     networkId.value = netInfo.getId ();
1237                 }
1238                 neutronNetworkId.value = netInfo.getId ();
1239                 status.value = netInfo.getStatus ();
1240                 if (vlans != null)
1241                         vlans.value = netInfo.getVlans ();
1242
1243                 logger.debug("Network {} found({}), ID = {}{}", networkNameOrId, mode, networkId.value,
1244                     ("HEAT".equals(mode) ? ",NeutronId = " + neutronNetworkId.value : ""));
1245             } else {
1246                 // Not found. Populate the status fields, leave the rest null
1247                 networkExists.value = Boolean.FALSE;
1248                 status.value = NetworkStatus.NOTFOUND;
1249                 neutronNetworkId.value = null;
1250                 if (vlans != null)
1251                         vlans.value = new ArrayList<>();
1252
1253                 logger.debug("Network {} not found", networkNameOrId);
1254             }
1255         } catch (MsoException me) {
1256             me.addContext ("QueryNetwork");
1257             logger.error("{} {} Exception - Query Network (neutron): {} in {}/{} ", MessageEnum.RA_QUERY_NETWORK_EXC,
1258                 ErrorCode.DataError.getValue(), networkNameOrId, cloudSiteId, tenantId, me);
1259             throw new NetworkException (me);
1260         }
1261         return;
1262     }
1263
1264     /**
1265      * This is the "Delete Network" web service implementation.
1266      * It will delete a Network in the specified cloud and tenant.
1267      *
1268      * If the network is not found, it is treated as a success.
1269      *
1270      * This service supports two modes of Network creation/update/delete:
1271      * - via Heat Templates
1272      * - via Neutron API
1273      * The network orchestration mode for each network type is declared in its
1274      * catalog definition.
1275      *
1276      * For Heat-based orchestration, the networkId should be the stack ID.
1277      * For Neutron-based orchestration, the networkId should be the Neutron network UUID.
1278      *
1279      * The method returns nothing on success. Rollback is not possible for delete
1280      * commands, so any failure on delete will require manual fallout in the client.
1281      */
1282     @Override
1283     public void deleteNetwork (String cloudSiteId,
1284                                String tenantId,
1285                                String networkType,
1286                                String modelCustomizationUuid,
1287                                String networkId,
1288                                MsoRequest msoRequest,
1289                                Holder <Boolean> networkDeleted) throws NetworkException {
1290
1291         logger.debug("*** DELETE Network adapter with Network: {} in {}/{}", networkId, cloudSiteId, tenantId);
1292
1293         // Will capture execution time for metrics
1294         long startTime = System.currentTimeMillis ();
1295
1296
1297             if (commonUtils.isNullOrEmpty (cloudSiteId)
1298                             || commonUtils.isNullOrEmpty(tenantId)
1299                             || commonUtils.isNullOrEmpty(networkId)) {
1300                 String error = "Missing mandatory parameter cloudSiteId, tenantId or networkId";
1301                 logger.error("{} {} {} ", MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
1302                 throw new NetworkException (error, MsoExceptionCategory.USERDATA);
1303             }
1304
1305             // Retrieve the Network Resource definition
1306             NetworkResource networkResource = null;
1307
1308                 if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
1309                         if (!commonUtils.isNullOrEmpty(networkType)) {
1310                                 networkResource = networkResourceRepo.findFirstByModelNameOrderByModelVersionDesc(networkType);
1311                         }
1312                         } else {
1313                                 NetworkResourceCustomization nrc = networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid);
1314                                 if (nrc != null) {
1315                                         networkResource = nrc.getNetworkResource();
1316                                 }
1317                         }
1318             String mode = "";
1319             if (networkResource != null) {
1320                 logger.debug("Got Network definition from Catalog: {}", networkResource.toString());
1321
1322                 mode = networkResource.getOrchestrationMode ();
1323             }
1324
1325             if (NEUTRON_MODE.equals (mode)) {
1326
1327                 // Use MsoNeutronUtils for all NEUTRON commands
1328                 long deleteNetworkStarttime = System.currentTimeMillis ();
1329                 try {
1330                     // The deleteNetwork function in MsoNeutronUtils returns success if the network
1331                     // was not found. So don't bother to query first.
1332                     boolean deleted = neutron.deleteNetwork (networkId, tenantId, cloudSiteId);
1333                     networkDeleted.value = deleted;
1334                 } catch (MsoException me) {
1335                     me.addContext ("DeleteNetwork");
1336                     logger.error("{} {} Delete Network (neutron): {} in {}/{} ", MessageEnum.RA_DELETE_NETWORK_EXC,
1337                         ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
1338                     throw new NetworkException (me);
1339                 }
1340             } else { // DEFAULT to ("HEAT".equals (mode))
1341                 long deleteStackStarttime = System.currentTimeMillis ();
1342
1343                 try {
1344                     // The deleteStack function in MsoHeatUtils returns NOTFOUND if the stack was not found or if the stack was deleted.
1345                     //  So query first to report back if stack WAS deleted or just NOTOFUND
1346                         StackInfo heatStack = null;
1347                         heatStack = heat.queryStack(cloudSiteId, "CloudOwner", tenantId, networkId);
1348                         if (heatStack != null && heatStack.getStatus() != HeatStatus.NOTFOUND)
1349                         {
1350                                 heat.deleteStack (tenantId, "CloudOwner", cloudSiteId, networkId, true);
1351                                 networkDeleted.value = true;
1352                         }
1353                         else
1354                         {
1355                                 networkDeleted.value = false;
1356                         }
1357                 } catch (MsoException me) {
1358                     me.addContext ("DeleteNetwork");
1359                     logger.error("{} {} Delete Network (heat): {} in {}/{} ", MessageEnum.RA_DELETE_NETWORK_EXC,
1360                         ErrorCode.DataError.getValue(), networkId, cloudSiteId, tenantId, me);
1361                     throw new NetworkException (me);
1362                 }
1363             }
1364
1365
1366         // On success, nothing is returned.
1367         return;
1368     }
1369
1370     /**
1371      * This web service endpoint will rollback a previous Create VNF operation.
1372      * A rollback object is returned to the client in a successful creation
1373      * response. The client can pass that object as-is back to the rollbackVnf
1374      * operation to undo the creation.
1375      *
1376      * The rollback includes removing the VNF and deleting the tenant if the
1377      * tenant did not exist prior to the VNF creation.
1378      */
1379     @Override
1380     public void rollbackNetwork (NetworkRollback rollback) throws NetworkException {
1381         // Will capture execution time for metrics
1382         long startTime = System.currentTimeMillis ();
1383
1384         if (rollback == null) {
1385             logger
1386                 .error("{} {} rollback is null", MessageEnum.RA_ROLLBACK_NULL, ErrorCode.DataError.getValue());
1387             return;
1388         }
1389
1390         // Get the elements of the VnfRollback object for easier access
1391         String cloudSiteId = rollback.getCloudId ();
1392         String tenantId = rollback.getTenantId ();
1393         String networkId = rollback.getNetworkStackId ();
1394         String networkType = rollback.getNetworkType ();
1395         String modelCustomizationUuid = rollback.getModelCustomizationUuid();
1396
1397         logger.debug("*** ROLLBACK Network {} in {}/{}", networkId, cloudSiteId, tenantId);
1398
1399
1400             // Retrieve the Network Resource definition
1401             NetworkResource networkResource = null;
1402                 if (commonUtils.isNullOrEmpty(modelCustomizationUuid)) {
1403                                 networkResource = networkCustomRepo.findOneByNetworkType(networkType).getNetworkResource();
1404                         } else {
1405                                 networkResource = networkCustomRepo.findOneByModelCustomizationUUID(modelCustomizationUuid).getNetworkResource();
1406                         }
1407             String mode = "";
1408             if (networkResource != null) {
1409
1410                 logger.debug("Got Network definition from Catalog: {}", networkResource.toString());
1411
1412                 mode = networkResource.getOrchestrationMode ();
1413             }
1414
1415             if (rollback.getNetworkCreated ()) {
1416                 // Rolling back a newly created network, so delete it.
1417                 if (NEUTRON_MODE.equals (mode)) {
1418                     // Use MsoNeutronUtils for all NEUTRON commands
1419                     long deleteNetworkStarttime = System.currentTimeMillis ();
1420                     try {
1421                         // The deleteNetwork function in MsoNeutronUtils returns success if the network
1422                         // was not found. So don't bother to query first.
1423                         neutron.deleteNetwork (networkId, tenantId, cloudSiteId);
1424                     } catch (MsoException me) {
1425                         me.addContext ("RollbackNetwork");
1426                         logger.error("{} {} Exception - Rollback Network (neutron): {} in {}/{} ",
1427                             MessageEnum.RA_DELETE_NETWORK_EXC, ErrorCode.BusinessProcesssError.getValue(),
1428                             networkId, cloudSiteId, tenantId, me);
1429                         throw new NetworkException (me);
1430                     }
1431                 } else { // DEFAULT to if ("HEAT".equals (mode))
1432                     long deleteStackStarttime = System.currentTimeMillis ();
1433                     try {
1434                         // The deleteStack function in MsoHeatUtils returns success if the stack
1435                         // was not found. So don't bother to query first.
1436                         heat.deleteStack (tenantId, "CloudOwner", cloudSiteId, networkId, true);
1437                     } catch (MsoException me) {
1438                         me.addContext ("RollbackNetwork");
1439                         logger.error("{} {} Exception - Rollback Network (heat): {} in {}/{} ",
1440                             MessageEnum.RA_DELETE_NETWORK_EXC, ErrorCode.BusinessProcesssError.getValue(),
1441                             networkId, cloudSiteId, tenantId, me);
1442                         throw new NetworkException (me);
1443                     }
1444                 }
1445             }
1446
1447         return;
1448     }
1449
1450     private String validateNetworkParams (NetworkType neutronNetworkType,
1451                                           String networkName,
1452                                           String physicalNetwork,
1453                                           List <Integer> vlans,
1454                                           List <RouteTarget> routeTargets) {
1455         String sep = "";
1456         StringBuilder missing = new StringBuilder ();
1457         if (commonUtils.isNullOrEmpty(networkName)) {
1458             missing.append ("networkName");
1459             sep = ",";
1460         }
1461
1462         if (neutronNetworkType == NetworkType.PROVIDER || neutronNetworkType == NetworkType.MULTI_PROVIDER) {
1463             if (commonUtils.isNullOrEmpty(physicalNetwork)) {
1464                 missing.append (sep).append ("physicalNetworkName");
1465                 sep = ",";
1466             }
1467             if (vlans == null || vlans.isEmpty ()) {
1468                 missing.append (sep).append (VLANS);
1469             }
1470         }
1471
1472         return missing.toString ();
1473     }
1474
1475     private Map <String, Object> populateNetworkParams (NetworkType neutronNetworkType,
1476                                                         String networkName,
1477                                                         String physicalNetwork,
1478                                                         List <Integer> vlans,
1479                                                         List <RouteTarget> routeTargets,
1480                                                         String shared,
1481                                                         String external,
1482                                                         boolean aic3template) {
1483         // Build the common set of HEAT template parameters
1484         Map <String, Object> stackParams = new HashMap <> ();
1485         stackParams.put ("network_name", networkName);
1486
1487         if (neutronNetworkType == NetworkType.PROVIDER) {
1488             // For Provider type
1489             stackParams.put (PHYSICAL_NETWORK, physicalNetwork);
1490             stackParams.put ("vlan", vlans.get (0).toString ());
1491         } else if (neutronNetworkType == NetworkType.MULTI_PROVIDER) {
1492             // For Multi-provider, PO supports a custom resource extension of ProviderNet.
1493             // It supports all ProviderNet properties except segmentation_id, and adds a
1494             // comma-separated-list of VLANs as a "segments" property.
1495             // Note that this does not match the Neutron definition of Multi-Provider network,
1496             // which contains a list of 'segments', each having physical_network, network_type,
1497             // and segmentation_id.
1498             StringBuilder buf = new StringBuilder ();
1499             String sep = "";
1500             for (Integer vlan : vlans) {
1501                 buf.append (sep).append (vlan.toString ());
1502                 sep = ",";
1503             }
1504             String csl = buf.toString ();
1505
1506             stackParams.put (PHYSICAL_NETWORK, physicalNetwork);
1507             stackParams.put (VLANS, csl);
1508         }
1509         if (routeTargets != null) {
1510
1511             String rtGlobal = "";
1512             String rtImport = "";
1513             String rtExport = "";
1514             String sep = "";
1515             for (RouteTarget rt : routeTargets) {
1516                 boolean rtIsNull = false;
1517                 if (rt != null)
1518                 {
1519                         String routeTarget = rt.getRouteTarget();
1520                         String routeTargetRole = rt.getRouteTargetRole();
1521                   logger.debug("Checking for an actually null route target: {}", rt);
1522                         if (routeTarget == null || routeTarget.equals("") || routeTarget.equalsIgnoreCase("null"))
1523                                 rtIsNull = true;
1524                         if (routeTargetRole == null || routeTargetRole.equals("") || routeTargetRole.equalsIgnoreCase("null"))
1525                                 rtIsNull = true;
1526                 } else {
1527                         rtIsNull = true;
1528                 }
1529                 if (!rtIsNull) {
1530                   logger.debug("Input RT:{}", rt);
1531                         String role = rt.getRouteTargetRole();
1532                         String rtValue = rt.getRouteTarget();
1533
1534                         if ("IMPORT".equalsIgnoreCase(role))
1535                         {
1536                                 sep = rtImport.isEmpty() ? "" : ",";
1537                                 rtImport = aic3template ? rtImport + sep + "target:" + rtValue  : rtImport + sep + rtValue ;
1538                         }
1539                         else if ("EXPORT".equalsIgnoreCase(role))
1540                         {
1541                                 sep = rtExport.isEmpty() ? "" : ",";
1542                                 rtExport = aic3template ? rtExport + sep + "target:" + rtValue  : rtExport + sep + rtValue ;
1543                         }
1544                         else // covers BOTH, empty etc
1545                         {
1546                                 sep = rtGlobal.isEmpty() ? "" : ",";
1547                                 rtGlobal = aic3template ? rtGlobal + sep + "target:" + rtValue  : rtGlobal + sep + rtValue ;
1548                         }
1549
1550                 }
1551             }
1552
1553             if (!rtImport.isEmpty())
1554             {
1555                 stackParams.put ("route_targets_import", rtImport);
1556             }
1557             if (!rtExport.isEmpty())
1558             {
1559                 stackParams.put ("route_targets_export", rtExport);
1560             }
1561             if (!rtGlobal.isEmpty())
1562             {
1563                 stackParams.put ("route_targets", rtGlobal);
1564             }
1565         }
1566         if (commonUtils.isNullOrEmpty(shared)) {
1567             stackParams.put ("shared", "False");
1568         } else {
1569             stackParams.put ("shared", shared);
1570         }
1571         if (commonUtils.isNullOrEmpty(external)) {
1572             stackParams.put ("external", "False");
1573         } else {
1574             stackParams.put ("external", external);
1575         }
1576         return stackParams;
1577     }
1578
1579
1580
1581     /** policyRef_list structure in stackParams
1582     [
1583      {
1584          "network_policy_refs_data_sequence": {
1585              "network_policy_refs_data_sequence_major": "1",
1586              "network_policy_refs_data_sequence_minor": "0"
1587          }
1588      },
1589      {
1590          "network_policy_refs_data_sequence": {
1591              "network_policy_refs_data_sequence_major": "2",
1592              "network_policy_refs_data_sequence_minor": "0"
1593          }
1594      }
1595         ]
1596     **/
1597     private void mergePolicyRefs(List <String> pFqdns, Map <String, Object> stackParams) throws MsoException {
1598                 //Resource Property
1599                 List<ContrailPolicyRef> prlist =  new ArrayList <> ();
1600                 int index = 1;
1601                 for (String pf : pFqdns) {
1602                         if (!commonUtils.isNullOrEmpty(pf))
1603                         {
1604                                 ContrailPolicyRef pr = new ContrailPolicyRef();
1605                                 ContrailPolicyRefSeq refSeq = new ContrailPolicyRefSeq(String.valueOf(index), "0");
1606                                 pr.setSeq(refSeq);
1607                                 index++;
1608           logger.debug("Contrail PolicyRefs Data:{}", pr);
1609                                 prlist.add(pr);
1610                         }
1611                 }
1612
1613                 JsonNode node = null;
1614                 try
1615                 {
1616                         ObjectMapper mapper = new ObjectMapper();
1617                         node = mapper.convertValue(prlist, JsonNode.class);
1618                         String jsonString = mapper.writeValueAsString(prlist);
1619         logger.debug("Json PolicyRefs Data:{}", jsonString);
1620                 }
1621                 catch (Exception e)
1622                 {
1623                         String error = "Error creating JsonNode for policyRefs Data";
1624         logger.error("{} {} {} ", MessageEnum.RA_MARSHING_ERROR, ErrorCode.BusinessProcesssError.getValue(),
1625             error, e);
1626                         throw new MsoAdapterException (error);
1627                 }
1628                 //update parameters
1629                 if (pFqdns != null && node != null)
1630                 {
1631                         StringBuilder buf = new StringBuilder ();
1632                         String sep = "";
1633                         for (String pf : pFqdns) {
1634                                 if (!commonUtils.isNullOrEmpty(pf))
1635                                 {
1636                                         buf.append (sep).append (pf);
1637                                         sep = ",";
1638                                 }
1639                         }
1640                         String csl = buf.toString ();
1641                         stackParams.put ("policy_refs", csl);
1642                         stackParams.put ("policy_refsdata", node);
1643                 }
1644
1645         logger.debug("StackParams updated with policy refs");
1646                 return;
1647     }
1648
1649     private void mergeRouteTableRefs(List <String> rtFqdns, Map <String, Object> stackParams) throws MsoException {
1650
1651                 //update parameters
1652                 if (rtFqdns != null)
1653                 {
1654                         StringBuilder buf = new StringBuilder ();
1655                         String sep = "";
1656                         for (String rtf : rtFqdns) {
1657                                 if (!commonUtils.isNullOrEmpty(rtf))
1658                                 {
1659                                         buf.append (sep).append (rtf);
1660                                         sep = ",";
1661                                 }
1662                         }
1663                         String csl = buf.toString ();
1664                         stackParams.put ("route_table_refs", csl);
1665                 }
1666
1667         logger.debug("StackParams updated with route_table refs");
1668                 return;
1669     }
1670
1671
1672     /*** Subnet Output structure from Juniper
1673      {
1674     "ipam_subnets": [
1675         {
1676             "subnet": {
1677                 "ip_prefix": "10.100.1.0",
1678                 "ip_prefix_len": 28
1679             },
1680             "addr_from_start": null,
1681             "enable_dhcp": false,
1682             "default_gateway": "10.100.1.1",
1683             "dns_nameservers": [],
1684             "dhcp_option_list": null,
1685             "subnet_uuid": "10391fbf-6b9c-4160-825d-2d018b7649cf",
1686             "allocation_pools": [
1687                 {
1688                     "start": "10.100.1.3",
1689                     "end": "10.100.1.5"
1690                 },
1691                 {
1692                     "start": "10.100.1.6",
1693                     "end": "10.100.1.9"
1694                 }
1695             ],
1696             "host_routes": null,
1697             "dns_server_address": "10.100.1.13",
1698             "subnet_name": "subnet_MsoNW1_692c9032-e1a2-4d64-828c-7b9a4fcc05b0"
1699         },
1700         {
1701             "subnet": {
1702                 "ip_prefix": "10.100.2.16",
1703                 "ip_prefix_len": 28
1704             },
1705             "addr_from_start": null,
1706             "enable_dhcp": true,
1707             "default_gateway": "10.100.2.17",
1708             "dns_nameservers": [],
1709             "dhcp_option_list": null,
1710             "subnet_uuid": "c7aac5ea-66fe-443a-85f9-9c38a608c0f6",
1711             "allocation_pools": [
1712                 {
1713                     "start": "10.100.2.18",
1714                     "end": "10.100.2.20"
1715                 }
1716             ],
1717             "host_routes": null,
1718             "dns_server_address": "10.100.2.29",
1719             "subnet_name": "subnet_MsoNW1_692c9032-e1a2-4d64-828c-7b9a4fcc05b1"
1720         }
1721     ],
1722     "host_routes": null
1723         }
1724     ***/
1725     private String mergeSubnetsAIC3 (String heatTemplate, List <Subnet> subnets, Map <String, Object> stackParams) throws MsoException {
1726
1727                 //Resource Property
1728                 List<ContrailSubnet> cslist =  new ArrayList <> ();
1729                 for (Subnet subnet : subnets) {
1730         logger.debug("Input Subnet:{}", subnet.toString());
1731                         ContrailSubnet cs = new ContrailSubnetMapper(subnet).map();
1732         logger.debug("Contrail Subnet:{}", cs.toString());
1733                         cslist.add(cs);
1734                 }
1735
1736                 JsonNode node = null;
1737                 try
1738                 {
1739                         ObjectMapper mapper = new ObjectMapper();
1740                         node = mapper.convertValue(cslist, JsonNode.class);
1741                         String jsonString = mapper.writeValueAsString(cslist);
1742         logger.debug("Json Subnet List:{}", jsonString);
1743                 }
1744                 catch (Exception e)
1745                 {
1746                         String error = "Error creating JsonNode from input subnets";
1747         logger.error("{} {} {} ", MessageEnum.RA_MARSHING_ERROR, ErrorCode.DataError.getValue(), error, e);
1748                         throw new MsoAdapterException (error);
1749                 }
1750                 //update parameters
1751                 if (node != null)
1752                 {
1753                         stackParams.put ("subnet_list", node);
1754                 }
1755                 //Outputs - All subnets are in one ipam_subnets structure
1756                 String outputTempl = "  subnet:\n" + "    description: Openstack subnet identifier\n"
1757                                 + "    value: { get_attr: [network, network_ipam_refs, 0, attr]}\n";
1758
1759                 // append outputs in heatTemplate
1760                 int outputsIdx = heatTemplate.indexOf ("outputs:");
1761                 heatTemplate = insertStr (heatTemplate, outputTempl, outputsIdx + 8);
1762         logger.debug("Template updated with all AIC3.0 subnets:{}", heatTemplate);
1763                 return heatTemplate;
1764     }
1765
1766
1767     private String mergeSubnets (String heatTemplate, List <Subnet> subnets) throws MsoException {
1768
1769                 String resourceTempl = "  subnet_%subnetId%:\n" + "    type: OS::Neutron::Subnet\n"
1770                                 + "    properties:\n"
1771                                 + "      name: %name%\n"
1772                                 + "      network_id: { get_resource: network }\n"
1773                                 + "      cidr: %cidr%\n";
1774
1775                 /* make these optional
1776                                + "      ip_version: %ipversion%\n"
1777                                + "      enable_dhcp: %enabledhcp%\n"
1778                                + "      gateway_ip: %gatewayip%\n"
1779                                + "      allocation_pools:\n"
1780                                + "       - start: %poolstart%\n"
1781                                + "         end: %poolend%\n";
1782
1783                  */
1784
1785                 String outputTempl = "  subnet_id_%subnetId%:\n" + "    description: Openstack subnet identifier\n"
1786                                 + "    value: {get_resource: subnet_%subnetId%}\n";
1787
1788                 String curR;
1789                 String curO;
1790                 StringBuilder resourcesBuf = new StringBuilder ();
1791                 StringBuilder outputsBuf = new StringBuilder ();
1792                 for (Subnet subnet : subnets) {
1793
1794                         // build template for each subnet
1795                         curR = resourceTempl;
1796                         if (subnet.getSubnetId () != null) {
1797                                 curR = curR.replace ("%subnetId%", subnet.getSubnetId ());
1798                         } else {
1799                                 String error = "Missing Required AAI SubnetId for subnet in HEAT Template";
1800               logger.error("{} {} {} ", MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
1801                                 throw new MsoAdapterException (error);
1802                         }
1803
1804                         if (subnet.getSubnetName () != null) {
1805                                 curR = curR.replace ("%name%", subnet.getSubnetName ());
1806                         } else {
1807                                 curR = curR.replace ("%name%", subnet.getSubnetId ());
1808                         }
1809
1810                         if (subnet.getCidr () != null) {
1811                                 curR = curR.replace ("%cidr%", subnet.getCidr ());
1812                         } else {
1813                                 String error = "Missing Required cidr for subnet in HEAT Template";
1814               logger.error("{} {} {} ", MessageEnum.RA_MISSING_PARAM, ErrorCode.DataError.getValue(), error);
1815                                 throw new MsoAdapterException (error);
1816                         }
1817
1818                         if (subnet.getIpVersion () != null) {
1819                                 curR = curR + "      ip_version: " + subnet.getIpVersion () + "\n";
1820                         }
1821                         if (subnet.getEnableDHCP () != null) {
1822                                 curR = curR + "      enable_dhcp: " +  Boolean.toString (subnet.getEnableDHCP ()) + "\n";
1823                         }
1824                         if (subnet.getGatewayIp () != null && !subnet.getGatewayIp ().isEmpty() ) {
1825                                 curR = curR + "      gateway_ip: " + subnet.getGatewayIp () + "\n";
1826                         }
1827
1828                         if (subnet.getAllocationPools() != null) {
1829                                 curR = curR + "      allocation_pools:\n";
1830                                 for (Pool pool : subnet.getAllocationPools())
1831                                 {
1832                                         if (!commonUtils.isNullOrEmpty(pool.getStart()) && !commonUtils.isNullOrEmpty(pool.getEnd()))
1833                                         {
1834                                                 curR = curR + "       - start: " + pool.getStart () + "\n";
1835                                                 curR = curR + "         end: " + pool.getEnd () + "\n";
1836                                         }
1837                                 }
1838                         }
1839
1840                         resourcesBuf.append (curR);
1841
1842                         curO = outputTempl;
1843                         curO = curO.replace ("%subnetId%", subnet.getSubnetId ());
1844
1845                         outputsBuf.append (curO);
1846
1847                 }
1848                 // append resources and outputs in heatTemplate
1849         logger.debug("Tempate initial:{}", heatTemplate);
1850                 int outputsIdx = heatTemplate.indexOf ("outputs:");
1851                 heatTemplate = insertStr (heatTemplate, outputsBuf.toString (), outputsIdx + 8);
1852                 int resourcesIdx = heatTemplate.indexOf ("resources:");
1853                 heatTemplate = insertStr (heatTemplate, resourcesBuf.toString (), resourcesIdx + 10);
1854
1855         logger.debug("Template updated with all subnets:{}", heatTemplate);
1856                 return heatTemplate;
1857     }
1858
1859     private Map <String, String> getSubnetUUId(String key,  Map <String, Object> outputs, List <Subnet> subnets) {
1860
1861         Map <String, String> sMap = new HashMap <> ();
1862
1863         try{
1864                 Object obj = outputs.get(key);
1865                 ObjectMapper mapper = new ObjectMapper();
1866                 String jStr = mapper.writeValueAsString(obj);
1867           logger.debug("Subnet_Ipam Output JSON String:{} {}", obj.getClass(), jStr);
1868
1869                 JsonNode rootNode = mapper.readTree(jStr);
1870                 for (JsonNode sNode : rootNode.path("ipam_subnets"))
1871                 {
1872             logger.debug("Output Subnet Node {}", sNode.toString());
1873                         String name = sNode.path("subnet_name").textValue();
1874                         String uuid = sNode.path("subnet_uuid").textValue();
1875                         String aaiId = name; // default
1876                         // try to find aaiId for name in input subnetList
1877                         if (subnets != null)
1878                         {
1879                                 for (Subnet subnet : subnets)
1880                                 {
1881                                         if ( subnet !=  null && !commonUtils.isNullOrEmpty(subnet.getSubnetName()))
1882                                         {
1883                                                 if (subnet.getSubnetName().equals(name))
1884                                                 {
1885                                                         aaiId = subnet.getSubnetId();
1886                                                         break;
1887                                                 }
1888                                         }
1889                                 }
1890                         }
1891                         sMap.put(aaiId, uuid); //bpmn needs aaid to uuid map
1892                 }
1893         }
1894         catch (Exception e)
1895         {
1896           logger.error("{} {} Exception getting subnet-uuids ", MessageEnum.RA_MARSHING_ERROR,
1897               ErrorCode.DataError.getValue(), e);
1898         }
1899
1900         logger.debug("Return sMap {}", sMap.toString());
1901         return sMap;
1902     }
1903
1904     private static String insertStr (String template, String snippet, int index) {
1905
1906         String updatedTemplate;
1907
1908         logger.debug("Index:{} Snippet:{}", index, snippet);
1909
1910         String templateBeg = template.substring (0, index);
1911         String templateEnd = template.substring (index);
1912
1913         updatedTemplate = templateBeg + "\n" + snippet + templateEnd;
1914
1915         logger.debug("Template updated with a subnet:{}", updatedTemplate);
1916         return updatedTemplate;
1917     }
1918
1919 }