Merge "Add service role"
authorRob Daugherty <rd472p@att.com>
Tue, 27 Nov 2018 20:51:05 +0000 (20:51 +0000)
committerGerrit Code Review <gerrit@onap.org>
Tue, 27 Nov 2018 20:51:05 +0000 (20:51 +0000)
33 files changed:
adapters/mso-adapter-utils/src/main/java/org/onap/so/cloud/authentication/AuthenticationMethodFactory.java
adapters/mso-adapter-utils/src/main/java/org/onap/so/cloud/authentication/KeystoneAuthHolder.java [new file with mode: 0644]
adapters/mso-adapter-utils/src/main/java/org/onap/so/cloud/authentication/KeystoneV3Authentication.java [new file with mode: 0644]
adapters/mso-adapter-utils/src/main/java/org/onap/so/cloud/authentication/ServiceEndpointNotFoundException.java [new file with mode: 0644]
adapters/mso-adapter-utils/src/main/java/org/onap/so/openstack/utils/MsoHeatUtils.java
adapters/mso-adapter-utils/src/main/java/org/onap/so/openstack/utils/MsoKeystoneV3Utils.java [new file with mode: 0644]
adapters/mso-adapter-utils/src/main/java/org/onap/so/openstack/utils/MsoNeutronUtils.java
adapters/mso-adapter-utils/src/main/java/org/onap/so/openstack/utils/MsoTenantUtilsFactory.java
adapters/mso-adapter-utils/src/test/java/org/onap/so/cloud/authentication/AuthenticationMethodTest.java
adapters/mso-adapter-utils/src/test/resources/__files/KeystoneV3Payload.json [new file with mode: 0644]
adapters/mso-catalog-db-adapter/src/main/resources/db/migration/V4.12.2__AddDomainColumnsCloudIdentity.sql [new file with mode: 0644]
adapters/mso-catalog-db-adapter/src/test/resources/db/migration/afterMigrate.sql
adapters/mso-openstack-adapters/src/test/resources/data.sql
adapters/mso-openstack-adapters/src/test/resources/schema.sql
adapters/mso-requests-db-adapter/src/main/java/org/onap/so/adapters/requestsdb/MsoRequestsDbAdapterImpl.java
adapters/mso-requests-db-adapter/src/test/java/org/onap/so/adapters/requestsdb/adapters/MSORequestDBImplTest.java
bpmn/MSOCommonBPMN/src/main/groovy/org/onap/so/bpmn/common/scripts/CatalogDbUtils.groovy
bpmn/MSOCommonBPMN/src/main/java/org/onap/so/bpmn/common/resource/ResourceRequestBuilder.java
bpmn/so-bpmn-infrastructure-common/src/main/groovy/org/onap/so/bpmn/infrastructure/scripts/ActivateSDNCNetworkResource.groovy
bpmn/so-bpmn-infrastructure-common/src/main/groovy/org/onap/so/bpmn/infrastructure/scripts/Create3rdONAPE2EServiceInstance.groovy
bpmn/so-bpmn-infrastructure-common/src/main/groovy/org/onap/so/bpmn/infrastructure/scripts/CreateDeviceResource.groovy
bpmn/so-bpmn-infrastructure-common/src/main/groovy/org/onap/so/bpmn/infrastructure/scripts/CreateSDNCNetworkResource.groovy
bpmn/so-bpmn-infrastructure-common/src/main/groovy/org/onap/so/bpmn/infrastructure/scripts/DeActivateSDNCNetworkResource.groovy
bpmn/so-bpmn-infrastructure-common/src/main/groovy/org/onap/so/bpmn/infrastructure/scripts/Delete3rdONAPE2EServiceInstance.groovy
bpmn/so-bpmn-infrastructure-common/src/main/groovy/org/onap/so/bpmn/infrastructure/scripts/DeleteDeviceResource.groovy
bpmn/so-bpmn-infrastructure-common/src/main/groovy/org/onap/so/bpmn/infrastructure/scripts/DeleteSDNCNetworkResource.groovy
bpmn/so-bpmn-infrastructure-common/src/main/groovy/org/onap/so/bpmn/infrastructure/scripts/DoCreateE2EServiceInstance.groovy
bpmn/so-bpmn-infrastructure-common/src/main/groovy/org/onap/so/bpmn/infrastructure/scripts/DoCreateResources.groovy
mso-catalog-db/src/main/java/org/onap/so/db/catalog/beans/CloudIdentity.java
mso-catalog-db/src/main/java/org/onap/so/db/catalog/beans/ServerType.java
mso-catalog-db/src/test/resources/data.sql
mso-catalog-db/src/test/resources/schema.sql
packages/docker/src/main/docker/docker-files/scripts/start-app.sh

index 1912cd8..49c80b4 100644 (file)
 
 package org.onap.so.cloud.authentication;
 
+import java.util.Collections;
+
+import org.onap.so.cloud.authentication.models.RackspaceAuthentication;
 import org.onap.so.db.catalog.beans.AuthenticationType;
 import org.onap.so.db.catalog.beans.CloudIdentity;
-import org.onap.so.cloud.authentication.models.RackspaceAuthentication;
 import org.onap.so.utils.CryptoUtils;
 import org.springframework.stereotype.Component;
 
 import com.woorea.openstack.keystone.model.Authentication;
 import com.woorea.openstack.keystone.model.authentication.UsernamePassword;
+import com.woorea.openstack.keystone.v3.model.Authentication.Identity;
+import com.woorea.openstack.keystone.v3.model.Authentication.Identity.Password;
+import com.woorea.openstack.keystone.v3.model.Authentication.Identity.Password.User;
+import com.woorea.openstack.keystone.v3.model.Authentication.Identity.Password.User.Domain;
+import com.woorea.openstack.keystone.v3.model.Authentication.Scope;
+import com.woorea.openstack.keystone.v3.model.Authentication.Scope.Project;
 
 /**
  * This factory manages all the wrappers associated to authentication types.
@@ -50,4 +58,30 @@ public final class AuthenticationMethodFactory {
                        return new UsernamePassword (cloudIdentity.getMsoId (), CryptoUtils.decryptCloudConfigPassword(cloudIdentity.getMsoPass ()));
                }
        }
+       
+       
+       public final com.woorea.openstack.keystone.v3.model.Authentication getAuthenticationForV3(CloudIdentity cloudIdentity, String tenantId) {
+               Identity identity = new Identity();
+               Password password = new Password();
+               User user = new User();
+               Domain userDomain = new Domain();
+               Scope scope = new Scope();
+               Project project = new Project();
+               Project.Domain projectDomain = new Project.Domain();
+               userDomain.setName(cloudIdentity.getUserDomainName());
+               projectDomain.setName(cloudIdentity.getProjectDomainName());
+               user.setName(cloudIdentity.getMsoId());
+               user.setPassword(CryptoUtils.decryptCloudConfigPassword(cloudIdentity.getMsoPass()));
+               user.setDomain(userDomain);
+               password.setUser(user);
+               project.setDomain(projectDomain);
+               project.setId(tenantId);
+               scope.setProject(project);
+               identity.setPassword(password);
+               identity.setMethods(Collections.singletonList("password"));
+               com.woorea.openstack.keystone.v3.model.Authentication v3Auth = new com.woorea.openstack.keystone.v3.model.Authentication();
+               v3Auth.setIdentity(identity);
+               v3Auth.setScope(scope);
+               return v3Auth;
+       }
 }
diff --git a/adapters/mso-adapter-utils/src/main/java/org/onap/so/cloud/authentication/KeystoneAuthHolder.java b/adapters/mso-adapter-utils/src/main/java/org/onap/so/cloud/authentication/KeystoneAuthHolder.java
new file mode 100644 (file)
index 0000000..1a221a8
--- /dev/null
@@ -0,0 +1,32 @@
+package org.onap.so.cloud.authentication;
+
+import java.io.Serializable;
+import java.util.Calendar;
+
+public class KeystoneAuthHolder implements Serializable {
+
+       private static final long serialVersionUID = -9073252905181739224L;
+       
+       private String id;
+       private Calendar expiration;
+       private String serviceUrl;
+       
+       public String getId() {
+               return id;
+       }
+       public void setId(String id) {
+               this.id = id;
+       }
+       public Calendar getexpiration() {
+               return expiration;
+       }
+       public void setexpiration(Calendar expiration) {
+               this.expiration = expiration;
+       }
+       public String getServiceUrl() {
+               return serviceUrl;
+       }
+       public void setHeatUrl(String serviceUrl) {
+               this.serviceUrl = serviceUrl;
+       }
+}
diff --git a/adapters/mso-adapter-utils/src/main/java/org/onap/so/cloud/authentication/KeystoneV3Authentication.java b/adapters/mso-adapter-utils/src/main/java/org/onap/so/cloud/authentication/KeystoneV3Authentication.java
new file mode 100644 (file)
index 0000000..05364d0
--- /dev/null
@@ -0,0 +1,113 @@
+package org.onap.so.cloud.authentication;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Predicate;
+
+import org.onap.so.config.beans.PoConfig;
+import org.onap.so.db.catalog.beans.CloudIdentity;
+import org.onap.so.db.catalog.beans.CloudSite;
+import org.onap.so.openstack.exceptions.MsoException;
+import org.onap.so.openstack.utils.MsoTenantUtils;
+import org.onap.so.openstack.utils.MsoTenantUtilsFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import com.woorea.openstack.base.client.OpenStackConnectException;
+import com.woorea.openstack.base.client.OpenStackRequest;
+import com.woorea.openstack.base.client.OpenStackResponse;
+import com.woorea.openstack.base.client.OpenStackResponseException;
+import com.woorea.openstack.keystone.v3.Keystone;
+import com.woorea.openstack.keystone.v3.model.Authentication;
+import com.woorea.openstack.keystone.v3.model.Token;
+import com.woorea.openstack.keystone.v3.model.Token.Service;
+
+import net.jodah.failsafe.Failsafe;
+import net.jodah.failsafe.RetryPolicy;
+
+
+@Component
+public class KeystoneV3Authentication {
+
+       @Autowired
+    private AuthenticationMethodFactory authenticationMethodFactory;
+    
+    @Autowired
+    private MsoTenantUtilsFactory tenantUtilsFactory;
+    
+    @Autowired
+       private PoConfig poConfig;
+       
+       public KeystoneAuthHolder getToken(CloudSite cloudSite, String tenantId, String type) throws MsoException {
+               
+               String cloudId = cloudSite.getId();
+        String region = cloudSite.getRegionId();
+
+               CloudIdentity cloudIdentity = cloudSite.getIdentityService();
+               MsoTenantUtils tenantUtils = tenantUtilsFactory.getTenantUtilsByServerType(cloudIdentity.getIdentityServerType());
+        String keystoneUrl = tenantUtils.getKeystoneUrl(cloudId, cloudIdentity);
+        Keystone keystoneTenantClient = new Keystone (keystoneUrl);
+        Authentication v3Credentials = authenticationMethodFactory.getAuthenticationForV3(cloudIdentity, tenantId);
+
+
+       OpenStackRequest<Token> v3Request = keystoneTenantClient.tokens ()
+                .authenticate(v3Credentials);
+       
+       KeystoneAuthHolder holder = makeRequest(v3Request, type, region);
+
+               return holder;
+       }
+       
+       protected KeystoneAuthHolder makeRequest(OpenStackRequest<Token> v3Request, String type, String region) {
+               
+               OpenStackResponse response = Failsafe.with(createRetryPolicy()).get(() -> {
+                       return v3Request.request();
+               });
+               String id = response.header("X-Subject-Token");
+               Token token = response.getEntity(Token.class);
+               KeystoneAuthHolder result = new KeystoneAuthHolder();
+               result.setId(id);
+               result.setexpiration(token.getExpiresAt());
+               result.setHeatUrl(findEndpointURL(token.getCatalog(), type, region, "public"));
+               return result;
+       }
+       
+       protected RetryPolicy createRetryPolicy() {
+               RetryPolicy policy = new RetryPolicy();
+               List<Predicate<Throwable>> result = new ArrayList<>();
+               result.add(e -> {
+                       return e.getCause() instanceof OpenStackResponseException 
+                                       && Arrays.asList(poConfig.getRetryCodes().split(","))
+                                       .contains(Integer.toString(((OpenStackResponseException)e).getStatus()));
+               });
+               result.add(e -> {
+                       return e.getCause() instanceof OpenStackConnectException;
+               });
+               
+               Predicate<Throwable> pred = result.stream().reduce(Predicate::or).orElse(x -> false);
+
+               policy.retryOn(error -> pred.test(error));
+               
+               policy.withDelay(poConfig.getRetryDelay(), TimeUnit.SECONDS)
+               .withMaxRetries(poConfig.getRetryCount());
+               
+               return policy;
+       }
+       
+       protected String findEndpointURL(List<Service> serviceCatalog, String type, String region, String facing) {
+               for(Service service : serviceCatalog) {
+                       if(type.equals(service.getType())) {
+                               for(Service.Endpoint endpoint : service.getEndpoints()) {
+                                       if(region == null || region.equals(endpoint.getRegion())) {
+                                               if(facing.equals(endpoint.getInterface())) {
+                                                       return endpoint.getUrl();
+                                               }
+                                       }
+                               }
+                       }
+               }
+               throw new ServiceEndpointNotFoundException("endpoint url not found");
+       }
+}
diff --git a/adapters/mso-adapter-utils/src/main/java/org/onap/so/cloud/authentication/ServiceEndpointNotFoundException.java b/adapters/mso-adapter-utils/src/main/java/org/onap/so/cloud/authentication/ServiceEndpointNotFoundException.java
new file mode 100644 (file)
index 0000000..3bc57a8
--- /dev/null
@@ -0,0 +1,10 @@
+package org.onap.so.cloud.authentication;
+
+public class ServiceEndpointNotFoundException extends RuntimeException {
+
+       private static final long serialVersionUID = -5347215451284361397L;
+
+       public ServiceEndpointNotFoundException(String message) {
+               super(message);
+       }
+}
index e36d0ff..d87330b 100644 (file)
@@ -24,6 +24,7 @@ package org.onap.so.openstack.utils;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Calendar;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -41,11 +42,15 @@ import org.onap.so.adapters.vdu.VduPlugin;
 import org.onap.so.adapters.vdu.VduStateType;
 import org.onap.so.adapters.vdu.VduStatus;
 import org.onap.so.cloud.CloudConfig;
+import org.onap.so.cloud.authentication.AuthenticationMethodFactory;
+import org.onap.so.cloud.authentication.KeystoneAuthHolder;
+import org.onap.so.cloud.authentication.KeystoneV3Authentication;
+import org.onap.so.cloud.authentication.ServiceEndpointNotFoundException;
 import org.onap.so.db.catalog.beans.CloudIdentity;
 import org.onap.so.db.catalog.beans.CloudSite;
-import org.onap.so.cloud.authentication.AuthenticationMethodFactory;
 import org.onap.so.db.catalog.beans.HeatTemplate;
 import org.onap.so.db.catalog.beans.HeatTemplateParam;
+import org.onap.so.db.catalog.beans.ServerType;
 import org.onap.so.logger.MessageEnum;
 import org.onap.so.logger.MsoAlarmLogger;
 import org.onap.so.logger.MsoLogger;
@@ -115,7 +120,10 @@ public class MsoHeatUtils extends MsoCommonUtils implements VduPlugin{
 
     @Autowired
     private MsoTenantUtilsFactory tenantUtilsFactory;
-
+    
+    @Autowired
+    private KeystoneV3Authentication keystoneV3Authentication;
+    
     private static final MsoLogger LOGGER = MsoLogger.getMsoLogger (MsoLogger.Catalog.RA, MsoHeatUtils.class);
 
     // Properties names and variables (with default values)
@@ -875,7 +883,9 @@ public class MsoHeatUtils extends MsoCommonUtils implements VduPlugin{
      */
     public Heat getHeatClient (CloudSite cloudSite, String tenantId) throws MsoException {
         String cloudId = cloudSite.getId();
-
+        // For DCP/LCP, the region should be the cloudId.
+        String region = cloudSite.getRegionId ();
+        
         // Check first in the cache of previously authorized clients
         String cacheKey = cloudId + ":" + tenantId;
         if (heatClientCache.containsKey (cacheKey)) {
@@ -895,16 +905,58 @@ public class MsoHeatUtils extends MsoCommonUtils implements VduPlugin{
         MsoTenantUtils tenantUtils = tenantUtilsFactory.getTenantUtilsByServerType(cloudIdentity.getIdentityServerType());
         String keystoneUrl = tenantUtils.getKeystoneUrl(cloudId, cloudIdentity);
         LOGGER.debug("keystoneUrl=" + keystoneUrl);
-        Keystone keystoneTenantClient = new Keystone (keystoneUrl);
-        Access access = null;
-        try {
-               Authentication credentials = authenticationMethodFactory.getAuthenticationFor(cloudIdentity);
-
-               OpenStackRequest <Access> request = keystoneTenantClient.tokens ()
-                       .authenticate (credentials).withTenantId (tenantId);
-
-            access = executeAndRecordOpenstackRequest (request);
-        } catch (OpenStackResponseException e) {
+        String heatUrl = null;
+        String tokenId = null;
+        Calendar expiration = null;
+           try {
+               if (ServerType.KEYSTONE.equals(cloudIdentity.getIdentityServerType())) {
+                       Keystone keystoneTenantClient = new Keystone (keystoneUrl);
+                       Access access = null;
+               
+                       Authentication credentials = authenticationMethodFactory.getAuthenticationFor(cloudIdentity);
+       
+                       OpenStackRequest <Access> request = keystoneTenantClient.tokens ()
+                              .authenticate (credentials).withTenantId (tenantId);
+       
+                   access = executeAndRecordOpenstackRequest (request);
+               
+                       try {
+                               // Isolate trying to printout the region IDs
+                               try {
+                                       LOGGER.debug("access=" + access.toString());
+                                       for (Access.Service service : access.getServiceCatalog()) {
+                                               List<Access.Service.Endpoint> endpoints = service.getEndpoints();
+                                               for (Access.Service.Endpoint endpoint : endpoints) {
+                                                       LOGGER.debug("AIC returned region=" + endpoint.getRegion());
+                                               }
+                                       }
+                               } catch (Exception e) {
+                                       LOGGER.debug("Encountered an error trying to printout Access object returned from AIC. " + e.getMessage());
+                               }
+                           heatUrl = KeystoneUtils.findEndpointURL (access.getServiceCatalog (), "orchestration", region, "public");
+                           LOGGER.debug("heatUrl=" + heatUrl + ", region=" + region);
+                       } catch (RuntimeException e) {
+                           // This comes back for not found (probably an incorrect region ID)
+                           String error = "AIC did not match an orchestration service for: region=" + region + ",cloud=" + cloudIdentity.getIdentityUrl();
+                           alarmLogger.sendAlarm ("MsoConfigurationError", MsoAlarmLogger.CRITICAL, error);
+                           throw new MsoAdapterException (error, e);
+                       }
+                       tokenId = access.getToken ().getId ();
+                       expiration = access.getToken ().getExpires ();
+               } else if (ServerType.KEYSTONE_V3.equals(cloudIdentity.getIdentityServerType())) {
+                       try {
+                               KeystoneAuthHolder holder = keystoneV3Authentication.getToken(cloudSite, tenantId, "orchestration");
+                               tokenId = holder.getId();
+                               expiration = holder.getexpiration();
+                               heatUrl = holder.getServiceUrl();
+                       } catch (ServiceEndpointNotFoundException e) {
+                               // This comes back for not found (probably an incorrect region ID)
+                           String error = "cloud did not match an orchestration service for: region=" + region + ",cloud=" + cloudIdentity.getIdentityUrl();
+                           alarmLogger.sendAlarm ("MsoConfigurationError", MsoAlarmLogger.CRITICAL, error);
+                           throw new MsoAdapterException (error, e);
+                       }
+               }
+           } catch (OpenStackResponseException e) {
             if (e.getStatus () == 401) {
                 // Authentication error.
                 String error = "Authentication Failure: tenant=" + tenantId + ",cloud=" + cloudIdentity.getId ();
@@ -922,39 +974,13 @@ public class MsoHeatUtils extends MsoCommonUtils implements VduPlugin{
             // Catch-all
             throw runtimeExceptionToMsoException (e, TOKEN_AUTH);
         }
-
-        // For DCP/LCP, the region should be the cloudId.
-        String region = cloudSite.getRegionId ();
-        String heatUrl = null;
-        try {
-               // Isolate trying to printout the region IDs
-               try {
-                       LOGGER.debug("access=" + access.toString());
-                       for (Access.Service service : access.getServiceCatalog()) {
-                               List<Access.Service.Endpoint> endpoints = service.getEndpoints();
-                               for (Access.Service.Endpoint endpoint : endpoints) {
-                                       LOGGER.debug("AIC returned region=" + endpoint.getRegion());
-                               }
-                       }
-               } catch (Exception e) {
-                       LOGGER.debug("Encountered an error trying to printout Access object returned from AIC. " + e.getMessage());
-               }
-            heatUrl = KeystoneUtils.findEndpointURL (access.getServiceCatalog (), "orchestration", region, "public");
-            LOGGER.debug("heatUrl=" + heatUrl + ", region=" + region);
-        } catch (RuntimeException e) {
-            // This comes back for not found (probably an incorrect region ID)
-            String error = "AIC did not match an orchestration service for: region=" + region + ",cloud=" + cloudIdentity.getIdentityUrl();
-            alarmLogger.sendAlarm ("MsoConfigurationError", MsoAlarmLogger.CRITICAL, error);
-            throw new MsoAdapterException (error, e);
-        }
-
         Heat heatClient = new Heat (heatUrl);
-        heatClient.token (access.getToken ().getId ());
+        heatClient.token (tokenId);
 
         heatClientCache.put (cacheKey,
                              new HeatCacheEntry (heatUrl,
-                                                 access.getToken ().getId (),
-                                                 access.getToken ().getExpires ()));
+                                                 tokenId,
+                                                 expiration));
         LOGGER.debug ("Caching HEAT Client for " + cacheKey);
 
         return heatClient;
diff --git a/adapters/mso-adapter-utils/src/main/java/org/onap/so/openstack/utils/MsoKeystoneV3Utils.java b/adapters/mso-adapter-utils/src/main/java/org/onap/so/openstack/utils/MsoKeystoneV3Utils.java
new file mode 100644 (file)
index 0000000..da1957f
--- /dev/null
@@ -0,0 +1,41 @@
+package org.onap.so.openstack.utils;
+
+import java.util.Map;
+
+import org.onap.so.db.catalog.beans.CloudIdentity;
+import org.onap.so.openstack.beans.MsoTenant;
+import org.onap.so.openstack.exceptions.MsoCloudSiteNotFound;
+import org.onap.so.openstack.exceptions.MsoException;
+import org.springframework.stereotype.Component;
+
+@Component
+public class MsoKeystoneV3Utils extends MsoTenantUtils {
+
+       @Override
+       public String createTenant(String tenantName, String cloudSiteId, Map<String, String> metadata, boolean backout)
+                       throws MsoException {
+               throw new UnsupportedOperationException();
+       }
+
+       @Override
+       public MsoTenant queryTenant(String tenantId, String cloudSiteId) throws MsoException, MsoCloudSiteNotFound {
+               throw new UnsupportedOperationException();
+       }
+
+       @Override
+       public MsoTenant queryTenantByName(String tenantName, String cloudSiteId)
+                       throws MsoException, MsoCloudSiteNotFound {
+               throw new UnsupportedOperationException();
+       }
+
+       @Override
+       public boolean deleteTenant(String tenantId, String cloudSiteId) throws MsoException {
+               throw new UnsupportedOperationException();
+       }
+
+       @Override
+       public String getKeystoneUrl(String regionId, CloudIdentity cloudIdentity) throws MsoException {
+               return cloudIdentity.getIdentityUrl();
+       }
+
+}
index a9f0a39..b7676e0 100644 (file)
@@ -22,14 +22,19 @@ package org.onap.so.openstack.utils;
 
 
 import java.util.ArrayList;
+import java.util.Calendar;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
 import org.onap.so.cloud.CloudConfig;
+import org.onap.so.cloud.authentication.AuthenticationMethodFactory;
+import org.onap.so.cloud.authentication.KeystoneAuthHolder;
+import org.onap.so.cloud.authentication.KeystoneV3Authentication;
+import org.onap.so.cloud.authentication.ServiceEndpointNotFoundException;
 import org.onap.so.db.catalog.beans.CloudIdentity;
 import org.onap.so.db.catalog.beans.CloudSite;
-import org.onap.so.cloud.authentication.AuthenticationMethodFactory;
+import org.onap.so.db.catalog.beans.ServerType;
 import org.onap.so.logger.MessageEnum;
 import org.onap.so.logger.MsoAlarmLogger;
 import org.onap.so.logger.MsoLogger;
@@ -78,6 +83,9 @@ public class MsoNeutronUtils extends MsoCommonUtils
        
        @Autowired
        private MsoTenantUtilsFactory tenantUtilsFactory;
+
+       @Autowired
+       private KeystoneV3Authentication keystoneV3Authentication;
        
        private static MsoLogger LOGGER = MsoLogger.getMsoLogger (MsoLogger.Catalog.RA, MsoNeutronUtils.class);
        
@@ -356,7 +364,8 @@ public class MsoNeutronUtils extends MsoCommonUtils
     private Quantum getNeutronClient(CloudSite cloudSite, String tenantId) throws MsoException
        {
                String cloudId = cloudSite.getId();
-
+               String region = cloudSite.getRegionId();
+               
                // Check first in the cache of previously authorized clients
                String cacheKey = cloudId + ":" + tenantId;
                if (neutronClientCache.containsKey(cacheKey)) {
@@ -378,12 +387,48 @@ public class MsoNeutronUtils extends MsoCommonUtils
                CloudIdentity cloudIdentity = cloudSite.getIdentityService();
                MsoTenantUtils tenantUtils = tenantUtilsFactory.getTenantUtilsByServerType(cloudIdentity.getIdentityServerType());
         final String keystoneUrl = tenantUtils.getKeystoneUrl(cloudId, cloudIdentity);
-               Keystone keystoneTenantClient = new Keystone(keystoneUrl);
-               Access access = null;
+               String neutronUrl = null;
+               String tokenId = null;
+               Calendar expiration = null;
                try {
-                       Authentication credentials = authenticationMethodFactory.getAuthenticationFor(cloudIdentity);
-                       OpenStackRequest<Access> request = keystoneTenantClient.tokens().authenticate(credentials).withTenantId(tenantId);
-                       access = executeAndRecordOpenstackRequest(request);
+               if (ServerType.KEYSTONE.equals(cloudIdentity.getIdentityServerType())) {
+                               Keystone keystoneTenantClient = new Keystone(keystoneUrl);
+                               Access access = null;
+                               
+                               Authentication credentials = authenticationMethodFactory.getAuthenticationFor(cloudIdentity);
+                               OpenStackRequest<Access> request = keystoneTenantClient.tokens().authenticate(credentials).withTenantId(tenantId);
+                               access = executeAndRecordOpenstackRequest(request);
+                               
+                               
+                               try {
+                                       neutronUrl = KeystoneUtils.findEndpointURL(access.getServiceCatalog(), "network", region, "public");
+                                       if (! neutronUrl.endsWith("/")) {
+                               neutronUrl += "/v2.0/";
+                           }
+                               } catch (RuntimeException e) {
+                                       // This comes back for not found (probably an incorrect region ID)
+                                       String error = "Network service not found: region=" + region + ",cloud=" + cloudIdentity.getId();
+                                       alarmLogger.sendAlarm("MsoConfigurationError", MsoAlarmLogger.CRITICAL, error);
+                                       throw new MsoAdapterException (error, e);
+                               }
+                               tokenId = access.getToken().getId();
+                               expiration = access.getToken().getExpires();
+               } else if (ServerType.KEYSTONE_V3.equals(cloudIdentity.getIdentityServerType())) {
+                       try {
+                               KeystoneAuthHolder holder = keystoneV3Authentication.getToken(cloudSite, tenantId, "network");
+                               tokenId = holder.getId();
+                               expiration = holder.getexpiration();
+                               neutronUrl = holder.getServiceUrl();
+                               if (! neutronUrl.endsWith("/")) {
+                               neutronUrl += "/v2.0/";
+                           }
+                       } catch (ServiceEndpointNotFoundException e) {
+                               // This comes back for not found (probably an incorrect region ID)
+                                       String error = "Network service not found: region=" + region + ",cloud=" + cloudIdentity.getId();
+                                       alarmLogger.sendAlarm("MsoConfigurationError", MsoAlarmLogger.CRITICAL, error);
+                                       throw new MsoAdapterException (error, e);
+                       }
+               }
                }
                catch (OpenStackResponseException e) {
                        if (e.getStatus() == 401) {
@@ -408,25 +453,10 @@ public class MsoNeutronUtils extends MsoCommonUtils
                        MsoException me = runtimeExceptionToMsoException(e, "TokenAuth");
                        throw me;
                }
-
-               String region = cloudSite.getRegionId();
-               String neutronUrl = null;
-               try {
-                       neutronUrl = KeystoneUtils.findEndpointURL(access.getServiceCatalog(), "network", region, "public");
-                       if (! neutronUrl.endsWith("/")) {
-                neutronUrl += "/v2.0/";
-            }
-               } catch (RuntimeException e) {
-                       // This comes back for not found (probably an incorrect region ID)
-                       String error = "Network service not found: region=" + region + ",cloud=" + cloudIdentity.getId();
-                       alarmLogger.sendAlarm("MsoConfigurationError", MsoAlarmLogger.CRITICAL, error);
-                       throw new MsoAdapterException (error, e);
-               }
-
                Quantum neutronClient = new Quantum(neutronUrl);
-               neutronClient.token(access.getToken().getId());
+               neutronClient.token(tokenId);
 
-               neutronClientCache.put(cacheKey, new NeutronCacheEntry(neutronUrl, access.getToken().getId(), access.getToken().getExpires()));
+               neutronClientCache.put(cacheKey, new NeutronCacheEntry(neutronUrl, tokenId, expiration));
                LOGGER.debug ("Caching Neutron Client for " + cacheKey);
 
                return neutronClient;
index 79934cc..08c98f3 100644 (file)
@@ -36,6 +36,8 @@ public class MsoTenantUtilsFactory {
        protected CloudConfig cloudConfig;
        @Autowired
        protected MsoKeystoneUtils keystoneUtils;
+       @Autowired
+       protected MsoKeystoneV3Utils keystoneV3Utils;
        
        // based on Cloud IdentityServerType returns ORM or KEYSTONE Utils
        public MsoTenantUtils getTenantUtils(String cloudSiteId) throws MsoCloudSiteNotFound {
@@ -50,6 +52,8 @@ public class MsoTenantUtilsFactory {
                MsoTenantUtils tenantU = null;
                if (ServerType.KEYSTONE.equals(serverType)) {
                        tenantU = keystoneUtils;
+               } else if (ServerType.KEYSTONE_V3.equals(serverType)) {
+                       tenantU = keystoneV3Utils;
                }
                return tenantU;
        }
index 95e4352..a5abe75 100644 (file)
 
 package org.onap.so.cloud.authentication;
 
+import static com.shazam.shazamcrest.matcher.Matchers.sameBeanAs;
+import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
 import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.onap.so.BaseTest;
+import org.onap.so.cloud.authentication.models.RackspaceAuthentication;
 import org.onap.so.db.catalog.beans.AuthenticationType;
 import org.onap.so.db.catalog.beans.CloudIdentity;
-import org.onap.so.cloud.authentication.models.RackspaceAuthentication;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.test.context.ActiveProfiles;
-import org.springframework.test.context.junit4.SpringRunner;
+import org.onap.so.utils.CryptoUtils;
 
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.woorea.openstack.keystone.model.Authentication;
 import com.woorea.openstack.keystone.model.authentication.UsernamePassword;
 
@@ -42,10 +46,9 @@ import com.woorea.openstack.keystone.model.authentication.UsernamePassword;
  * only are tested.
  *
  */
-public class AuthenticationMethodTest extends BaseTest {
+public class AuthenticationMethodTest {
 
-       @Autowired
-       private AuthenticationMethodFactory authenticationMethodFactory;
+       private AuthenticationMethodFactory authenticationMethodFactory = new AuthenticationMethodFactory();
        /**
         * 
         */
@@ -99,4 +102,20 @@ public class AuthenticationMethodTest extends BaseTest {
                assertTrue(UsernamePassword.class.equals(auth.getClass()));
        
        }
+       
+       @Test
+       public void getAuthenticationForV3Test() throws JsonParseException, JsonMappingException, IOException {
+               
+               CloudIdentity identity = new CloudIdentity();
+               identity.setMsoId("my-username");
+               identity.setMsoPass(CryptoUtils.encryptCloudConfigPassword("my-password"));
+               identity.setProjectDomainName("test-domain");
+               identity.setUserDomainName("user-domain");
+               ObjectMapper mapper = new ObjectMapper();
+               com.woorea.openstack.keystone.v3.model.Authentication expected = 
+                               mapper.readValue(new String(Files.readAllBytes(Paths.get("src/test/resources/__files/KeystoneV3Payload.json"))), com.woorea.openstack.keystone.v3.model.Authentication.class);
+               com.woorea.openstack.keystone.v3.model.Authentication actual = authenticationMethodFactory.getAuthenticationForV3(identity, "project-x");
+               
+               assertThat(actual, sameBeanAs(expected));
+       }
 }
diff --git a/adapters/mso-adapter-utils/src/test/resources/__files/KeystoneV3Payload.json b/adapters/mso-adapter-utils/src/test/resources/__files/KeystoneV3Payload.json
new file mode 100644 (file)
index 0000000..dc6588e
--- /dev/null
@@ -0,0 +1,24 @@
+{
+       "identity": {
+               "methods": [
+                       "password"
+               ],
+               "password": {
+                       "user": {
+                               "domain": {
+                                       "name": "user-domain"
+                               },
+                               "name": "my-username",
+                               "password": "my-password"
+                       }
+               }
+       },
+       "scope": {
+               "project": {
+                       "domain": {
+                               "name": "test-domain"
+                       },
+                       "id": "project-x"
+               }
+       }
+}
diff --git a/adapters/mso-catalog-db-adapter/src/main/resources/db/migration/V4.12.2__AddDomainColumnsCloudIdentity.sql b/adapters/mso-catalog-db-adapter/src/main/resources/db/migration/V4.12.2__AddDomainColumnsCloudIdentity.sql
new file mode 100644 (file)
index 0000000..e2c4dc8
--- /dev/null
@@ -0,0 +1,3 @@
+ALTER TABLE identity_services
+ADD PROJECT_DOMAIN_NAME VARCHAR(255) DEFAULT NULL,
+ADD USER_DOMAIN_NAME VARCHAR(255) DEFAULT NULL;
index b4916bd..4d5105d 100644 (file)
@@ -4,6 +4,8 @@ CREATE TABLE IF NOT EXISTS `identity_services` (
   `IDENTITY_URL` varchar(200) DEFAULT NULL,
   `MSO_ID` varchar(255) DEFAULT NULL,
   `MSO_PASS` varchar(255) DEFAULT NULL,
+  `PROJECT_DOMAIN_NAME` varchar(255) DEFAULT NULL,
+  `USER_DOMAIN_NAME` varchar(255) DEFAULT NULL,
   `ADMIN_TENANT` varchar(50) DEFAULT NULL,
   `MEMBER_ROLE` varchar(50) DEFAULT NULL,
   `TENANT_METADATA` tinyint(1) DEFAULT 0,
@@ -193,7 +195,7 @@ insert into vnf_components(vnf_id, component_type, heat_template_id, heat_enviro
 
 INSERT INTO `cloudify_managers` (`ID`, `CLOUDIFY_URL`, `USERNAME`, `PASSWORD`, `VERSION`, `LAST_UPDATED_BY`, `CREATION_TIMESTAMP`, `UPDATE_TIMESTAMP`) VALUES ('mtn13', 'http://localhost:28090/v2.0', 'm93945', '93937EA01B94A10A49279D4572B48369', NULL, 'MSO_USER', '2018-07-17 14:05:08', '2018-07-17 14:05:08');
 
-INSERT INTO `identity_services` (`ID`, `IDENTITY_URL`, `MSO_ID`, `MSO_PASS`, `ADMIN_TENANT`, `MEMBER_ROLE`, `TENANT_METADATA`, `IDENTITY_SERVER_TYPE`, `IDENTITY_AUTHENTICATION_TYPE`, `LAST_UPDATED_BY`, `CREATION_TIMESTAMP`, `UPDATE_TIMESTAMP`) VALUES ('MTN13', 'http://localhost:28090/v2.0', 'm93945', '93937EA01B94A10A49279D4572B48369', 'admin', 'admin', 1, 'KEYSTONE', 'USERNAME_PASSWORD', 'MSO_USER', '2018-07-17 14:02:33', '2018-07-17 14:02:33');
+INSERT INTO `identity_services` (`ID`, `IDENTITY_URL`, `MSO_ID`, `MSO_PASS`, `PROJECT_DOMAIN_NAME`, `USER_DOMAIN_NAME`, `ADMIN_TENANT`, `MEMBER_ROLE`, `TENANT_METADATA`, `IDENTITY_SERVER_TYPE`, `IDENTITY_AUTHENTICATION_TYPE`, `LAST_UPDATED_BY`, `CREATION_TIMESTAMP`, `UPDATE_TIMESTAMP`) VALUES ('MTN13', 'http://localhost:28090/v2.0', 'm93945', '93937EA01B94A10A49279D4572B48369', NULL, NULL, 'admin', 'admin', 1, 'KEYSTONE', 'USERNAME_PASSWORD', 'MSO_USER', '2018-07-17 14:02:33', '2018-07-17 14:02:33');
 
 INSERT INTO `cloud_sites` (`ID`, `REGION_ID`, `IDENTITY_SERVICE_ID`, `CLOUD_VERSION`, `CLLI`, `CLOUDIFY_ID`, `PLATFORM`, `ORCHESTRATOR`, `LAST_UPDATED_BY`, `CREATION_TIMESTAMP`, `UPDATE_TIMESTAMP`) VALUES ('mtn13', 'mtn13', 'MTN13', '2.5', 'MDT13', 'mtn13', NULL, 'orchestrator', 'MSO_USER', '2018-07-17 14:06:28', '2018-07-17 14:06:28');
 
index 0103564..77df06b 100644 (file)
@@ -152,7 +152,7 @@ INSERT INTO `heat_environment` (`ARTIFACT_UUID`, `NAME`, `VERSION`, `DESCRIPTION
 
 INSERT INTO `cloudify_managers` (`ID`, `CLOUDIFY_URL`, `USERNAME`, `PASSWORD`, `VERSION`, `LAST_UPDATED_BY`, `CREATION_TIMESTAMP`, `UPDATE_TIMESTAMP`) VALUES ('mtn13', 'http://localhost:28090/v2.0', 'm93945', '93937EA01B94A10A49279D4572B48369', NULL, 'MSO_USER', '2018-07-17 14:05:08', '2018-07-17 14:05:08');
 
-INSERT INTO `identity_services` (`ID`, `IDENTITY_URL`, `MSO_ID`, `MSO_PASS`, `ADMIN_TENANT`, `MEMBER_ROLE`, `TENANT_METADATA`, `IDENTITY_SERVER_TYPE`, `IDENTITY_AUTHENTICATION_TYPE`, `LAST_UPDATED_BY`, `CREATION_TIMESTAMP`, `UPDATE_TIMESTAMP`) VALUES ('MTN13', 'http://localhost:28090/v2.0', 'm939454', '93937EA01B94A10A49279D4572B48369', 'admin', 'admin', 1, 'KEYSTONE', 'USERNAME_PASSWORD', 'MSO_USER', '2018-07-17 14:02:33', '2018-07-17 14:02:33');
+INSERT INTO `identity_services` (`ID`, `IDENTITY_URL`, `MSO_ID`, `MSO_PASS`, `PROJECT_DOMAIN_NAME`, `USER_DOMAIN_NAME`, `ADMIN_TENANT`, `MEMBER_ROLE`, `TENANT_METADATA`, `IDENTITY_SERVER_TYPE`, `IDENTITY_AUTHENTICATION_TYPE`, `LAST_UPDATED_BY`, `CREATION_TIMESTAMP`, `UPDATE_TIMESTAMP`) VALUES ('MTN13', 'http://localhost:28090/v2.0', 'm939454', '93937EA01B94A10A49279D4572B48369', null, null, 'admin', 'admin', 1, 'KEYSTONE', 'USERNAME_PASSWORD', 'MSO_USER', '2018-07-17 14:02:33', '2018-07-17 14:02:33');
 
 INSERT INTO `cloud_sites` (`ID`, `region_id`, `identity_service_id`, `cloud_version`, `clli`, `cloudify_id`, `platform`, `orchestrator`, `CREATION_TIMESTAMP`, `UPDATE_TIMESTAMP`) VALUES ('MTN13', 'mtn13', 'MTN13', '3.0', 'MDT13', 'mtn13', null, 'orchestrator', '2018-07-17 14:06:28', '2018-07-17 14:06:28');
 
index a051417..0c67123 100644 (file)
@@ -788,6 +788,8 @@ CREATE TABLE IF NOT EXISTS `identity_services` (
   `IDENTITY_URL` varchar(200) DEFAULT NULL,
   `MSO_ID` varchar(255) DEFAULT NULL,
   `MSO_PASS` varchar(255) DEFAULT NULL,
+  `PROJECT_DOMAIN_NAME` varchar(255) DEFAULT NULL,
+  `USER_DOMAIN_NAME` varchar(255) DEFAULT NULL,
   `ADMIN_TENANT` varchar(50) DEFAULT NULL,
   `MEMBER_ROLE` varchar(50) DEFAULT NULL,
   `TENANT_METADATA` tinyint(1) DEFAULT 0,
index dc6abce..c8ea165 100644 (file)
@@ -201,7 +201,10 @@ public class MsoRequestsDbAdapterImpl implements MsoRequestsDbAdapter {
                        String error = "Entity not found. Unable to retrieve OperationStatus Object ServiceId: " + serviceId + " operationId: "
                                        + operationId;
                        logger.error(error);
-                       throw new MsoRequestsDbException(error,MsoLogger.ErrorCode.BusinessProcesssError);
+//                     throw new MsoRequestsDbException(error,MsoLogger.ErrorCode.BusinessProcesssError);
+                       operStatus = new OperationStatus();
+                       operStatus.setOperationId(operationId);
+                       operStatus.setServiceId(serviceId);
                }
 
                operStatus.setUserId(userId);
index 7c2dd12..103e216 100644 (file)
@@ -365,35 +365,10 @@ public class MSORequestDBImplTest {
                updatedOperationStatus.setReason(reason);
                updatedOperationStatus.setOperationContent(operationContent);
 
-               try {
-            dbAdapter.updateServiceOperationStatus(serviceId, operationId, operation,  userId,
-                     result, operationContent,  progress, reason);
-            fail("Expected MsoRequestsDbException to be thrown");   
-        } catch (Exception e) {
-            assertEquals("Entity not found. Unable to retrieve OperationStatus Object ServiceId: " + serviceId + " operationId: " + operationId,e.getMessage());
-            for(ILoggingEvent logEvent : TestAppender.events)
-                if(logEvent.getLoggerName().equals("org.onap.so.logging.cxf.interceptor.SOAPLoggingInInterceptor") &&
-                        logEvent.getMarker().getName().equals("ENTRY")
-                        ){
-                    Map<String,String> mdc = logEvent.getMDCPropertyMap();
-                    assertNotNull(mdc.get(ONAPLogConstants.MDCs.INSTANCE_UUID));
-                    assertNotNull(mdc.get(ONAPLogConstants.MDCs.REQUEST_ID));
-                    assertNotNull(mdc.get(ONAPLogConstants.MDCs.INVOCATION_ID));
-                    assertEquals("",mdc.get(ONAPLogConstants.MDCs.PARTNER_NAME));
-                    assertEquals("/services/RequestsDbAdapter",mdc.get(ONAPLogConstants.MDCs.SERVICE_NAME));
-                    assertEquals("INPROGRESS",mdc.get(ONAPLogConstants.MDCs.RESPONSE_STATUS_CODE));
-                }else if(logEvent.getLoggerName().equals("org.onap.so.logging.cxf.interceptor.SOAPLoggingOutInterceptor") &&
-                        logEvent.getMarker()!= null && logEvent.getMarker().getName().equals("EXIT")){
-                    Map<String,String> mdc = logEvent.getMDCPropertyMap();
-                    assertNotNull(mdc.get(ONAPLogConstants.MDCs.REQUEST_ID));
-                    assertNotNull(mdc.get(ONAPLogConstants.MDCs.INVOCATION_ID));
-                    assertEquals("500",mdc.get(ONAPLogConstants.MDCs.RESPONSE_CODE));
-                    assertEquals("",mdc.get(ONAPLogConstants.MDCs.PARTNER_NAME));
-                    assertEquals("/services/RequestsDbAdapter",mdc.get(ONAPLogConstants.MDCs.SERVICE_NAME));
-                    assertEquals("ERROR",mdc.get(ONAPLogConstants.MDCs.RESPONSE_STATUS_CODE));
-                }
-        }              
-               
+               dbAdapter.updateServiceOperationStatus(serviceId, operationId, operation,  userId,
+                    result, operationContent,  progress, reason);              
+               OperationStatus dbOpStatus = operationStatusRepository.findOneByServiceIdAndOperationId(serviceId,operationId);         
+               assertThat(dbOpStatus, sameBeanAs(updatedOperationStatus).ignoring("operateAt").ignoring("finishedAt"));                
        }
        
        @Test 
index d5183ff..f1eab66 100644 (file)
@@ -982,22 +982,7 @@ class CatalogDbUtils {
                                                vfModuleModelJson.put("modelInfo", vfModuleModelInfo)
                                                String vfModuleType = jsonUtils.getJsonValueForKey(vfModule, "type")
                                                vfModuleModelJson.put("vfModuleType", vfModuleType)
-                                               switch(catalogUtilsVersion) {
-                                                       case "v1":
-                                                       //TODO this does not work, isBase is not a integer.
-                                                               Integer isBase = jsonUtils.getJsonIntValueForKey(vfModule, "isBase")
-                                                               if (isBase.intValue() == 1) {
-                                                                       vfModuleModelJson.put("isBase", "true")
-                                                               }
-                                                               else {
-                                                                       vfModuleModelJson.put("isBase", "false")
-                                                               }
-                                                               break
-                                                       default:
-                                                               boolean isBase = jsonUtils.getJsonBooleanValueForKey(vfModule, "isBase")
-                                                               vfModuleModelJson.put("isBase", isBase)
-                                                               break
-                                               }
+                                               vfModuleModelJson.put("isBase", jsonUtils.getJsonBooleanValueForKey(vfModule, "isBase"))
                                                String vfModuleLabel = jsonUtils.getJsonValueForKey(vfModule, "label")
                                                vfModuleModelJson.put("vfModuleLabel", vfModuleLabel)
                                                Integer initialCount = jsonUtils.getJsonIntValueForKey(vfModule, "initialCount")
index 0cce8b3..1989ca8 100644 (file)
@@ -266,7 +266,7 @@ public class ResourceRequestBuilder {
 
         HashMap<String, String> map = new Gson().fromJson(value, new TypeToken<HashMap<String, String>>() {}.getType());
 
-        String filePath = System.getProperty("mso.config.path") + "ASDC/" +  map.get("version") + "/" + map.get("name");
+        String filePath = System.getProperty("mso.config.path") + "/ASDC/" +  map.get("version") + "/" + map.get("name");
 
         File csarFile = new File(filePath);
 
index 64de29e..b912b64 100644 (file)
@@ -134,7 +134,7 @@ public class ActivateSDNCNetworkResource extends AbstractServiceTaskProcessor {
     public void prepareUpdateAfterActivateSDNCResource(DelegateExecution execution) {
         msoLogger.info("started prepareUpdateAfterActivateSDNCResource ")
 
-        ResourceInput resourceInputObj = execution.getVariable(Prefix + "resourceInput")
+        ResourceInput resourceInputObj = ResourceRequestBuilder.getJsonObject(execution.getVariable(Prefix + "resourceInput"), ResourceInput.class)
         String operType = resourceInputObj.getOperationType()
         String resourceCustomizationUuid = resourceInputObj.getResourceModelInfo().getModelCustomizationUuid()
         String ServiceInstanceId = resourceInputObj.getServiceInstanceId()
@@ -147,7 +147,7 @@ public class ActivateSDNCNetworkResource extends AbstractServiceTaskProcessor {
 
         String body = """
                 <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
-                        xmlns:ns="http://org.openecomp.mso/requestsdb">
+                        xmlns:ns="http://org.onap.so/requestsdb">
                         <soapenv:Header/>
                 <soapenv:Body>
                     <ns:updateResourceOperationStatus>
@@ -203,7 +203,7 @@ public class ActivateSDNCNetworkResource extends AbstractServiceTaskProcessor {
             String serviceInstanceId = execution.getVariable(Prefix + "serviceInstanceId")
             String source = execution.getVariable("source")
             String sdnc_service_id = execution.getVariable(Prefix + "sdncServiceId")
-            ResourceInput resourceInputObj = execution.getVariable(Prefix + "resourceInput")
+            ResourceInput resourceInputObj = ResourceRequestBuilder.getJsonObject(execution.getVariable(Prefix + "resourceInput"), ResourceInput.class)
             String networkInstanceId = execution.getVariable("networkInstanceId")
             String serviceType = resourceInputObj.getServiceType()
             String serviceModelInvariantUuid = resourceInputObj.getServiceModelInfo().getModelInvariantUuid()
index afd67c3..2ae7686 100644 (file)
@@ -278,7 +278,7 @@ public class Create3rdONAPE2EServiceInstance extends AbstractServiceTaskProcesso
 
                String body = """
                 <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
-                        xmlns:ns="http://org.openecomp.mso/requestsdb">
+                        xmlns:ns="http://org.onap.so/requestsdb">
                         <soapenv:Header/>
                 <soapenv:Body>
                     <ns:updateResourceOperationStatus>
index ff9a119..489e77e 100644 (file)
@@ -173,7 +173,7 @@ public class CreateDeviceResource extends AbstractServiceTaskProcessor {
 
                String body = """
                 <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
-                        xmlns:ns="http://org.openecomp.mso/requestsdb">
+                        xmlns:ns="http://org.onap.so/requestsdb">
                         <soapenv:Header/>
                 <soapenv:Body>
                     <ns:updateResourceOperationStatus>
index d0a3ddd..a505f3f 100644 (file)
@@ -73,7 +73,7 @@ public class CreateSDNCNetworkResource extends AbstractServiceTaskProcessor {
             msoLogger.info("The resourceInput is: " + resourceInput)
             //Get ResourceInput Object
             ResourceInput resourceInputObj = ResourceRequestBuilder.getJsonObject(resourceInput, ResourceInput.class)
-            execution.setVariable(Prefix + "resourceInput", resourceInputObj)
+            execution.setVariable(Prefix + "resourceInput", resourceInputObj.toString())
 
             //Deal with recipeParams
             String recipeParamsFromWf = execution.getVariable("recipeParamXsd")
@@ -185,7 +185,7 @@ public class CreateSDNCNetworkResource extends AbstractServiceTaskProcessor {
      * @param execution
      */
     public void updateResourceInput(DelegateExecution execution) {
-        ResourceInput resourceInputObj = execution.getVariable(Prefix + "resourceInput")
+        ResourceInput resourceInputObj = ResourceRequestBuilder.getJsonObject(execution.getVariable(Prefix + "resourceInput"), ResourceInput.class)
         String modelName = resourceInputObj.getResourceModelInfo().getModelName()
 
         switch (modelName) {
@@ -252,7 +252,7 @@ public class CreateSDNCNetworkResource extends AbstractServiceTaskProcessor {
             String serviceInstanceId = execution.getVariable(Prefix + "serviceInstanceId")
             String source = execution.getVariable("source")
             String sdnc_service_id = execution.getVariable(Prefix + "sdncServiceId")
-            ResourceInput resourceInputObj = execution.getVariable(Prefix + "resourceInput")
+            ResourceInput resourceInputObj = ResourceRequestBuilder.getJsonObject(execution.getVariable(Prefix + "resourceInput"), ResourceInput.class)
             String serviceType = resourceInputObj.getServiceType()
             String serviceModelInvariantUuid = resourceInputObj.getServiceModelInfo().getModelInvariantUuid()
             String serviceModelUuid = resourceInputObj.getServiceModelInfo().getModelUuid()
@@ -458,7 +458,7 @@ public class CreateSDNCNetworkResource extends AbstractServiceTaskProcessor {
     }
 
     public void prepareUpdateBeforeCreateSDNCResource(DelegateExecution execution) {
-        ResourceInput resourceInputObj = execution.getVariable(Prefix + "resourceInput")
+        ResourceInput resourceInputObj = ResourceRequestBuilder.getJsonObject(execution.getVariable(Prefix + "resourceInput"), ResourceInput.class)
         String operType = resourceInputObj.getOperationType()
         String resourceCustomizationUuid = resourceInputObj.getResourceModelInfo().getModelCustomizationUuid()
         String ServiceInstanceId = resourceInputObj.getServiceInstanceId()
@@ -471,7 +471,7 @@ public class CreateSDNCNetworkResource extends AbstractServiceTaskProcessor {
 
         String body = """
                 <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
-                        xmlns:ns="http://org.openecomp.mso/requestsdb">
+                        xmlns:ns="http://org.onap.so/requestsdb">
                         <soapenv:Header/>
                 <soapenv:Body>
                     <ns:updateResourceOperationStatus>
@@ -491,7 +491,7 @@ public class CreateSDNCNetworkResource extends AbstractServiceTaskProcessor {
     }
 
     public void prepareUpdateAfterCreateSDNCResource(execution) {
-        ResourceInput resourceInputObj = execution.getVariable(Prefix + "resourceInput")
+        ResourceInput resourceInputObj = ResourceRequestBuilder.getJsonObject(execution.getVariable(Prefix + "resourceInput"), ResourceInput.class)
         String operType = resourceInputObj.getOperationType()
         String resourceCustomizationUuid = resourceInputObj.getResourceModelInfo().getModelCustomizationUuid()
         String ServiceInstanceId = resourceInputObj.getServiceInstanceId()
@@ -504,7 +504,7 @@ public class CreateSDNCNetworkResource extends AbstractServiceTaskProcessor {
 
         String body = """
                 <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
-                        xmlns:ns="http://org.openecomp.mso/requestsdb">
+                        xmlns:ns="http://org.onap.so/requestsdb">
                         <soapenv:Header/>
                 <soapenv:Body>
                     <ns:updateResourceOperationStatus>
@@ -543,7 +543,7 @@ public class CreateSDNCNetworkResource extends AbstractServiceTaskProcessor {
         data = data.substring(data.indexOf("<"))
 
         def resp = new XmlSlurper().parseText(data)
-        ResourceInput resourceInputObj = execution.getVariable(Prefix + "resourceInput")
+        ResourceInput resourceInputObj = ResourceRequestBuilder.getJsonObject(execution.getVariable(Prefix + "resourceInput"), ResourceInput.class)
         String modelName = resourceInputObj.getResourceModelInfo().getModelName()
         def val = ""
 
index 6ab03b9..66479be 100644 (file)
@@ -145,7 +145,7 @@ public class DeActivateSDNCNetworkResource extends AbstractServiceTaskProcessor
             String serviceInstanceId = execution.getVariable(Prefix + "serviceInstanceId")
             String source = execution.getVariable("source")
             String sdnc_service_id = execution.getVariable(Prefix + "sdncServiceId")
-            ResourceInput resourceInputObj = execution.getVariable(Prefix + "resourceInput")
+            ResourceInput resourceInputObj = ResourceRequestBuilder.getJsonObject(resourceInput, ResourceInput.class)
             String serviceType = resourceInputObj.getServiceType()
             String serviceModelInvariantUuid = resourceInputObj.getServiceModelInfo().getModelInvariantUuid()
             String serviceModelUuid = resourceInputObj.getServiceModelInfo().getModelUuid()
@@ -339,7 +339,7 @@ public class DeActivateSDNCNetworkResource extends AbstractServiceTaskProcessor
     }
 
     public void prepareUpdateAfterDeActivateSDNCResource(DelegateExecution execution) {
-        ResourceInput resourceInputObj = execution.getVariable(Prefix + "resourceInput")
+        ResourceInput resourceInputObj = ResourceRequestBuilder.getJsonObject(resourceInput, ResourceInput.class)
         String operType = resourceInputObj.getOperationType()
         String resourceCustomizationUuid = resourceInputObj.getResourceModelInfo().getModelCustomizationUuid()
         String serviceInstanceId = resourceInputObj.getServiceInstanceId()
@@ -352,7 +352,7 @@ public class DeActivateSDNCNetworkResource extends AbstractServiceTaskProcessor
 
         String body = """
                 <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
-                        xmlns:ns="http://org.openecomp.mso/requestsdb">
+                        xmlns:ns="http://org.onap.so/requestsdb">
                         <soapenv:Header/>
                 <soapenv:Body>
                     <ns:updateResourceOperationStatus>
index 052b28d..078d68b 100644 (file)
@@ -184,7 +184,7 @@ public class Delete3rdONAPE2EServiceInstance extends AbstractServiceTaskProcesso
 
                String body = """
                 <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
-                        xmlns:ns="http://org.openecomp.mso/requestsdb">
+                        xmlns:ns="http://org.onap.so/requestsdb">
                         <soapenv:Header/>
                 <soapenv:Body>
                     <ns:updateResourceOperationStatus>
index 2829797..05e0f23 100644 (file)
@@ -167,7 +167,7 @@ public class DeleteDeviceResource extends AbstractServiceTaskProcessor {
 
                String body = """
                 <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
-                        xmlns:ns="http://org.openecomp.mso/requestsdb">
+                        xmlns:ns="http://org.onap.so/requestsdb">
                         <soapenv:Header/>
                 <soapenv:Body>
                     <ns:updateResourceOperationStatus>
index 6456fc3..cf78193 100644 (file)
@@ -171,7 +171,7 @@ public class DeleteSDNCNetworkResource extends AbstractServiceTaskProcessor {
             String serviceInstanceId = execution.getVariable(Prefix + "serviceInstanceId")
             String source = execution.getVariable("source")
             String sdnc_service_id = execution.getVariable(Prefix + "sdncServiceId")
-            ResourceInput resourceInputObj = execution.getVariable(Prefix + "resourceInput")
+            ResourceInput resourceInputObj = ResourceRequestBuilder.getJsonObject(resourceInput, ResourceInput.class)
             String serviceType = resourceInputObj.getServiceType()
             String serviceModelInvariantUuid = resourceInputObj.getServiceModelInfo().getModelInvariantUuid()
             String serviceModelUuid = resourceInputObj.getServiceModelInfo().getModelUuid()
@@ -371,7 +371,7 @@ public class DeleteSDNCNetworkResource extends AbstractServiceTaskProcessor {
     }
 
     public void prepareUpdateBeforeDeleteSDNCResource(DelegateExecution execution) {
-        ResourceInput resourceInputObj = execution.getVariable(Prefix + "resourceInput")
+        ResourceInput resourceInputObj = ResourceRequestBuilder.getJsonObject(resourceInput, ResourceInput.class)
         String operType = resourceInputObj.getOperationType()
         String resourceCustomizationUuid = resourceInputObj.getResourceModelInfo().getModelCustomizationUuid()
         String serviceInstanceId = resourceInputObj.getServiceInstanceId()
@@ -384,7 +384,7 @@ public class DeleteSDNCNetworkResource extends AbstractServiceTaskProcessor {
 
         String body = """
                 <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
-                        xmlns:ns="http://org.openecomp.mso/requestsdb">
+                        xmlns:ns="http://org.onap.so/requestsdb">
                         <soapenv:Header/>
                 <soapenv:Body>
                     <ns:updateResourceOperationStatus>
@@ -404,7 +404,7 @@ public class DeleteSDNCNetworkResource extends AbstractServiceTaskProcessor {
     }
 
     public void prepareUpdateAfterDeleteSDNCResource(DelegateExecution execution) {
-        ResourceInput resourceInputObj = execution.getVariable(Prefix + "resourceInput")
+        ResourceInput resourceInputObj = ResourceRequestBuilder.getJsonObject(resourceInput, ResourceInput.class)
         String operType = resourceInputObj.getOperationType()
         String resourceCustomizationUuid = resourceInputObj.getResourceModelInfo().getModelCustomizationUuid()
         String serviceInstanceId = resourceInputObj.getServiceInstanceId()
@@ -417,7 +417,7 @@ public class DeleteSDNCNetworkResource extends AbstractServiceTaskProcessor {
 
         String body = """
                 <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
-                        xmlns:ns="http://org.openecomp.mso/requestsdb">
+                        xmlns:ns="http://org.onap.so/requestsdb">
                         <soapenv:Header/>
                 <soapenv:Body>
                     <ns:updateResourceOperationStatus>
index 5b7d38d..194e7ff 100644 (file)
@@ -265,7 +265,7 @@ public class DoCreateE2EServiceInstance extends AbstractServiceTaskProcessor {
        public void createServiceInstance(DelegateExecution execution) {
                msoLogger.trace("createServiceInstance ")
                String msg = ""
-               String serviceInstanceId = UUID.randomUUID().toString()
+               String serviceInstanceId = execution.getVariable("serviceInstanceId")
                try {
                        org.onap.aai.domain.yang.ServiceInstance si = execution.getVariable("serviceInstanceData")
 
index 2167025..7707903 100644 (file)
@@ -242,7 +242,8 @@ public class DoCreateResources extends AbstractServiceTaskProcessor{
         String resourceParameters = ResourceRequestBuilder.buildResourceRequestParameters(execution, serviceModelUuid, resourceCustomizationUuid, serviceParameters)
         resourceInput.setResourceParameters(resourceParameters)
         resourceInput.setRequestsInputs(incomingRequest)
-        execution.setVariable("resourceInput", resourceInput)
+        execution.setVariable("resourceInput", resourceInput.toString())
+        execution.setVariable("resourceModelUUID", resourceInput.getResourceModelInfo().getModelUuid())
         msoLogger.trace("COMPLETED prepareResourceRecipeRequest Process ")
     }
 
@@ -253,18 +254,21 @@ public class DoCreateResources extends AbstractServiceTaskProcessor{
             String requestId = execution.getVariable("msoRequestId")
             String serviceInstanceId = execution.getVariable("serviceInstanceId")
             String serviceType = execution.getVariable("serviceType")
-            ResourceInput resourceInput = execution.getVariable("resourceInput")
+            String resourceInput = execution.getVariable("resourceInput")
+            String resourceModelUUID = execution.getVariable("resourceModelUUID")
 
             // requestAction is action, not opertiontype
             //String requestAction = resourceInput.getOperationType()
             String requestAction = "createInstance"
-            JSONObject resourceRecipe = cutils.getResourceRecipe(execution, resourceInput.getResourceModelInfo().getModelUuid(), requestAction)
+            JSONObject resourceRecipe = cutils.getResourceRecipe(execution, resourceModelUUID, requestAction)
 
             if (resourceRecipe != null) {
                 String recipeURL = BPMNProperties.getProperty("bpelURL", "http://mso:8080") + resourceRecipe.getString("orchestrationUri")
                 int recipeTimeOut = resourceRecipe.getInt("recipeTimeout")
                 String recipeParamXsd = resourceRecipe.get("paramXSD")
-                HttpResponse resp = BpmnRestClient.post(recipeURL, requestId, recipeTimeOut, requestAction, serviceInstanceId, serviceType, resourceInput.toString(), recipeParamXsd)
+
+                BpmnRestClient bpmnRestClient = new BpmnRestClient()
+                HttpResponse resp = bpmnRestClient.post(recipeURL, requestId, recipeTimeOut, requestAction, serviceInstanceId, serviceType, resourceInput, recipeParamXsd)
             } else {
                 String exceptionMessage = "Resource receipe is not found for resource modeluuid: " +
                         resourceInput.getResourceModelInfo().getModelUuid()
index b1c81cf..820b47a 100644 (file)
@@ -70,6 +70,16 @@ public class CloudIdentity {
     @Column(name = "MSO_PASS")
     private String msoPass;
     
+    @JsonProperty("project_domain_name")
+    @BusinessKey
+    @Column(name = "PROJECT_DOMAIN_NAME")
+    private String projectDomainName;
+    
+    @JsonProperty("user_domain_name")
+    @BusinessKey
+    @Column(name = "USER_DOMAIN_NAME")
+    private String userDomainName;
+    
     @JsonProperty("admin_tenant")
     @BusinessKey
     @Column(name = "ADMIN_TENANT")
@@ -224,6 +234,21 @@ public class CloudIdentity {
                this.identityAuthenticationType = identityAuthenticationType;
        }
 
+       public String getProjectDomainName() {
+               return projectDomainName;
+       }
+       
+       public void setProjectDomainName(String projectDomainName) {
+               this.projectDomainName = projectDomainName;
+       }
+       
+       public String getUserDomainName() {
+               return userDomainName;
+       }
+       
+       public void setUserDomainName(String userDomainName) {
+               this.userDomainName = userDomainName;
+       }
        @Override
        public CloudIdentity clone() {
                CloudIdentity cloudIdentityCopy = new CloudIdentity();
@@ -237,6 +262,8 @@ public class CloudIdentity {
                cloudIdentityCopy.tenantMetadata = this.tenantMetadata;
                cloudIdentityCopy.identityServerType = this.identityServerType;
                cloudIdentityCopy.identityAuthenticationType = this.identityAuthenticationType;
+               cloudIdentityCopy.projectDomainName = this.projectDomainName;
+               cloudIdentityCopy.userDomainName = this.userDomainName;
 
                return cloudIdentityCopy;
        }
@@ -245,6 +272,7 @@ public class CloudIdentity {
        public String toString() {
                return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).append("id", getId())
                                .append("identityUrl", getIdentityUrl()).append("msoId", getMsoId())
+                               .append("projectDomain", getProjectDomainName()).append("userDomain", getUserDomainName())
                                .append("adminTenant", getAdminTenant()).append("memberRole", getMemberRole())
                                .append("tenantMetadata", getTenantMetadata()).append("identityServerType", getIdentityServerType())
                                .append("identityAuthenticationType", getIdentityAuthenticationType()).toString();
@@ -262,6 +290,7 @@ public class CloudIdentity {
                return new EqualsBuilder().append(getId(), castOther.getId())
                                .append(getIdentityUrl(), castOther.getIdentityUrl()).append(getMsoId(), castOther.getMsoId())
                                .append(getMsoPass(), castOther.getMsoPass()).append(getAdminTenant(), castOther.getAdminTenant())
+                               .append(getProjectDomainName(), castOther.getProjectDomainName()).append(getUserDomainName(), castOther.getUserDomainName())
                                .append(getMemberRole(), castOther.getMemberRole())
                                .append(getTenantMetadata(), castOther.getTenantMetadata())
                                .append(getIdentityServerType(), castOther.getIdentityServerType())
@@ -271,7 +300,7 @@ public class CloudIdentity {
        @Override
        public int hashCode() {
                return new HashCodeBuilder(1, 31).append(getId()).append(getIdentityUrl()).append(getMsoId())
-                               .append(getMsoPass()).append(getAdminTenant()).append(getMemberRole()).append(getTenantMetadata())
+                               .append(getMsoPass()).append(getProjectDomainName()).append(getUserDomainName()).append(getAdminTenant()).append(getMemberRole()).append(getTenantMetadata())
                                .append(getIdentityServerType()).append(getIdentityAuthenticationType()).toHashCode();
        }
 }
\ No newline at end of file
index eeb5724..14834ea 100644 (file)
@@ -657,7 +657,7 @@ VALUES
 
 INSERT INTO `cloudify_managers` (`ID`, `CLOUDIFY_URL`, `USERNAME`, `PASSWORD`, `VERSION`, `LAST_UPDATED_BY`, `CREATION_TIMESTAMP`, `UPDATE_TIMESTAMP`) VALUES ('mtn13', 'http://localhost:28090/v2.0', 'm93945', '93937EA01B94A10A49279D4572B48369', NULL, 'MSO_USER', '2018-07-17 14:05:08', '2018-07-17 14:05:08');
 
-INSERT INTO `identity_services` (`ID`, `IDENTITY_URL`, `MSO_ID`, `MSO_PASS`, `ADMIN_TENANT`, `MEMBER_ROLE`, `TENANT_METADATA`, `IDENTITY_SERVER_TYPE`, `IDENTITY_AUTHENTICATION_TYPE`, `LAST_UPDATED_BY`, `CREATION_TIMESTAMP`, `UPDATE_TIMESTAMP`) VALUES ('MTN13', 'http://localhost:28090/v2.0', 'm93945', '93937EA01B94A10A49279D4572B48369', 'admin', 'admin', 1, 'KEYSTONE', 'USERNAME_PASSWORD', 'MSO_USER', '2018-07-17 14:02:33', '2018-07-17 14:02:33');
+INSERT INTO `identity_services` (`ID`, `IDENTITY_URL`, `MSO_ID`, `MSO_PASS`, `PROJECT_DOMAIN_NAME`, `USER_DOMAIN_NAME`, `ADMIN_TENANT`, `MEMBER_ROLE`, `TENANT_METADATA`, `IDENTITY_SERVER_TYPE`, `IDENTITY_AUTHENTICATION_TYPE`, `LAST_UPDATED_BY`, `CREATION_TIMESTAMP`, `UPDATE_TIMESTAMP`) VALUES ('MTN13', 'http://localhost:28090/v2.0', 'm93945', '93937EA01B94A10A49279D4572B48369', NULL, NULL, 'admin', 'admin', 1, 'KEYSTONE', 'USERNAME_PASSWORD', 'MSO_USER', '2018-07-17 14:02:33', '2018-07-17 14:02:33');
 
 INSERT INTO `cloud_sites` (`ID`, `REGION_ID`, `IDENTITY_SERVICE_ID`, `CLOUD_VERSION`, `CLLI`, `CLOUDIFY_ID`, `PLATFORM`, `ORCHESTRATOR`, `LAST_UPDATED_BY`, `CREATION_TIMESTAMP`, `UPDATE_TIMESTAMP`) VALUES ('mtn13', 'mtn13', 'MTN13', '2.5', 'MDT13', 'mtn13', NULL, 'orchestrator', 'MSO_USER', '2018-07-17 14:06:28', '2018-07-17 14:06:28');
 
index 6eaad26..b7d0061 100644 (file)
@@ -837,6 +837,8 @@ CREATE TABLE IF NOT EXISTS `identity_services` (
   `IDENTITY_URL` varchar(200) DEFAULT NULL,
   `MSO_ID` varchar(255) DEFAULT NULL,
   `MSO_PASS` varchar(255) DEFAULT NULL,
+  `PROJECT_DOMAIN_NAME` varchar(255) DEFAULT NULL,
+  `USER_DOMAIN_NAME` varchar(255) DEFAULT NULL,
   `ADMIN_TENANT` varchar(50) DEFAULT NULL,
   `MEMBER_ROLE` varchar(50) DEFAULT NULL,
   `TENANT_METADATA` tinyint(1) DEFAULT 0,
index 143b1cf..cb5586d 100644 (file)
@@ -77,7 +77,7 @@ if [ ! -z "${TRUSTSTORE}" ]; then
        jksargs="$jksargs -Djavax.net.ssl.trustStorePassword=${TRUSTSTORE_PASSWORD}"
 fi
 
-jvmargs="${JVM_ARGS} -Djava.security.egd=file:/dev/./urandom -Dlogs_dir=${LOG_PATH} -Dlogging.config=/app/logback-spring.xml $jksargs -Dspring.config.location=$CONFIG_PATH ${SSL_DEBUG} ${DISABLE_SNI}"
+jvmargs="${JVM_ARGS} -Djava.security.egd=file:/dev/./urandom -Dlogs_dir=${LOG_PATH} -Dlogging.config=/app/logback-spring.xml $jksargs -Dspring.config.additional-location=$CONFIG_PATH ${SSL_DEBUG} ${DISABLE_SNI}"
 
 echo "JVM Arguments: ${jvmargs}"