@Override
  public VirtualNetworkFunctionRecord stopVNFCInstance(
      VirtualNetworkFunctionRecord virtualNetworkFunctionRecord, VNFCInstance vnfcInstance)
      throws Exception {

    log.info("Stopping vnfc instance: " + vnfcInstance.getHostname());

    if (VnfmUtils.getLifecycleEvent(virtualNetworkFunctionRecord.getLifecycle_event(), Event.STOP)
        != null) {
      if (VnfmUtils.getLifecycleEvent(virtualNetworkFunctionRecord.getLifecycle_event(), Event.STOP)
              .getLifecycle_events()
          != null) {
        String output = "\n--------------------\n--------------------\n";
        for (String result :
            executeScriptsForEvent(virtualNetworkFunctionRecord, vnfcInstance, Event.STOP)) {
          output +=
              this.parser
                  .fromJson(result, JsonObject.class)
                  .get("output")
                  .getAsString()
                  .replaceAll("\\\\n", "\n");
          output += "\n--------------------\n";
        }
        output += "\n--------------------\n";
        log.info(
            "Executed script for STOP on VNFC Instance "
                + vnfcInstance.getHostname()
                + ". Output was: \n\n"
                + output);
      }
    }

    return virtualNetworkFunctionRecord;
  }
  @Override
  public VirtualNetworkFunctionRecord start(
      VirtualNetworkFunctionRecord virtualNetworkFunctionRecord) throws Exception {

    log.info("Starting vnfr: " + virtualNetworkFunctionRecord.getName());

    if (VnfmUtils.getLifecycleEvent(virtualNetworkFunctionRecord.getLifecycle_event(), Event.START)
        != null) {
      if (VnfmUtils.getLifecycleEvent(
                  virtualNetworkFunctionRecord.getLifecycle_event(), Event.START)
              .getLifecycle_events()
          != null) {
        String output = "\n--------------------\n--------------------\n";
        for (String result : executeScriptsForEvent(virtualNetworkFunctionRecord, Event.START)) {
          output +=
              this.parser
                  .fromJson(result, JsonObject.class)
                  .get("output")
                  .getAsString()
                  .replaceAll("\\\\n", "\n");
          output += "\n--------------------\n";
        }
        output += "\n--------------------\n";
        log.info("Executed script for START. Output was: \n\n" + output);
      }
    }
    return virtualNetworkFunctionRecord;
  }
  @Override
  public VirtualNetworkFunctionRecord heal(
      VirtualNetworkFunctionRecord virtualNetworkFunctionRecord,
      VNFCInstance component,
      String cause)
      throws Exception {

    if ("switchToStandby".equals(cause)) {
      for (VirtualDeploymentUnit virtualDeploymentUnit : virtualNetworkFunctionRecord.getVdu()) {
        for (VNFCInstance vnfcInstance : virtualDeploymentUnit.getVnfc_instance()) {
          if (vnfcInstance.getId().equals(component.getId())
              && "standby".equalsIgnoreCase(vnfcInstance.getState())) {
            log.debug("Activation of the standby component");
            if (VnfmUtils.getLifecycleEvent(
                    virtualNetworkFunctionRecord.getLifecycle_event(), Event.START)
                != null) {
              log.debug(
                  "Executed scripts for event START "
                      + this.executeScriptsForEvent(
                          virtualNetworkFunctionRecord, component, Event.START));
            }
            log.debug("Changing the status from standby to active");
            // This is inside the vnfr
            vnfcInstance.setState("ACTIVE");
            // This is a copy of the object received as parameter and modified.
            // It will be sent to the orchestrator
            component.setState("ACTIVE");
            break;
          }
        }
      }
    } else if (VnfmUtils.getLifecycleEvent(
            virtualNetworkFunctionRecord.getLifecycle_event(), Event.HEAL)
        != null) {
      if (VnfmUtils.getLifecycleEvent(virtualNetworkFunctionRecord.getLifecycle_event(), Event.HEAL)
              .getLifecycle_events()
          != null) {
        log.debug("Heal method started");
        log.info("-----------------------------------------------------------------------");
        String output = "\n--------------------\n--------------------\n";
        for (String result :
            executeScriptsForEvent(virtualNetworkFunctionRecord, component, Event.HEAL, cause)) {
          output +=
              this.parser
                  .fromJson(result, JsonObject.class)
                  .get("output")
                  .getAsString()
                  .replaceAll("\\\\n", "\n");
          output += "\n--------------------\n";
        }
        output += "\n--------------------\n";
        log.info("Executed script for HEAL. Output was: \n\n" + output);
        log.info("-----------------------------------------------------------------------");
      }
    }
    return virtualNetworkFunctionRecord;
  }
  public Iterable<String> executeScriptsForEvent(
      VirtualNetworkFunctionRecord virtualNetworkFunctionRecord, Event event)
      throws Exception { // TODO make it parallel
    Map<String, String> env = getMap(virtualNetworkFunctionRecord);
    Collection<String> res = new ArrayList<>();
    LifecycleEvent le =
        VnfmUtils.getLifecycleEvent(virtualNetworkFunctionRecord.getLifecycle_event(), event);

    if (le != null) {
      log.trace(
          "The number of scripts for "
              + virtualNetworkFunctionRecord.getName()
              + " are: "
              + le.getLifecycle_events());
      for (String script : le.getLifecycle_events()) {
        log.info(
            "Sending script: "
                + script
                + " to VirtualNetworkFunctionRecord: "
                + virtualNetworkFunctionRecord.getName());
        for (VirtualDeploymentUnit vdu : virtualNetworkFunctionRecord.getVdu()) {
          for (VNFCInstance vnfcInstance : vdu.getVnfc_instance()) {

            Map<String, String> tempEnv = new HashMap<>();
            for (Ip ip : vnfcInstance.getIps()) {
              log.debug("Adding net: " + ip.getNetName() + " with value: " + ip.getIp());
              tempEnv.put(ip.getNetName(), ip.getIp());
            }
            log.debug("adding floatingIp: " + vnfcInstance.getFloatingIps());
            for (Ip fip : vnfcInstance.getFloatingIps()) {
              tempEnv.put(fip.getNetName() + "_floatingIp", fip.getIp());
            }

            tempEnv.put("hostname", vnfcInstance.getHostname());
            tempEnv = modifyUnsafeEnvVarNames(tempEnv);
            env.putAll(tempEnv);
            log.info("Environment Variables are: " + env);

            String command = getJsonObject("EXECUTE", script, env).toString();
            String output =
                executeActionOnEMS(
                    vnfcInstance.getHostname(),
                    command,
                    virtualNetworkFunctionRecord,
                    vnfcInstance);
            res.add(output);

            saveLogToFile(virtualNetworkFunctionRecord, script, vnfcInstance, output);
            for (String key : tempEnv.keySet()) {
              env.remove(key);
            }
          }
        }
      }
    }
    return res;
  }
  @Override
  public VirtualNetworkFunctionRecord modify(
      VirtualNetworkFunctionRecord virtualNetworkFunctionRecord, VNFRecordDependency dependency)
      throws Exception {
    log.trace(
        "VirtualNetworkFunctionRecord VERSION is: " + virtualNetworkFunctionRecord.getHb_version());
    log.info("executing modify for VNFR: " + virtualNetworkFunctionRecord.getName());

    log.debug("Got dependency: " + dependency);
    log.debug("Parameters are: ");
    for (Entry<String, DependencyParameters> entry : dependency.getParameters().entrySet()) {
      log.debug("Source type: " + entry.getKey());
      log.debug("Parameters: " + entry.getValue().getParameters());
    }

    if (VnfmUtils.getLifecycleEvent(
            virtualNetworkFunctionRecord.getLifecycle_event(), Event.CONFIGURE)
        != null) {
      log.debug(
          "LifeCycle events: "
              + VnfmUtils.getLifecycleEvent(
                      virtualNetworkFunctionRecord.getLifecycle_event(), Event.CONFIGURE)
                  .getLifecycle_events());
      log.info("-----------------------------------------------------------------------");
      String output = "\n--------------------\n--------------------\n";
      for (String result :
          executeScriptsForEvent(virtualNetworkFunctionRecord, Event.CONFIGURE, dependency)) {
        output +=
            this.parser
                .fromJson(result, JsonObject.class)
                .get("output")
                .getAsString()
                .replaceAll("\\\\n", "\n");
        output += "\n--------------------\n";
      }
      output += "\n--------------------\n";
      log.info("Executed script for CONFIGURE. Output was: \n\n" + output);
      log.info("-----------------------------------------------------------------------");
    } else {
      log.debug("No LifeCycle events for Event.CONFIGURE");
    }
    return virtualNetworkFunctionRecord;
  }
 // When the EMS reveive a script which terminate the vnf, the EMS is still running.
 // Once the vnf is terminated NFVO requests deletion of resources (MANO B.5) and the EMS will be
 // terminated.
 @Override
 public VirtualNetworkFunctionRecord terminate(
     VirtualNetworkFunctionRecord virtualNetworkFunctionRecord) throws Exception {
   log.debug("Termination of VNF: " + virtualNetworkFunctionRecord.getName());
   if (VnfmUtils.getLifecycleEvent(
           virtualNetworkFunctionRecord.getLifecycle_event(), Event.TERMINATE)
       != null) {
     String output = "\n--------------------\n--------------------\n";
     for (String result : executeScriptsForEvent(virtualNetworkFunctionRecord, Event.TERMINATE)) {
       output +=
           this.parser
               .fromJson(result, JsonObject.class)
               .get("output")
               .getAsString()
               .replaceAll("\\\\n", "\n");
       output += "\n--------------------\n";
     }
     output += "\n--------------------\n";
     log.info("Executed script for TERMINATE. Output was: \n\n" + output);
   }
   return virtualNetworkFunctionRecord;
 }
 @Override
 public void handleError(VirtualNetworkFunctionRecord virtualNetworkFunctionRecord) {
   log.error("Received Error for VNFR " + virtualNetworkFunctionRecord.getName());
   if (VnfmUtils.getLifecycleEvent(virtualNetworkFunctionRecord.getLifecycle_event(), Event.ERROR)
       != null) {
     String output = "\n--------------------\n--------------------\n";
     try {
       for (String result : executeScriptsForEvent(virtualNetworkFunctionRecord, Event.ERROR)) {
         output +=
             this.parser
                 .fromJson(result, JsonObject.class)
                 .get("output")
                 .getAsString()
                 .replaceAll("\\\\n", "\n");
         output += "\n--------------------\n";
       }
     } catch (Exception e) {
       e.printStackTrace();
       log.error("Exception executing Error handling");
     }
     output += "\n--------------------\n";
     log.info("Executed script for ERROR. Output was: \n\n" + output);
   }
 }
  private Iterable<String> executeScriptsForEvent(
      VirtualNetworkFunctionRecord virtualNetworkFunctionRecord,
      VNFCInstance vnfcInstance,
      Event event,
      VNFRecordDependency dependency)
      throws Exception {
    Map<String, String> env = getMap(virtualNetworkFunctionRecord);
    List<String> res = new ArrayList<>();
    LifecycleEvent le =
        VnfmUtils.getLifecycleEvent(virtualNetworkFunctionRecord.getLifecycle_event(), event);
    log.trace(
        "The number of scripts for "
            + virtualNetworkFunctionRecord.getName()
            + " are: "
            + le.getLifecycle_events());
    log.debug("DEPENDENCY IS: " + dependency);
    if (le != null) {
      for (String script : le.getLifecycle_events()) {
        int indexOf = script.indexOf('_');
        VNFCDependencyParameters vnfcDependencyParameters = null;
        String type = null;
        if (indexOf != -1) {
          type = script.substring(0, indexOf);
          vnfcDependencyParameters = dependency.getVnfcParameters().get(type);
        }
        if (vnfcDependencyParameters != null) {
          log.debug(
              "There are "
                  + vnfcDependencyParameters.getParameters().size()
                  + " VNFCInstanceForeign");
          for (String vnfcForeignId : vnfcDependencyParameters.getParameters().keySet()) {
            log.info("Running script: " + script + " for VNFCInstance foreign id " + vnfcForeignId);

            log.info(
                "Sending command: "
                    + script
                    + " to adding relation with type: "
                    + type
                    + " from VirtualNetworkFunctionRecord "
                    + virtualNetworkFunctionRecord.getName());

            Map<String, String> tempEnv = new HashMap<>();

            // Adding own ips
            for (Ip ip : vnfcInstance.getIps()) {
              log.debug("Adding net: " + ip.getNetName() + " with value: " + ip.getIp());
              tempEnv.put(ip.getNetName(), ip.getIp());
            }

            // Adding own floating ip
            log.debug("adding floatingIp: " + vnfcInstance.getFloatingIps());
            for (Ip fip : vnfcInstance.getFloatingIps()) {
              tempEnv.put(fip.getNetName() + "_floatingIp", fip.getIp());
            }
            // Adding foreign parameters such as ip
            if (script.contains("_")) {
              // Adding foreign parameters such as ip
              Map<String, String> parameters = dependency.getParameters().get(type).getParameters();
              for (Entry<String, String> param : parameters.entrySet()) {
                tempEnv.put(type + "_" + param.getKey(), param.getValue());
              }

              Map<String, String> parametersVNFC =
                  vnfcDependencyParameters.getParameters().get(vnfcForeignId).getParameters();
              for (Entry<String, String> param : parametersVNFC.entrySet()) {
                tempEnv.put(type + "_" + param.getKey(), param.getValue());
              }
            }

            tempEnv.put("hostname", vnfcInstance.getHostname());
            tempEnv = modifyUnsafeEnvVarNames(tempEnv);
            env.putAll(tempEnv);
            log.info("The Environment Variables for script " + script + " are: " + env);

            String command = getJsonObject("EXECUTE", script, env).toString();
            String output =
                executeActionOnEMS(
                    vnfcInstance.getHostname(),
                    command,
                    virtualNetworkFunctionRecord,
                    vnfcInstance);
            res.add(output);

            saveLogToFile(virtualNetworkFunctionRecord, script, vnfcInstance, output);
            for (String key : tempEnv.keySet()) {
              env.remove(key);
            }
          }
        }
      }
    }
    return res;
  }
  @Override
  public VirtualNetworkFunctionRecord scale(
      Action scaleInOrOut,
      VirtualNetworkFunctionRecord virtualNetworkFunctionRecord,
      VNFComponent component,
      Object scripts,
      VNFRecordDependency dependency)
      throws Exception {
    VNFCInstance vnfcInstance = (VNFCInstance) component;
    if (scaleInOrOut.ordinal() == Action.SCALE_OUT.ordinal()) {
      log.info("Created VNFComponent");
      saveScriptOnEms(vnfcInstance, scripts, virtualNetworkFunctionRecord);
      String output = "\n--------------------\n--------------------\n";
      for (String result :
          executeScriptsForEvent(virtualNetworkFunctionRecord, vnfcInstance, Event.INSTANTIATE)) {
        output +=
            this.parser
                .fromJson(result, JsonObject.class)
                .get("output")
                .getAsString()
                .replaceAll("\\\\n", "\n");
        output += "\n--------------------\n";
      }
      output += "\n--------------------\n";
      log.info("Executed script for INSTANTIATE. Output was: \n\n" + output);

      if (dependency != null) {
        output = "\n--------------------\n--------------------\n";
        for (String result :
            executeScriptsForEvent(
                virtualNetworkFunctionRecord, vnfcInstance, Event.CONFIGURE, dependency)) {
          output +=
              this.parser
                  .fromJson(result, JsonObject.class)
                  .get("output")
                  .getAsString()
                  .replaceAll("\\\\n", "\n");
          output += "\n--------------------\n";
        }
        output += "\n--------------------\n";
        log.info("Executed script for CONFIGURE. Output was: \n\n" + output);
      }

      if ((vnfcInstance.getState() == null) || !vnfcInstance.getState().equals("STANDBY")) {
        if (VnfmUtils.getLifecycleEvent(
                virtualNetworkFunctionRecord.getLifecycle_event(), Event.START)
            != null) {
          output = "\n--------------------\n--------------------\n";
          for (String result :
              executeScriptsForEvent(virtualNetworkFunctionRecord, vnfcInstance, Event.START)) {
            output +=
                this.parser
                    .fromJson(result, JsonObject.class)
                    .get("output")
                    .getAsString()
                    .replaceAll("\\\\n", "\n");
            output += "\n--------------------\n";
          }
          output += "\n--------------------\n";
          log.info("Executed script for START. Output was: \n\n" + output);
        }
      }

      log.trace("HB_VERSION == " + virtualNetworkFunctionRecord.getHb_version());
      return virtualNetworkFunctionRecord;
    } else { // SCALE_IN

      String output = "\n--------------------\n--------------------\n";
      for (String result :
          executeScriptsForEventOnVnfr(
              virtualNetworkFunctionRecord, vnfcInstance, Event.SCALE_IN)) {
        output +=
            this.parser
                .fromJson(result, JsonObject.class)
                .get("output")
                .getAsString()
                .replaceAll("\\\\n", "\n");
        output += "\n--------------------\n";
      }
      output += "\n--------------------\n";
      log.info("Executed script for SCALE_IN. Output was: \n\n" + output);

      return virtualNetworkFunctionRecord;
    }
  }
  public Iterable<String> executeScriptsForEvent(
      VirtualNetworkFunctionRecord virtualNetworkFunctionRecord,
      Event event,
      VNFRecordDependency dependency)
      throws Exception {
    Map<String, String> env = getMap(virtualNetworkFunctionRecord);
    LifecycleEvent le =
        VnfmUtils.getLifecycleEvent(virtualNetworkFunctionRecord.getLifecycle_event(), event);
    List<String> res = new ArrayList<>();
    if (le != null) {
      for (String script : le.getLifecycle_events()) {

        String type = null;
        if (script.contains("_")) {
          type = script.substring(0, script.indexOf('_'));
          log.info(
              "Sending command: "
                  + script
                  + " to adding relation with type: "
                  + type
                  + " from VirtualNetworkFunctionRecord "
                  + virtualNetworkFunctionRecord.getName());
        }

        for (VirtualDeploymentUnit vdu : virtualNetworkFunctionRecord.getVdu()) {
          for (VNFCInstance vnfcInstance : vdu.getVnfc_instance()) {
            if (dependency.getVnfcParameters().get(type) != null) {
              for (String vnfcId :
                  dependency.getVnfcParameters().get(type).getParameters().keySet()) {

                Map<String, String> tempEnv = new HashMap<>();

                // Adding own ips
                for (Ip ip : vnfcInstance.getIps()) {
                  log.debug("Adding net: " + ip.getNetName() + " with value: " + ip.getIp());
                  tempEnv.put(ip.getNetName(), ip.getIp());
                }

                // Adding own floating ip
                for (Ip fip : vnfcInstance.getFloatingIps()) {
                  log.debug("adding floatingIp: " + fip.getNetName() + " = " + fip.getIp());
                  tempEnv.put(fip.getNetName() + "_floatingIp", fip.getIp());
                }

                if (script.contains("_")) {
                  // Adding foreign parameters such as ip
                  log.debug("Fetching parameter from dependency of type: " + type);
                  Map<String, String> parameters =
                      dependency.getParameters().get(type).getParameters();

                  for (Map.Entry<String, String> param : parameters.entrySet()) {
                    log.debug(
                        "adding param: " + type + "_" + param.getKey() + " = " + param.getValue());
                    tempEnv.put(type + "_" + param.getKey(), param.getValue());
                  }

                  Map<String, String> parametersVNFC =
                      dependency
                          .getVnfcParameters()
                          .get(type)
                          .getParameters()
                          .get(vnfcId)
                          .getParameters();
                  for (Map.Entry<String, String> param : parametersVNFC.entrySet()) {
                    log.debug(
                        "adding param: " + type + "_" + param.getKey() + " = " + param.getValue());
                    tempEnv.put(type + "_" + param.getKey(), param.getValue());
                  }
                }

                tempEnv.put("hostname", vnfcInstance.getHostname());
                tempEnv = modifyUnsafeEnvVarNames(tempEnv);
                env.putAll(tempEnv);
                log.info("Environment Variables are: " + env);

                String command = getJsonObject("EXECUTE", script, env).toString();
                String output =
                    executeActionOnEMS(
                        vnfcInstance.getHostname(),
                        command,
                        virtualNetworkFunctionRecord,
                        vnfcInstance);
                res.add(output);

                saveLogToFile(virtualNetworkFunctionRecord, script, vnfcInstance, output);
                for (String key : tempEnv.keySet()) {
                  env.remove(key);
                }
              }
            }
          }
        }
      }
    }
    return res;
  }