content: //
  // Licensed to the Apache Software Foundation (ASF) under one
  // or more contributor license agreements.  See the NOTICE file
  // distributed with this work for additional information
  // regarding copyright ownership.  The ASF licenses this file
  // to you under the Apache License, Version 2.0 (the
  // "License"); you may not use this file except in compliance
  // with the License.  You may obtain a copy of the License at
  //
  //   http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing,
  // software distributed under the License is distributed on an
  // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  // KIND, either express or implied.  See the License for the
  // specific language governing permissions and limitations
  // under the License.
  package org.apache.cloudstack.diagnostics;
  
  import static org.apache.cloudstack.diagnostics.DiagnosticsHelper.getTimeDifference;
  import static org.apache.cloudstack.diagnostics.fileprocessor.DiagnosticsFilesList.RouterDefaultSupportedFiles;
  import static org.apache.cloudstack.diagnostics.fileprocessor.DiagnosticsFilesList.SystemVMDefaultSupportedFiles;
  
  import java.io.File;
  import java.util.ArrayList;
  import java.util.List;
  import java.util.Map;
  import java.util.regex.Pattern;
  
  import javax.inject.Inject;
  import javax.naming.ConfigurationException;
  
  import com.cloud.capacity.CapacityManager;
  import org.apache.cloudstack.api.command.admin.diagnostics.GetDiagnosticsDataCmd;
  import org.apache.cloudstack.api.command.admin.diagnostics.RunDiagnosticsCmd;
  import org.apache.cloudstack.diagnostics.fileprocessor.DiagnosticsFilesList;
  import org.apache.cloudstack.diagnostics.fileprocessor.DiagnosticsFilesListFactory;
  import org.apache.cloudstack.diagnostics.to.DiagnosticsDataObject;
  import org.apache.cloudstack.diagnostics.to.DiagnosticsDataTO;
  import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
  import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
  import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
  import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
  import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope;
  import org.apache.cloudstack.framework.config.ConfigKey;
  import org.apache.cloudstack.framework.config.Configurable;
  import org.apache.cloudstack.managed.context.ManagedContextRunnable;
  import org.apache.cloudstack.poll.BackgroundPollManager;
  import org.apache.cloudstack.poll.BackgroundPollTask;
  import org.apache.cloudstack.storage.NfsMountManager;
  import org.apache.cloudstack.storage.image.datastore.ImageStoreEntity;
  import org.apache.commons.collections.CollectionUtils;
  import org.apache.commons.lang3.StringUtils;
  
  import com.cloud.agent.AgentManager;
  import com.cloud.agent.api.Answer;
  import com.cloud.agent.api.routing.NetworkElementCommand;
  import com.cloud.agent.api.to.DataTO;
  import com.cloud.dc.DataCenterVO;
  import com.cloud.dc.dao.DataCenterDao;
  import com.cloud.event.ActionEvent;
  import com.cloud.event.EventTypes;
  import com.cloud.exception.InvalidParameterValueException;
  import com.cloud.hypervisor.Hypervisor;
  import com.cloud.server.StatsCollector;
  import com.cloud.storage.ImageStoreDetailsUtil;
  import com.cloud.storage.Storage;
  import com.cloud.utils.Pair;
  import com.cloud.utils.component.ManagerBase;
  import com.cloud.utils.component.PluggableService;
  import com.cloud.utils.exception.CloudRuntimeException;
  import com.cloud.utils.ssh.SshHelper;
  import com.cloud.vm.VMInstanceVO;
  import com.cloud.vm.VirtualMachine;
  import com.cloud.vm.VirtualMachineManager;
  import com.cloud.vm.dao.VMInstanceDao;
  
  public class DiagnosticsServiceImpl extends ManagerBase implements PluggableService, DiagnosticsService, Configurable {
  
      @Inject
      private AgentManager agentManager;
      @Inject
      private VMInstanceDao instanceDao;
      @Inject
      private VirtualMachineManager vmManager;
      @Inject
      private NetworkOrchestrationService networkManager;
      @Inject
      private StatsCollector statsCollector;
      @Inject
      private DataStoreManager storeMgr;
      @Inject
      private BackgroundPollManager backgroundPollManager;
      @Inject
      private ImageStoreDetailsUtil imageStoreDetailsUtil;
      @Inject
      private NfsMountManager mountManager;
      @Inject
      private DataCenterDao dataCenterDao;
  
      // These 2 settings should require a restart of the management server
      private static final ConfigKey<Boolean> EnableGarbageCollector = new ConfigKey<>("Advanced", Boolean.class,
              "diagnostics.data.gc.enable", "true",
              "Enable the garbage collector background task to delete old files from secondary storage.", false);
      private static final ConfigKey<Integer> GarbageCollectionInterval = new ConfigKey<>("Advanced", Integer.class,
              "diagnostics.data.gc.interval", "86400",
              "The interval at which the garbage collector background tasks in seconds", false, EnableGarbageCollector.key());
  
      // These are easily computed properties and need not need a restart of the management server
      private static final ConfigKey<Long> DataRetrievalTimeout = new ConfigKey<>("Advanced", Long.class,
              "diagnostics.data.retrieval.timeout", "1800",
              "Overall system VM script execution time out in seconds.", true);
      private static final ConfigKey<Long> MaximumFileAgeforGarbageCollection = new ConfigKey<>("Advanced", Long.class,
              "diagnostics.data.max.file.age", "86400",
              "Sets the maximum time in seconds a file can stay in secondary storage before it is deleted.", true, EnableGarbageCollector.key());
      private static final ConfigKey<Double> DiskQuotaPercentageThreshold = new ConfigKey<>("Advanced", Double.class,
              "diagnostics.data.disable.threshold", "0.9",
              "Sets the secondary storage disk utilisation percentage for file retrieval. " +
                      "Used to look for suitable secondary storage with enough space, otherwise an exception is " +
                      "thrown when no secondary store is found.", true);
  
      @Override
      @ActionEvent(eventType = EventTypes.EVENT_SYSTEM_VM_DIAGNOSTICS, eventDescription = "running diagnostics on system vm", async = true)
      public Map<String, String> runDiagnosticsCommand(final RunDiagnosticsCmd cmd) {
          final Long vmId = cmd.getId();
          final String cmdType = cmd.getType().getValue();
          final String ipAddress = cmd.getAddress();
          final String optionalArguments = cmd.getOptionalArguments();
          final VMInstanceVO vmInstance = instanceDao.findByIdTypes(vmId, VirtualMachine.Type.ConsoleProxy, VirtualMachine.Type.DomainRouter, VirtualMachine.Type.SecondaryStorageVm);
  
          if (vmInstance == null) {
              throw new InvalidParameterValueException("Unable to find a system vm with id " + vmId);
          }
          final Long hostId = vmInstance.getHostId();
  
          if (hostId == null) {
              throw new CloudRuntimeException("Unable to find host for virtual machine instance: " + vmInstance.getInstanceName());
          }
  
          final String shellCmd = prepareShellCmd(cmdType, ipAddress, optionalArguments);
  
          if (StringUtils.isEmpty(shellCmd)) {
              throw new IllegalArgumentException("Optional parameters contain unwanted characters: " + optionalArguments);
          }
  
          final Hypervisor.HypervisorType hypervisorType = vmInstance.getHypervisorType();
  
          final DiagnosticsCommand command = new DiagnosticsCommand(shellCmd, vmManager.getExecuteInSequence(hypervisorType));
          final Map<String, String> accessDetails = networkManager.getSystemVMAccessDetails(vmInstance);
  
          if (StringUtils.isEmpty(accessDetails.get(NetworkElementCommand.ROUTER_IP))) {
              throw new CloudRuntimeException("Unable to set system vm ControlIP for system vm: " + vmInstance);
          }
  
          command.setAccessDetail(accessDetails);
  
          Map<String, String> detailsMap;
  
          Answer answer = agentManager.easySend(hostId, command);
  
          if (answer != null) {
              detailsMap = ((DiagnosticsAnswer) answer).getExecutionDetails();
              return detailsMap;
          } else {
              throw new CloudRuntimeException("Failed to execute diagnostics command for system vm: " + vmInstance + ", on remote host: " + vmInstance.getHostName());
          }
      }
  
      protected boolean hasValidChars(String optionalArgs) {
          if (StringUtils.isEmpty(optionalArgs)) {
              return true;
          } else {
              final String regex = "^[\\w\\-\\s.]+$";
              final Pattern pattern = Pattern.compile(regex);
              return pattern.matcher(optionalArgs).find();
          }
      }
  
      protected String prepareShellCmd(String cmdType, String ipAddress, String optionalParams) {
          final String CMD_TEMPLATE = String.format("%s %s", cmdType, ipAddress);
          if (StringUtils.isEmpty(optionalParams)) {
              return CMD_TEMPLATE;
          } else {
              if (hasValidChars(optionalParams)) {
                  return String.format("%s %s", CMD_TEMPLATE, optionalParams);
              } else {
                  return null;
              }
          }
      }
  
      private String zipFilesInSystemVm(VMInstanceVO vmInstance, List<String> optionalFilesList) {
          List<String> fileList = getFileListToBeRetrieved(optionalFilesList, vmInstance);
  
          if (CollectionUtils.isEmpty(fileList)) {
              throw new CloudRuntimeException("Failed to generate diagnostics file list for retrieval.");
          }
  
          final Answer zipFilesAnswer = prepareDiagnosticsFilesInSystemVm(vmInstance, fileList);
  
          if (zipFilesAnswer == null) {
              throw new CloudRuntimeException(String.format("Failed to generate diagnostics zip file in the system VM %s", vmInstance.getUuid()));
          }
  
          if (!zipFilesAnswer.getResult()) {
              throw new CloudRuntimeException(String.format("Failed to generate diagnostics zip file in VM %s due to: %s", vmInstance.getUuid(), zipFilesAnswer.getDetails()));
          }
  
          return zipFilesAnswer.getDetails().replace("\n", "");
      }
  
      @Override
      @ActionEvent(eventType = EventTypes.EVENT_SYSTEM_VM_DIAGNOSTICS, eventDescription = "getting diagnostics files on system vm", async = true)
      public String getDiagnosticsDataCommand(GetDiagnosticsDataCmd cmd) {
          final Long vmId = cmd.getId();
          final List<String> optionalFilesList = cmd.getFilesList();
          final VMInstanceVO vmInstance = getSystemVMInstance(vmId);
          final DataStore store = getImageStore(vmInstance.getDataCenterId());
  
          final String zipFileInSystemVm = zipFilesInSystemVm(vmInstance, optionalFilesList);
          final Long vmHostId = vmInstance.getHostId();
          copyZipFileToSecondaryStorage(vmInstance, vmHostId, zipFileInSystemVm, store);
          deleteDiagnosticsZipFileInsystemVm(vmInstance, zipFileInSystemVm);
  
          // Now we need to create the file download URL
          // Find ssvm of store
          final long zoneId = vmInstance.getDataCenterId();
          VMInstanceVO ssvm = getSecondaryStorageVmInZone(zoneId);
          if (ssvm == null) {
              throw new CloudRuntimeException("No SSVM found in zone: " + dataCenterDao.findById(zoneId));
          }
  
          // Secondary Storage install path = "diagnostics_data/diagnostics_files_xxxx.tar
          String installPath = DIAGNOSTICS_DIRECTORY + File.separator + zipFileInSystemVm.replace("/root", "");
          return createFileDownloadUrl(store, ssvm.getHypervisorType(), installPath);
      }
  
      /**
       * Copy retrieved diagnostics zip file from system vm to secondary storage
       * For VMware use the mgmt server, and for Xen/KVM use the hyperhost of the target VM
       * The strategy is to mount secondary storage on mgmt server or host and scp directly to /mnt/SecStorage/diagnostics_data
       *
       * @param fileToCopy zip file in system vm to be copied
       * @param store      secondary storage to copy zip file to
       */
      private Pair<Boolean, String> copyZipFileToSecondaryStorage(VMInstanceVO vmInstance, Long vmHostId, String fileToCopy, DataStore store) {
          String vmControlIp = getVMSshIp(vmInstance);
          if (StringUtils.isBlank(vmControlIp)) {
              return new Pair<>(false, "Unable to find system vm ssh/control IP for  vm with ID: " + vmInstance.getId());
          }
          Pair<Boolean, String> copyResult;
          if (vmInstance.getHypervisorType() == Hypervisor.HypervisorType.VMware) {
              copyResult = copyToSecondaryStorageVMware(store, vmControlIp, fileToCopy);
          } else {
              copyResult = copyToSecondaryStorageNonVMware(store, vmControlIp, fileToCopy, vmHostId);
          }
  
          if (!copyResult.first()) {
              throw new CloudRuntimeException(String.format("Failed to copy %s to secondary storage %s due to: %s.", fileToCopy, store.getUri(), copyResult.second()));
          }
  
          return copyResult;
      }
  
      private void configureNetworkElementCommand(NetworkElementCommand cmd, VMInstanceVO vmInstance) {
          Map<String, String> accessDetails = networkManager.getSystemVMAccessDetails(vmInstance);
          if (StringUtils.isBlank(accessDetails.get(NetworkElementCommand.ROUTER_IP))) {
              throw new CloudRuntimeException(String.format("Unable to set system vm ControlIP for system vm: %s", vmInstance));
          }
          cmd.setAccessDetail(accessDetails);
      }
  
      private Answer prepareDiagnosticsFilesInSystemVm(VMInstanceVO vmInstance, List<String> fileList) {
          final PrepareFilesCommand cmd = new PrepareFilesCommand(fileList, DataRetrievalTimeout.value());
          configureNetworkElementCommand(cmd, vmInstance);
          Answer answer = agentManager.easySend(vmInstance.getHostId(), cmd);
          return answer;
      }
  
      private Answer deleteDiagnosticsZipFileInsystemVm(VMInstanceVO vmInstance, String zipFileName) {
          final DeleteFileInVrCommand cmd = new DeleteFileInVrCommand(zipFileName);
          configureNetworkElementCommand(cmd, vmInstance);
          final Answer fileCleanupAnswer = agentManager.easySend(vmInstance.getHostId(), cmd);
          if (fileCleanupAnswer == null) {
              logger.error("Failed to cleanup diagnostics zip file on vm: {}", vmInstance);
          } else {
              if (!fileCleanupAnswer.getResult()) {
                  logger.error("Zip file cleanup for vm {} has failed with: {}", vmInstance, fileCleanupAnswer.getDetails());
              }
          }
  
          return fileCleanupAnswer;
      }
  
      /**
       * Generate a list of diagnostics file to be retrieved depending on the system VM type
       *
       * @param optionalFileList Optional list of files that user may want to retrieve, empty by default
       * @param vmInstance       system VM instance, either SSVM, CPVM or VR
       * @return a list of files to be retrieved for system VM, either generated from defaults depending on the VM type, or specified
       * by the optional list param
       */
      private List<String> getFileListToBeRetrieved(List<String> optionalFileList, VMInstanceVO vmInstance) {
          DiagnosticsFilesList fileListObject = DiagnosticsFilesListFactory.getDiagnosticsFilesList(optionalFileList, vmInstance);
          List<String> fileList = new ArrayList<>();
  
          if (fileListObject != null) {
              fileList = fileListObject.generateFileList();
          }
          return fileList;
      }
  
      private Pair<Boolean, String> copyToSecondaryStorageNonVMware(final DataStore store, final String vmControlIp, String fileToCopy, Long vmHostId) {
          String nfsVersion = CapacityManager.ImageStoreNFSVersion.valueIn(store.getId());
          CopyToSecondaryStorageCommand toSecondaryStorageCommand = new CopyToSecondaryStorageCommand(store.getUri(), vmControlIp, fileToCopy, nfsVersion);
          Answer copyToSecondaryAnswer = agentManager.easySend(vmHostId, toSecondaryStorageCommand);
          Pair<Boolean, String> copyAnswer;
          if (copyToSecondaryAnswer != null) {
              copyAnswer = new Pair<>(copyToSecondaryAnswer.getResult(), copyToSecondaryAnswer.getDetails());
          } else {
              copyAnswer = new Pair<>(false, "Diagnostics Zip file to secondary storage failed");
          }
          return copyAnswer;
      }
  
      private Pair<Boolean, String> copyToSecondaryStorageVMware(final DataStore store, final String vmSshIp, String diagnosticsFile) {
          logger.info(String.format("Copying %s from %s to secondary store %s", diagnosticsFile, vmSshIp, store.getUri()));
          boolean success = false;
          String mountPoint = mountManager.getMountPoint(store.getUri(), imageStoreDetailsUtil.getNfsVersion(store.getId()));
          if (StringUtils.isBlank(mountPoint)) {
              logger.error("Failed to generate mount point for copying to secondary storage for {}", store);
              return new Pair<>(false, "Failed to mount secondary storage:" + store.getName());
          }
  
          // dirIn/mnt/SecStorage/uuid/diagnostics_data
          String dataDirectoryInSecondaryStore = String.format("%s/%s", mountPoint, DIAGNOSTICS_DIRECTORY);
          try {
              File dataDirectory = new File(dataDirectoryInSecondaryStore);
              boolean existsInSecondaryStore = dataDirectory.exists() || dataDirectory.mkdir();
              if (existsInSecondaryStore) {
                  // scp from system VM to mounted sec storage directory
                  String homeDir = System.getProperty("user.home");
                  File permKey = new File(homeDir + "/.ssh/id_rsa");
                  SshHelper.scpFrom(vmSshIp, 3922, "root", permKey, dataDirectoryInSecondaryStore, diagnosticsFile);
              }
  
              // Verify File copy to Secondary Storage
              File fileInSecondaryStore = new File(dataDirectoryInSecondaryStore + diagnosticsFile.replace("/root", ""));
              success = fileInSecondaryStore.exists();
          } catch (Exception e) {
              String msg = String.format("Exception caught during scp from %s to secondary store %s: ", vmSshIp, dataDirectoryInSecondaryStore);
              logger.error(msg, e);
              return new Pair<>(false, msg);
          }
  
          return new Pair<>(success, "File copied to secondary storage successfully");
      }
  
      // Get ssvm from the zone to use for creating entity download URL
      private VMInstanceVO getSecondaryStorageVmInZone(Long zoneId) {
          List<VMInstanceVO> ssvm = instanceDao.listByZoneIdAndType(zoneId, VirtualMachine.Type.SecondaryStorageVm);
          return (CollectionUtils.isEmpty(ssvm)) ? null : ssvm.get(0);
      }
  
      /**
       * Iterate through all Image stores in the current running zone and select any that has less than DiskQuotaPercentageThreshold.value() disk usage
       *
       * @param zoneId of the current running zone
       * @return a valid secondary storage with less than DiskQuotaPercentageThreshold set by global config
       */
      private DataStore getImageStore(Long zoneId) {
          List<DataStore> stores = storeMgr.getImageStoresByScopeExcludingReadOnly(new ZoneScope(zoneId));
          if (CollectionUtils.isEmpty(stores)) {
              throw new CloudRuntimeException(String.format("No Secondary storage found in Zone: %s", dataCenterDao.findById(zoneId)));
          }
          DataStore imageStore = null;
          for (DataStore store : stores) {
              // Return image store if used percentage is less then threshold value set by global config diagnostics.data.disable.threshold
              if (statsCollector.imageStoreHasEnoughCapacity(store, DiskQuotaPercentageThreshold.value())) {
                  imageStore = store;
                  break;
              }
          }
          if (imageStore == null) {
              throw new CloudRuntimeException(String.format("No suitable secondary storage found to retrieve diagnostics in Zone: %s", dataCenterDao.findById(zoneId)));
          }
          return imageStore;
      }
  
      // createEntityExtractUrl throws CloudRuntime exception in case of failure
      private String createFileDownloadUrl(DataStore store, Hypervisor.HypervisorType hypervisorType, String filePath) {
          // Get image store driver
          ImageStoreEntity secStore = (ImageStoreEntity) store;
  
          //Create dummy TO with hyperType
          DataTO dataTO = new DiagnosticsDataTO(hypervisorType, store.getTO());
          DataObject dataObject = new DiagnosticsDataObject(dataTO, store);
          return secStore.createEntityExtractUrl(filePath, Storage.ImageFormat.ZIP, dataObject);
      }
  
      private VMInstanceVO getSystemVMInstance(Long vmId) {
          VMInstanceVO vmInstance = instanceDao.findByIdTypes(vmId, VirtualMachine.Type.ConsoleProxy,
                  VirtualMachine.Type.DomainRouter, VirtualMachine.Type.SecondaryStorageVm);
          if (vmInstance == null) {
              String msg = String.format("Unable to find vm instance with id: %s", vmId);
              logger.error(msg);
              throw new CloudRuntimeException("Diagnostics command execution failed, " + msg);
          }
  
          final Long hostId = vmInstance.getHostId();
          if (hostId == null) {
              throw new CloudRuntimeException("Unable to find host for virtual machine instance: " + vmInstance.getInstanceName());
          }
          return vmInstance;
      }
  
      private String getVMSshIp(final VMInstanceVO vmInstance) {
          Map<String, String> accessDetails = networkManager.getSystemVMAccessDetails(vmInstance);
          String controlIP = accessDetails.get(NetworkElementCommand.ROUTER_IP);
          if (StringUtils.isBlank(controlIP)) {
              throw new CloudRuntimeException(String.format("Unable to find system vm ssh/control IP for vm: %s", vmInstance));
          }
          return controlIP;
      }
  
      @Override
      public boolean start() {
          super.start();
          return true;
      }
  
      @Override
      public boolean configure(final String name, final Map<String, Object> params) throws ConfigurationException {
          if (EnableGarbageCollector.value()) {
              backgroundPollManager.submitTask(new GCBackgroundTask(this));
          }
          return true;
      }
  
      public static final class GCBackgroundTask extends ManagedContextRunnable implements BackgroundPollTask {
          private DiagnosticsServiceImpl serviceImpl;
  
          public GCBackgroundTask(DiagnosticsServiceImpl serviceImpl) {
              this.serviceImpl = serviceImpl;
          }
  
          private void deleteOldDiagnosticsFiles(File directory, String storeName) {
              final File[] fileList = directory.listFiles();
              if (fileList != null) {
                  String msg = String.format("Found %s diagnostics files in store %s for garbage collection", fileList.length, storeName);
                  logger.info(msg);
                  for (File file : fileList) {
                      if (file.isFile() && MaximumFileAgeforGarbageCollection.value() <= getTimeDifference(file)) {
                          boolean success = file.delete();
                          logger.info(file.getName() + " delete status: " + success);
                      }
                  }
              }
          }
  
          @Override
          protected void runInContext() {
              List<DataCenterVO> dcList = serviceImpl.dataCenterDao.listEnabledZones();
              for (DataCenterVO vo: dcList) {
                  // Get All Image Stores in current running Zone
                  List<DataStore> storeList = serviceImpl.storeMgr.getImageStoresByScope(new ZoneScope(vo.getId()));
                  for (DataStore store : storeList) {
                      cleanupOldDiagnosticFiles(store);
                  }
              }
          }
  
          @Override
          public Long getDelay() {
              // In Milliseconds
              return GarbageCollectionInterval.value() * 1000L;
          }
  
          private void cleanupOldDiagnosticFiles(DataStore store) {
              String mountPoint = null;
              mountPoint = serviceImpl.mountManager.getMountPoint(store.getUri(),
                      serviceImpl.imageStoreDetailsUtil.getNfsVersion(store.getId()));
              if (StringUtils.isNotBlank(mountPoint)) {
                  File directory = new File(mountPoint + File.separator + DIAGNOSTICS_DIRECTORY);
                  if (directory.isDirectory()) {
                      deleteOldDiagnosticsFiles(directory, store.getName());
                  }
              }
          }
      }
  
      @Override
      public List<Class<?>> getCommands() {
          List<Class<?>> cmdList = new ArrayList<>();
          cmdList.add(RunDiagnosticsCmd.class);
          cmdList.add(GetDiagnosticsDataCmd.class);
          return cmdList;
      }
  
      @Override
      public String getConfigComponentName() {
          return DiagnosticsServiceImpl.class.getSimpleName();
      }
  
      @Override
      public ConfigKey<?>[] getConfigKeys() {
          return new ConfigKey<?>[]{
                  EnableGarbageCollector,
                  DataRetrievalTimeout,
                  MaximumFileAgeforGarbageCollection,
                  GarbageCollectionInterval,
                  DiskQuotaPercentageThreshold,
                  SystemVMDefaultSupportedFiles,
                  RouterDefaultSupportedFiles
          };
      }
  }
  