diff --git a/pom.xml b/pom.xml index c455b7a8d519792b5f1c14e356a390678efd4ce5..ef7a01f3962142dcea4f5cb326a2efd9b15a3cc4 100755 --- a/pom.xml +++ b/pom.xml @@ -156,9 +156,20 @@ ${lombok.version} - - - + + org.apache.httpcomponents + httpclient + 4.5 + + + org.postgresql + postgresql + runtime + + + org.flywaydb + flyway-core + diff --git a/src/main/java/org/edgegallery/mecm/appo/AppOrchestratorApplication.java b/src/main/java/org/edgegallery/mecm/appo/AppOrchestratorApplication.java index 5dce29e2f73dfbdc95a0f1c71843fa141cbcd60f..96af184fb97ad642a25e6c87bb111c2aa9cb8efb 100755 --- a/src/main/java/org/edgegallery/mecm/appo/AppOrchestratorApplication.java +++ b/src/main/java/org/edgegallery/mecm/appo/AppOrchestratorApplication.java @@ -16,6 +16,14 @@ package org.edgegallery.mecm.appo; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.X509Certificate; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.SpringApplication; @@ -37,9 +45,36 @@ public class AppOrchestratorApplication { * @param args arguments */ public static void main(String[] args) { - // TODO: Token & https based support. + logger.info("Edge application orchestrator starting----"); - SpringApplication.run(AppOrchestratorApplication.class, args); + + // do not check host name + TrustManager[] trustAllCerts = new TrustManager[]{ + new X509TrustManager() { + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + + public void checkClientTrusted(X509Certificate[] certs, String authType) { + logger.info("checks client trusted"); + } + + public void checkServerTrusted(X509Certificate[] certs, String authType) { + logger.info("checks server trusted"); + } + } + }; + SSLContext sc = null; + try { + sc = SSLContext.getInstance("TLSv1.2"); + sc.init(null, trustAllCerts, new java.security.SecureRandom()); + HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); + HttpsURLConnection.setDefaultHostnameVerifier(NoopHostnameVerifier.INSTANCE); + + SpringApplication.run(AppOrchestratorApplication.class, args); + } catch (KeyManagementException | NoSuchAlgorithmException e) { + logger.info("SSL context init error... exiting system {}", e.getMessage()); + } } } diff --git a/src/main/java/org/edgegallery/mecm/appo/apihandler/AppOrchestratorHandler.java b/src/main/java/org/edgegallery/mecm/appo/apihandler/AppOrchestratorHandler.java index 11d2ec14d75707fa85b270f28692897acf60f97f..9df3fe60aec99780aacdd16e9b734d9e14d108e2 100644 --- a/src/main/java/org/edgegallery/mecm/appo/apihandler/AppOrchestratorHandler.java +++ b/src/main/java/org/edgegallery/mecm/appo/apihandler/AppOrchestratorHandler.java @@ -72,14 +72,14 @@ public class AppOrchestratorHandler { @ApiOperation(value = "Creates application instance", response = Map.class) @PostMapping(path = "/tenants/{tenant_id}/app_instances", produces = MediaType.APPLICATION_JSON_VALUE) @ApiResponses(value = {@ApiResponse(code = 201, message = "request accepted ", response = String.class), - @ApiResponse(code = 500, message = "internal server error", response = String.class) + @ApiResponse(code = 500, message = "internal server error", response = String.class) }) public ResponseEntity> createAppInstance( - @ApiParam(value = "access token") @RequestHeader("access_token") String accessToken, - @ApiParam(value = "tenant id") @PathVariable("tenant_id") - @Pattern(regexp = TENENT_ID_REGEX) String tenantId, - @ApiParam(value = "create application instance") - @Valid @RequestBody CreateParam createParam) { + @ApiParam(value = "access token") @RequestHeader("access_token") String accessToken, + @ApiParam(value = "tenant id") @PathVariable("tenant_id") + @Pattern(regexp = TENENT_ID_REGEX) String tenantId, + @ApiParam(value = "create application instance") + @Valid @RequestBody CreateParam createParam) { logger.debug("Application create request received..."); return appoService.createAppInstance(accessToken, tenantId, createParam); @@ -95,7 +95,7 @@ public class AppOrchestratorHandler { @ApiOperation(value = "Instantiate application instance", response = String.class) @PostMapping(path = "/tenants/{tenant_id}/app_instances/{app_instance_id}", produces = MediaType.TEXT_PLAIN_VALUE) @ApiResponses(value = {@ApiResponse(code = 201, message = "request accepted ", response = String.class), - @ApiResponse(code = 500, message = "internal server error", response = String.class) + @ApiResponse(code = 500, message = "internal server error", response = String.class) }) public ResponseEntity instantiateAppInstance( @ApiParam(value = "access token") @RequestHeader("access_token") String accessToken, @@ -157,7 +157,7 @@ public class AppOrchestratorHandler { @DeleteMapping(path = "/tenants/{tenant_id}/app_instances/{app_instance_id}", produces = MediaType.APPLICATION_JSON_VALUE) @ApiResponses(value = {@ApiResponse(code = 201, message = "request accepted ", response = String.class), - @ApiResponse(code = 500, message = "internal server error", response = String.class) + @ApiResponse(code = 500, message = "internal server error", response = String.class) }) public ResponseEntity terminateAppInstance( @ApiParam(value = "access token") @RequestHeader("access_token") String accessToken, diff --git a/src/main/java/org/edgegallery/mecm/appo/bpmn/tasks/ProcessflowAbstractTask.java b/src/main/java/org/edgegallery/mecm/appo/bpmn/tasks/ProcessflowAbstractTask.java new file mode 100644 index 0000000000000000000000000000000000000000..3c94bbaba9dfcaf67d2b636c6907a99115825273 --- /dev/null +++ b/src/main/java/org/edgegallery/mecm/appo/bpmn/tasks/ProcessflowAbstractTask.java @@ -0,0 +1,85 @@ +package org.edgegallery.mecm.appo.bpmn.tasks; + +import java.util.Map; +import javax.ws.rs.core.UriBuilder; +import org.camunda.bpm.engine.delegate.DelegateExecution; + +public abstract class ProcessflowAbstractTask { + + public static final String RESPONSE = "Response"; + public static final String RESPONSE_CODE = "ResponseCode"; + public static final String ERROR_RESPONSE = "ErrResponse"; + public static final String FLOW_EXCEPTION = "ProcessflowException"; + + /** + * Retrieves protocol. + * + * @return protocol + */ + public String getProtocol(String isSslEnabled) { + if (isSslEnabled.equals("true")) { + return "https://"; + } + return "http://"; + } + + /** + * Replaces path variables in URL. + * + * @param urlString url string + * @param parameters parameters to replace + * @return url string with replaced parameters + */ + public String replaceParamsInUrl(String urlString, Map parameters) { + UriBuilder builder = UriBuilder.fromPath(urlString); + return builder.buildFromMap(parameters).toString(); + } + + /** + * Sets process flow response attributes to delegate execution. + * + * @param delegateExecution delegate execution + * @param response response + * @param responseCode response code + */ + public void setProcessflowResponseAttributes(DelegateExecution delegateExecution, + String response, String responseCode) { + if (responseCode == null) { + throw new IllegalArgumentException(); + } + delegateExecution.setVariable(RESPONSE, response); + delegateExecution.setVariable(RESPONSE_CODE, responseCode); + } + + /** + * Sets process flow error response attributes to delegate execution. + * + * @param delegateExecution delegate execution + * @param response response + * @param responseCode response code + */ + public void setProcessflowErrorResponseAttributes(DelegateExecution delegateExecution, + String response, String responseCode) { + if (responseCode == null) { + throw new IllegalArgumentException(); + } + delegateExecution.setVariable(ERROR_RESPONSE, response); + delegateExecution.setVariable(RESPONSE_CODE, responseCode); + } + + /** + * Sets process flow exception response attributes to delegate execution. + * + * @param delegateExecution delegate execution + * @param response response + * @param responseCode response code + */ + public void setProcessflowExceptionResponseAttributes(DelegateExecution delegateExecution, + String response, String responseCode) { + if (responseCode == null) { + throw new IllegalArgumentException(); + } + delegateExecution.setVariable(RESPONSE_CODE, responseCode); + delegateExecution.setVariable(FLOW_EXCEPTION, response); + } +} diff --git a/src/main/java/org/edgegallery/mecm/appo/common/Constants.java b/src/main/java/org/edgegallery/mecm/appo/common/Constants.java index b56d1d3a30a8468aa98195143a31ba81a1a1c67e..4ffbee8c1df8c00906025d37eb1a48ba2854280c 100644 --- a/src/main/java/org/edgegallery/mecm/appo/common/Constants.java +++ b/src/main/java/org/edgegallery/mecm/appo/common/Constants.java @@ -1,9 +1,26 @@ +/* + * Copyright 2020 Huawei Technologies Co., Ltd. + * + * Licensed 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.edgegallery.mecm.appo.common; public final class Constants { public static final String HOST_IP_REGX = "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.)" + "{3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$"; + public static final String ID_REGX = "^[a-zA-Z0-9]$|^[a-zA-Z0-9][a-zA-Z0-9\\-][a-zA-Z0-9]$"; public static final String APP_INST_ID_REGX = "([a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}){1}"; public static final String APPD_ID_REGEX = "[0-9a-f]{32}"; @@ -12,6 +29,32 @@ public final class Constants { public static final String APP_NAME_REGEX = "^[a-zA-Z0-9]*$|^[a-zA-Z0-9][a-zA-Z0-9_\\-]*[a-zA-Z0-9]$"; public static final String RECORD_NOT_FOUND = "Record not found"; public static final String EMPTY_STRING = ""; + public static final String APP_INSTANCE_NOT_FOUND = "App instance not found"; + public static final String OPER_STATUS_INVALID_STATE = "Invalid state"; + + public static final String REQUEST_ID = "request_id"; + public static final String TENANT_ID = "tenant_id"; + public static final String APP_PACKAGE_ID = "app_package_id"; + public static final String APP_NAME = "app_name"; + public static final String APP_DESCR = "app_instance_description"; + public static final String MEC_HOST = "mec_host"; + public static final String APP_INSTANCE_ID = "app_instance_id"; + public static final String APPD_ID = "appd_id"; + public static final String ACCESS_TOKEN = "access_token"; + public static final String MEC_HOST_IP = "host_ip"; + public static final String APPLCM_IP = "applcm_ip"; + public static final String APPLCM_PORT = "applcm_port"; + public static final String APP_INSTANCE_INFO = "app_instance_info"; + + public static final String APM_DOWNLOAD_URI = "/apm/v1/tenants/{tenant_id}/packages/{package_id}/download"; + public static final String INVENTORY_MEC_HOST_URI = "/inventory/v1/tenants/{tenant_id}/mechosts/{mecHost}"; + public static final String INVENTORY_APPLCM_URI = "/inventory/v1/tenants/{tenant_id}/applcms/{applcmIp}"; + + public static final String APPLCM_INSTANTIATE_URI = "/lcmbroker/v1/app_instances/{appInstanceId}"; + public static final String APPLCM_QUERY_URI = "/lcmbroker/v1/app_instances/{appInstanceId}"; + public static final String APPLCM_TERMINATE_URI = "/lcmbroker/v1/app_instances/{appInstanceId}"; + public static final String APPLCM_QUERY_KPI_URI = "/lcmbroker/v1/kpi"; + public static final String APPLCM_QUERY_CAPABILITY_URI = "/lcmbroker/v1/mep_capabilities"; private Constants() { } diff --git a/src/main/java/org/edgegallery/mecm/appo/service/AppInstanceInfoServiceImpl.java b/src/main/java/org/edgegallery/mecm/appo/service/AppInstanceInfoServiceImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..7c938451769ed7869965d4e5dc9e023f4bc5248c --- /dev/null +++ b/src/main/java/org/edgegallery/mecm/appo/service/AppInstanceInfoServiceImpl.java @@ -0,0 +1,93 @@ +package org.edgegallery.mecm.appo.service; + +import java.util.LinkedList; +import java.util.List; +import java.util.Optional; +import javax.transaction.Transactional; +import org.edgegallery.mecm.appo.exception.AppoException; +import org.edgegallery.mecm.appo.model.AppInstanceInfo; +import org.edgegallery.mecm.appo.repository.AppInstanceInfoRepository; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class AppInstanceInfoServiceImpl implements AppInstanceInfoService { + + public static final Logger logger = LoggerFactory.getLogger(AppInstanceInfoServiceImpl.class); + private static final String RECORD_NOT_FOUND = "Record does not exist with app instance id "; + private AppInstanceInfoRepository appInstanceInfoRepository; + + @Autowired + public AppInstanceInfoServiceImpl(AppInstanceInfoRepository appInstanceInfoRepository) { + this.appInstanceInfoRepository = appInstanceInfoRepository; + } + + @Override + public AppInstanceInfo getAppInstanceInfo(String tenantId, String appInstanceId) { + logger.debug("Get application instance {}... from DB", appInstanceId); + + Optional info = appInstanceInfoRepository.findById(appInstanceId); + if (!info.isPresent() || !info.get().getAppInstanceId().equals(appInstanceId) + || !info.get().getTenant().equals(tenantId)) { + throw new AppoException(RECORD_NOT_FOUND + appInstanceId); + } + return info.get(); + } + + @Override + public List getAllAppInstanceInfo(String tenantId) { + logger.debug("Get all application instance info of tenant {} ... from DB", tenantId); + + List tenantAppInstanceInfos = new LinkedList<>(); + appInstanceInfoRepository.findAll().forEach((AppInstanceInfo appInstanceInfo) -> { + if (appInstanceInfo.getTenant().equals(tenantId)) { + tenantAppInstanceInfos.add(appInstanceInfo); + } + }); + + return tenantAppInstanceInfos; + } + + @Override + public AppInstanceInfo createAppInstanceInfo(String tenantId, + AppInstanceInfo appInstanceInfo) { + logger.debug("Add application instance {}...to DB", appInstanceInfo.getAppInstanceId()); + + appInstanceInfo.setTenant(tenantId); + return appInstanceInfoRepository.save(appInstanceInfo); + } + + @Override + public void deleteAppInstanceInfo(String tenantId, String appInstanceId) { + logger.debug("Delete application instance {}... from DB", appInstanceId); + + Optional info = appInstanceInfoRepository.findById(appInstanceId); + if (!info.isPresent() || !info.get().getAppInstanceId().equals(appInstanceId) + || !info.get().getTenant().equals(tenantId)) { + throw new AppoException(RECORD_NOT_FOUND + appInstanceId); + } + appInstanceInfoRepository.deleteById(appInstanceId); + } + + @Override + @Transactional + public AppInstanceInfo updateAppInstanceInfo(String tenantId, AppInstanceInfo appInstanceInfo) { + logger.debug("Update application instance {}... to DB", appInstanceInfo.getAppInstanceId()); + appInstanceInfo.setTenant(tenantId); + + Optional info = appInstanceInfoRepository.findById(appInstanceInfo.getAppInstanceId()); + if (!info.isPresent() || !info.get().getAppInstanceId().equals(appInstanceInfo.getAppInstanceId()) + || !info.get().getTenant().equals(tenantId)) { + throw new AppoException(RECORD_NOT_FOUND + appInstanceInfo.getAppInstanceId()); + } + AppInstanceInfo dbAppInstanceInfo = info.get(); + + dbAppInstanceInfo.setOperationalStatus(appInstanceInfo.getOperationalStatus()); + dbAppInstanceInfo.setOperationInfo(appInstanceInfo.getOperationInfo()); + + dbAppInstanceInfo.setCreateTime(info.get().getCreateTime()); + return appInstanceInfoRepository.save(dbAppInstanceInfo); + } +} diff --git a/src/main/java/org/edgegallery/mecm/appo/service/AppoProcessEngineService.java b/src/main/java/org/edgegallery/mecm/appo/service/AppoProcessEngineService.java new file mode 100644 index 0000000000000000000000000000000000000000..7b4898e94f249e2284f2b7b0fc77f978752b5f87 --- /dev/null +++ b/src/main/java/org/edgegallery/mecm/appo/service/AppoProcessEngineService.java @@ -0,0 +1,48 @@ +/* + * Copyright 2020 Huawei Technologies Co., Ltd. + * + * Licensed 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.edgegallery.mecm.appo.service; + +import org.camunda.bpm.engine.ProcessEngineServices; +import org.camunda.bpm.engine.ProcessEngines; +import org.springframework.stereotype.Service; + +/** + * service class must be aware of process engines. currently "default" process engine supported. + */ +@Service +public class AppoProcessEngineService { + + private static final String PROCESS_ENGINE_NAME = "default"; + + /** + * Retrieves process engine name. + * + * @return the process engine name + */ + public String getEngineName() { + return PROCESS_ENGINE_NAME; + } + + /** + * Retrieves process engine services. + * + * @return process engine services + */ + public ProcessEngineServices getEngineServices() { + return ProcessEngines.getProcessEngine(getEngineName()); + } +} \ No newline at end of file diff --git a/src/main/java/org/edgegallery/mecm/appo/service/AppoProcessflowService.java b/src/main/java/org/edgegallery/mecm/appo/service/AppoProcessflowService.java index 622fe3f9a63bdfe1d852d20d2026488bbf921b7b..d35b96fe9d15a98fa398e403088b975c23e98f67 100644 --- a/src/main/java/org/edgegallery/mecm/appo/service/AppoProcessflowService.java +++ b/src/main/java/org/edgegallery/mecm/appo/service/AppoProcessflowService.java @@ -35,7 +35,7 @@ public interface AppoProcessflowService { * * @param processKey process key * @param requestInput input parameters - * @return workflow response + * @return processflow response on success */ AppoProcessFlowResponse executeProcessSync(String processKey, Map requestInput); } \ No newline at end of file diff --git a/src/main/java/org/edgegallery/mecm/appo/service/AppoService.java b/src/main/java/org/edgegallery/mecm/appo/service/AppoService.java index a8352d9f1aec6819eb873eec913b145dfd438d97..fcca6df6316b7d7b560ce0231e42d8826d6c6268 100644 --- a/src/main/java/org/edgegallery/mecm/appo/service/AppoService.java +++ b/src/main/java/org/edgegallery/mecm/appo/service/AppoService.java @@ -18,9 +18,8 @@ public interface AppoService { * @return application instance ID on success, error code on failure */ - ResponseEntity> createAppInstance(String accessToken, - String tenantId, - CreateParam createParam); + ResponseEntity> createAppInstance(String accessToken, String tenantId, + CreateParam createParam); /** * Instantiates an application instance. @@ -31,9 +30,7 @@ public interface AppoService { * @return status code 200 on success, error code on failure */ - ResponseEntity instantiateAppInstance(String accessToken, - String tenantId, - String appInstanceId); + ResponseEntity instantiateAppInstance(String accessToken, String tenantId, String appInstanceId); /** * Retrieves an application instance information. @@ -44,9 +41,7 @@ public interface AppoService { * @return application instance info & status code 200 on success, error code on failure */ - ResponseEntity getAppInstance(String accessToken, - String tenantId, - String appInstanceId); + ResponseEntity getAppInstance(String accessToken, String tenantId, String appInstanceId); /** * Retrieves all application instance information. @@ -56,8 +51,7 @@ public interface AppoService { * @return all application instances & status code 200 on success, error code on failure */ - ResponseEntity> getAllAppInstance(String accessToken, - String tenantId); + ResponseEntity> getAllAppInstance(String accessToken, String tenantId); /** * Terminates an application instance. @@ -68,9 +62,7 @@ public interface AppoService { * @return status code 200 on success, error code on failure */ - ResponseEntity terminateAppInstance(String accessToken, - String tenantId, - String appInstanceId); + ResponseEntity terminateAppInstance(String accessToken, String tenantId, String appInstanceId); /** * Retrieves edge host performance statistics. @@ -81,9 +73,7 @@ public interface AppoService { * @return status code 200 on success, error code on failure */ - ResponseEntity queryKpi(String accessToken, - String tenantId, - String hostIp); + ResponseEntity queryKpi(String accessToken, String tenantId, String hostIp); /** * Retrieves edge host platform capabilities. @@ -94,7 +84,5 @@ public interface AppoService { * @return status code 200 on success, error code on failure */ - ResponseEntity queryEdgehostCapabilities(String accessToken, - String tenantId, - String hostIp); + ResponseEntity queryEdgehostCapabilities(String accessToken, String tenantId, String hostIp); } diff --git a/src/main/resources/META-INF/processes.xml b/src/main/resources/META-INF/processes.xml new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 880fff630582b966ae9f7d0e1bfcc0bbafb0ddc8..dad61253feec7bea281f8ff10b8c1afb3e5ec358 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -22,13 +22,32 @@ server: tomcat: max-threads: 200 + ssl: + enabled: ${SSL_ENABLED:false} + protocol: TLS + enabled-Protocols: [TLSv1.2] + # Keystore + key-store: ${SSL_KEY_STORE_PATH:} + key-store-password: ${SSL_KEY_STORE_PASSWORD:} + key-store-type: ${SSL_KEY_STORE_TYPE:} + key-alias: ${SSL_KEY_ALIAS:} spring: http: multipart: max-file-size: 10MB max-request-size: 10MB - + datasource: + url: jdbc:postgresql://${APPO_DB_HOST:}:${APPO_DB_PORT:}/${APPO_DB:} + username: ${APPO_DB_USER:} + password: ${APPO_DB_PASSWORD:} + jpa: + show-sql: true + properties: + hibernate: + dialect: org.hibernate.dialect.PostgreSQLDialect + ddl-auto: validate + open-in-view: false #### security config #### security: oauth2: @@ -36,3 +55,4 @@ security: jwt: key-uri: ${AUTH_SERVER_ADDRESS:http://user-mgmt-svc:8067}/oauth/token_key + diff --git a/src/main/resources/db/migration/V1__Add_App_Instance_Info_Task_Table.sql b/src/main/resources/db/migration/V1__Add_App_Instance_Info_Task_Table.sql new file mode 100644 index 0000000000000000000000000000000000000000..8b69f4ba94bf7542d2dd2e9a9d740255f4f98d89 --- /dev/null +++ b/src/main/resources/db/migration/V1__Add_App_Instance_Info_Task_Table.sql @@ -0,0 +1,16 @@ + + create table appinstanceinfo ( + app_instance_id varchar(64) not null, + app_package_id varchar(64) not null, + appd_id varchar(64) not null, + tenant varchar(64) not null, + app_name varchar(128) not null, + app_descriptor varchar(256) not null, + mec_host varchar(15) not null, + applcm_host varchar(15), + operational_status varchar(128) not null, + operation_info varchar(256), + create_time timestamp default current_timestamp, + update_time timestamp default current_timestamp, + primary key (app_instance_id) + ) \ No newline at end of file