From af56b68e030085aa523152e83811705636ead79c Mon Sep 17 00:00:00 2001 From: asgar Date: Fri, 8 Mar 2019 19:52:33 +0530 Subject: added ansible server functionality multiple ansible server for CDT Issue-ID: APPC-1510 Change-Id: I383bc63705418654efb596c617309821ebbeb9b4 Signed-off-by: Mohamed Asgar Samiulla --- src/app/admin/admin.component.css | 34 +++ src/app/admin/admin.component.html | 93 ++++++ src/app/admin/admin.component.ts | 247 ++++++++++++++++ .../admin/view-edit/ansible-server.component.css | 143 +++++++++ .../admin/view-edit/ansible-server.component.html | 139 +++++++++ .../admin/view-edit/ansible-server.component.ts | 326 +++++++++++++++++++++ 6 files changed, 982 insertions(+) create mode 100644 src/app/admin/admin.component.css create mode 100644 src/app/admin/admin.component.html create mode 100644 src/app/admin/admin.component.ts create mode 100644 src/app/admin/view-edit/ansible-server.component.css create mode 100644 src/app/admin/view-edit/ansible-server.component.html create mode 100644 src/app/admin/view-edit/ansible-server.component.ts (limited to 'src/app/admin') diff --git a/src/app/admin/admin.component.css b/src/app/admin/admin.component.css new file mode 100644 index 0000000..f6cbef3 --- /dev/null +++ b/src/app/admin/admin.component.css @@ -0,0 +1,34 @@ +/* +============LICENSE_START========================================== +=================================================================== +Copyright (C) 2018 AT&T Intellectual Property. All rights reserved. +=================================================================== + +Unless otherwise specified, all software contained herein is licensed +under the Apache License, Version 2.0 (the License); +you may not use this software 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. + +============LICENSE_END============================================ +*/ + +.build-button { + padding: 11px; + font-size: 20px; + color: green; + border: 1PX SOLID GREEN; + MARGIN-BOTTOM: 40PX +} + +table.table th, +table.table td { + padding: 0rem; +} \ No newline at end of file diff --git a/src/app/admin/admin.component.html b/src/app/admin/admin.component.html new file mode 100644 index 0000000..1185957 --- /dev/null +++ b/src/app/admin/admin.component.html @@ -0,0 +1,93 @@ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
CONFIGURATION SERVER CLOUD-OWNER/CLOUD-REGION/TENANTDESCRIPTIONMODIFIERDATE MODIFIED
{{item['serverandport']}} + + + + + + + +
{{info.ownerid}} / {{info.regionid}} / {{info.tenantid}}
+
{{item['descr']}} {{item['modifier']}} {{item['modified-date']}} + +
+ +
+ + +
+
+
+
+

There is no admin Artifacts

+
+
+ {{noDataMsg}} +
+
+
+
+
+ + + +
\ No newline at end of file diff --git a/src/app/admin/admin.component.ts b/src/app/admin/admin.component.ts new file mode 100644 index 0000000..74fe86a --- /dev/null +++ b/src/app/admin/admin.component.ts @@ -0,0 +1,247 @@ +/* +============LICENSE_START========================================== +=================================================================== +Copyright (C) 2018 AT&T Intellectual Property. All rights reserved. +=================================================================== + +Unless otherwise specified, all software contained herein is licensed +under the Apache License, Version 2.0 (the License); +you may not use this software 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. + +============LICENSE_END============================================ +*/ + +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { HttpUtilService } from '../shared/services/httpUtil/http-util.service'; +import { MappingEditorService } from '../shared/services/mapping-editor.service'; +import { ParamShareService } from '../shared/services/paramShare.service'; +import { environment } from '../../environments/environment'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { NgProgress } from 'ngx-progressbar'; +import { APIService } from "../shared/services/cdt.apicall"; +import { UtilityService } from '../shared/services/utilityService/utility.service'; +import { NotificationsService } from 'angular2-notifications'; + + +@Component({ selector: 'admin', templateUrl: './admin.component.html', styleUrls: ['./admin.component.css']}) +export class AdminComponent implements OnInit { + displayAnsibleServerData: Array = []; + ansibleServerData; + sortOrder = false; + noData = false; + sortBy: string; + filter: Object = {}; + noDataMsg: string; + errorMessage = ''; + invalid = true; + currentUser; + fileName = "ansible_admin_FQDN_Artifact_0.0.1V.json"; + + options = { + timeOut: 4000, + showProgressBar: true, + pauseOnHover: true, + clickToClose: true, + maxLength: 200 + }; + + constructor ( + private paramShareService: ParamShareService, + private ngProgress: NgProgress, + private httpUtil: HttpUtilService, + private router: Router, + private activeROute: ActivatedRoute, + private mappingEditorService: MappingEditorService, + private modalService: NgbModal, + private apiService:APIService, + private utilService: UtilityService, + private nService: NotificationsService + ) { + } + + ngOnInit() { + const apiToken = localStorage['apiToken']; + this.currentUser = localStorage['userId']; + + if(this.paramShareService.ansibleServerData) { + this.ansibleServerData = this.paramShareService.ansibleServerData; + console.log("cached ansibleServerData===>"+JSON.stringify(this.ansibleServerData)); + this.formatRawData(); + } else { + //testing + //this.ansibleServerData = "{\"fqdn-list\":[{\"vnf-management-server-fqdn\": \"fqdn-value1:url\:port\",\"cloud-owner-list\":[{\"cloud-owner\": \"owner1\",\"region-id-list\": [{\"region-id\": \"regiodnid1\",\"tenant-id-list\": [\"tenantid1\",\"tenantid2\"]},{\"region-id\": \"regiondid2\",\"tenant-id-list\": [\"tenantid1\",\"tenantid2\"]}]},{\"cloud-owner\": \"owner2\",\"region-id-list\": [{\"region-id\": \"regionid1\",\"tenant-id-list\": [\"tenantid1\",\"tenantid2\"]}]}],\"description\": \"fqdn for east zone vUSP Production\",\"username\": \"albino attuid\",\"create-date\": \"\",\"modify-username\": \"Asgar\",\"modify-date\": \"10/26/2018\"}]}"; + //this.ansibleServerData = JSON.parse(this.ansibleServerData); + //this.formatRawData(); + this.getArtifacts(); + } + + } + + getArtifacts() { + + const input = { + "input":{ + "design-request":{ + "request-id":localStorage['apiToken'], + "action":"getArtifact", + "payload":"{\"vnf-type\":\"NULL\",\"vnfc-type\":\"NULL\",\"protocol\":\"\",\"incart\":\"N\",\"action\":\"NULL\",\"artifact-name\":\""+this.fileName+"\",\"artifact-type\":\"APPC-CONFIG\",\"userID\":\"admin\"}" + } + } + }; + //const x = JSON.parse(data.input['design-request']['payload']); + //x.userID = localStorage['userId']; + //data.input['design-request']['payload'] = JSON.stringify(x); + console.log("input to payload====", JSON.stringify(input)); + + this.ngProgress.start(); + + this.apiService.callGetArtifactsApi(input).subscribe(data => { + + if( this.utilService.checkResult(data)) { + console.log("response===>"+JSON.stringify(data)); + this.ansibleServerData = JSON.parse(data.output.data.block).artifactInfo[0]["artifact-content"]; + this.ansibleServerData = JSON.parse(this.ansibleServerData); + console.log("ansibleServerData===>"+JSON.stringify(this.ansibleServerData)) + this.paramShareService.ansibleServerData = this.ansibleServerData; + this.formatRawData(); + } + this.ngProgress.done(); + }, + error => { + + this.nService.error('Error', + 'Error in connecting to APPC Server', this.options ); + }); + + + } + + formatRawData(){ + this.displayAnsibleServerData = [this.ansibleServerData["fqdn-list"].length]; + console.log("length==>"+this.ansibleServerData["fqdn-list"].length) + for(let i=0;i 1) { + this.displayAnsibleServerData[i]['port'] = splitArray[splitArray.length-1]; + } + let clouditem = ""; + for(let j=0; j"+JSON.stringify({"ownerid": ownerid, "regionid": regionid, "tenantid": tenantid})) + } + + } + + } + console.log("info array==>"+JSON.stringify(cloudInfoArray)) + this.displayAnsibleServerData[i]["info"] = cloudInfoArray; + this.displayAnsibleServerData[i]["cloud"] = clouditem; + this.displayAnsibleServerData[i]["descr"] = fqdl["description"]; + this.displayAnsibleServerData[i]["creator"] = fqdl["username"]; + this.displayAnsibleServerData[i]["created-date"] = fqdl["create-date"]; + this.displayAnsibleServerData[i]["modifier"] = fqdl["modify-username"]; + this.displayAnsibleServerData[i]["modified-date"] = fqdl["modify-date"]; + + } + } + + + + viewAnbsibleServer(selectedConfig, updateIndex) { + sessionStorage.setItem('updateIndex', updateIndex); + sessionStorage.setItem('ansibleserver', JSON.stringify(selectedConfig)); + + this + .router + .navigate(['../ansible-server'], { + relativeTo: this.activeROute + }); + } + + createAnsibleServer() { + let newServer = {"server":"","port":"","info":[],"cloud":"","descr":"","creator":this.currentUser,"created-date":this.utilService.getDate(),"modifier":"","modified-date":""}; + sessionStorage.setItem('ansibleserver', JSON.stringify(newServer)); + + if(this.ansibleServerData) { + sessionStorage.setItem('updateIndex', this.ansibleServerData["fqdn-list"].length); + } else { + sessionStorage.setItem('updateIndex', '0'); + } + + this + .router + .navigate(['../ansible-server'], { + relativeTo: this.activeROute + }); + } + + + saveToAppc() { + let artifactContent = this.utilService.appendSlashes(JSON.stringify(this.paramShareService.ansibleServerData)); + let input = { + "input": { + "design-request": { + "request-id": localStorage['apiToken'], + "action": "uploadAdminArtifact", + "payload": "{\"userID\": \"admin\",\"vnf-type\" : \"NULL \",\"action\" : \"NULL\",\"artifact-name\" : \""+this.fileName+"\",\"artifact-type\" : \"APPC-CONFIG\",\"artifact-version\" : \"0.1\",\"artifact-contents\":\""+artifactContent+"\"}", + } + } + } + let response = this.apiService.callGetArtifactsApi(input); + response.subscribe(response => { + this.utilService.processApiSubscribe(response, this.utilService.putAction, "admin artifact"); + //this.clearCache(); + }, + error => this.utilService.processApiError()); + } + + // ngOnDestroy() { + // console.log("destry....") + // } + + clearCache() { + this.paramShareService.ansibleServerData = undefined; + sessionStorage.removeItem('ansibleserver'); + } + + download() { + if(this.ansibleServerData){ + this.utilService.downloadArtifactToPc(JSON.stringify(this.ansibleServerData, null, '\t'), "json", this.fileName, 100) + } + } + +} \ No newline at end of file diff --git a/src/app/admin/view-edit/ansible-server.component.css b/src/app/admin/view-edit/ansible-server.component.css new file mode 100644 index 0000000..1d4f6c0 --- /dev/null +++ b/src/app/admin/view-edit/ansible-server.component.css @@ -0,0 +1,143 @@ +/* +============LICENSE_START========================================== +=================================================================== +Copyright (C) 2018 AT&T Intellectual Property. All rights reserved. +=================================================================== + +Unless otherwise specified, all software contained herein is licensed +under the Apache License, Version 2.0 (the License); +you may not use this software 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. + +============LICENSE_END============================================ +*/ + +.form-group { + padding: 5px +} + +.panel { + margin-top: 20px; +} + +.form-control { + width: 100%; +} + +.panel-header { + padding: 20px 0px; +} + +.container { + border-radius: 0.375rem; + transition: all 0.3s ease; + padding: 40px; +} + +.container2 { + box-shadow: -4px 14px 20px 0px rgba(46, 61, 73, 0.15); + border-radius: 0.375rem; + transition: all 0.3s ease; + padding: 10px; + margin-top: 57px; +} + +.error-message { + color: red; +} + +.warning-message { + color: rgb(255,191,0); +} + +.create-wrapper { + text-align: left; +} + +.removevnfcClass { + text-align: right; +} + +.form-custom-group { + margin-bottom: 0; + vertical-align: middle; +} + +.side-by-side { + position: relative; +} + +.sidebtn { + position: absolute; + left: 26.5em; + top: 0em; +} + +.clear-control { + margin: 40px 20px; + display: inherit; +} + +.short-column { + width: 100px; +} + +.long-column { + width: 200px; +} + +th, +td { + min-width: 150px; +} + +.headlinesInBold { + font-weight: bold; +} + +.titleCustom { + padding-left: 25px; + overflow: visible; +} + +.anchorHover { + cursor: pointer; +} + +.custom-btn { + margin-top: 20px; + margin-bottom: 10px; +} + +.clear-btn { + margin-top: 23px; +} + +.col-md-button { + margin: auto; + margin-right: 20px; + text-align: center; + margin-bottom: 15px; +} + +.selectedAction { + /* change here */ + font-weight: bold; + color: #1A86E4 +} + +.file { + visibility: hidden; + position: absolute; +} + +.col-md-1point5 {width: 12%} +.col-md-2point5 {width: 21%;} \ No newline at end of file diff --git a/src/app/admin/view-edit/ansible-server.component.html b/src/app/admin/view-edit/ansible-server.component.html new file mode 100644 index 0000000..a8f2137 --- /dev/null +++ b/src/app/admin/view-edit/ansible-server.component.html @@ -0,0 +1,139 @@ + + + + +
+ +
+
+
+ + + {{errorMessage}}{{warningMessage}} +
+
+ + + {{porterrorMessage}}{{portwarningMessage}} +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+ +
+
+
+
+ +
+
+ + +
+
+ + + {{ownerIdErrMsg}} +
+
+ + + {{regionIdErrMsg}} +
+
+ + + {{tenantIdErrMsg}} +
+
+ +
+ +
+
+
+
+ {{zeroTenantIdsErrorMsg}} +
+ +
+
+
+ + +
Enter Owner ID
+
+
+ + +
Enter Region ID
+
+
+ + +
Enter Tenant ID
+
+
+ +
+ Remove +
+
+
+
+ +
+
+ +
+
+ Cancel + +
+
+ +
+
+
+
diff --git a/src/app/admin/view-edit/ansible-server.component.ts b/src/app/admin/view-edit/ansible-server.component.ts new file mode 100644 index 0000000..b629b65 --- /dev/null +++ b/src/app/admin/view-edit/ansible-server.component.ts @@ -0,0 +1,326 @@ +/* +============LICENSE_START========================================== +=================================================================== +Copyright (C) 2018 AT&T Intellectual Property. All rights reserved. +=================================================================== + +Unless otherwise specified, all software contained herein is licensed +under the Apache License, Version 2.0 (the License); +you may not use this software 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. + +============LICENSE_END============================================ +*/ + +import * as XLSX from 'xlsx'; +import * as _ from 'underscore'; + +import { ActivatedRoute, Router } from '@angular/router'; +import { Component, OnInit, ViewChild } from '@angular/core'; +import { Observable } from 'rxjs/Observable'; +import { UtilityService } from '../../shared/services/utilityService/utility.service'; + +// Common Confirm Modal +import { DialogService } from 'ng2-bootstrap-modal'; +import { ParamShareService } from '../../shared/services/paramShare.service'; + + +declare var $: any; +type AOA = Array>; + +@Component({ + selector: 'ansible-server', + templateUrl: './ansible-server.component.html', + styleUrls: ['./ansible-server.component.css'], +}) +export class AnsibleServerComponent implements OnInit { + + //settings for the notifications. + options = { + timeOut: 4500, + showProgressBar: true, + pauseOnHover: true, + clickToClose: true, + maxLength: 200 + }; + + public ownerIdErrMsg; + public regionIdErrMsg; + public tenantIdErrMsg + public zeroTenantIdsErrorMsg; + public porterrorMessage; + public portwarningMessage; + public errorMessage; + public warningMessage; + public invalid = true; + public portinvalid = true; + public item; + public sample = {ownerid:"", regionid:"", tenantid:""}; + public updateIndex; + public currentUser; + constructor( + private paramShareService: ParamShareService, + private router: Router, + private activeROute: ActivatedRoute, + private utilService: UtilityService + + ) + { + console.log("testing...."); + } + +ngOnInit() { + this.currentUser = localStorage['userId']; + this.item = JSON.parse(sessionStorage.getItem("ansibleserver")); + this.updateIndex = parseInt(sessionStorage.getItem("updateIndex")); + console.log("index===>"+this.updateIndex); + if(!this.paramShareService.ansibleServerData) { + this.invalid = true; + } else { + this.invalid = false; + } + console.log("selecteditem===>"+JSON.stringify(this.item)); +} + +addInfo() { + console.log("selectediteminfo===>"+JSON.stringify(this.item.info)); + if(this.validateTenantId()) { + this.item.info.push(this.sample) + this.sample = {ownerid:"", regionid:"", tenantid:""}; + this.zeroTenantIdsErrorMsg = ""; + } + +} + +removeInfo(index) { + console.log("selectediteminfo===>"+index+JSON.stringify(this.item.info)); + this.item.info.splice(index, 1); + console.log("selectediteminfo===>"+JSON.stringify(this.item.info)); + +} + +// save() { +// console.log("ansibleServerData===>"+ JSON.stringify(this.paramShareService.ansibleServerData)) +// let ansibleServer = this.createAnsibleserverData(this.item); +// this.paramShareService.ansibleServerData["fqdn-list"].push(ansibleServer); +// this.router.navigate(['../admin'], { +// relativeTo: this.activeROute +// }); +// } + +update() { + console.log("ansibleServerData===>"+ JSON.stringify(this.paramShareService.ansibleServerData)) + + + let ansibleServer = this.createAnsibleserverData(this.item); + //need to revisit to initialize paramShareService.ansibleServerData + if(!this.paramShareService.ansibleServerData) { + this.paramShareService.ansibleServerData = {"fqdn-list" : []}; + } + if(this.updateIndex != this.paramShareService.ansibleServerData["fqdn-list"].length) { + ansibleServer["modify-username"] = this.currentUser; + ansibleServer["modify-date"] = this.utilService.getDate(); + console.log("update........") + } + this.paramShareService.ansibleServerData["fqdn-list"][this.updateIndex] = ansibleServer; + this.router.navigate(['../admin'], { + relativeTo: this.activeROute + }); + + +} + +cancel() { + sessionStorage.removeItem("ansibleserver"); + this.router.navigate(['../admin'], { + relativeTo: this.activeROute + }); +} + +createAnsibleserverData(displayAnsibleServer){ + let cloudOwnerList = this.createCloudOwnerList(displayAnsibleServer); + let anisble = { + "vnf-management-server-fqdn": displayAnsibleServer.server+":"+displayAnsibleServer.port, + "cloud-owner-list":cloudOwnerList, + "description":displayAnsibleServer.descr, + "username":displayAnsibleServer.creator, + "create-date":displayAnsibleServer['created-date'], + "modify-username":displayAnsibleServer.modifier, + "modify-date":displayAnsibleServer['modified-date'] + }; + return anisble; + +} + +createCloudOwnerList(displayAnsibleServer) { + let cloudOwnerList = []; + + //prepare unique cloud-owner + for(let i=0; i { + if(element["cloud-owner"] == info.ownerid) { + exist = true; + + } + }); + if(!exist){ + cloudOwnerList.push(cloudOwner); + } + + } + console.log("cloudOwnerList===>"+JSON.stringify(cloudOwnerList)); + + //prepare region id + cloudOwnerList.forEach(cloudOwner => { + let regionIdList = []; + for(let i=0; i { + if(element["region-id"] == info.regionid) { + exist = true; + } + }); + if(!exist){ + regionIdList.push({"region-id":info.regionid}); + } + } + + } + cloudOwner["region-id-list"] = regionIdList; + }); + + console.log("cloudOwnerList===>"+JSON.stringify(cloudOwnerList)); + + //prepare tenant id + cloudOwnerList.forEach(cloudOwner => { + cloudOwner["region-id-list"].forEach(regionid => { + let teanantIdList = []; + for(let i=0; i { + if(element == info.tenantid) { + exist = true; + } + }); + if(!exist){ + teanantIdList.push(info.tenantid); + } + } + + } + regionid["tenant-id-list"] = teanantIdList; + }); + }); + console.log("cloudOwnerList===>"+JSON.stringify(cloudOwnerList)); + return cloudOwnerList; + } + + //validating the fdqn url + validateFdqn(fdqn) { + if (fdqn.trim().length < 1) { + this.errorMessage = 'Please enter Configuration Server FQDN'; + this.warningMessage = ''; + this.invalid = true; + } else if (fdqn.startsWith(' ') || fdqn.endsWith(' ')) { + this.errorMessage = 'Leading and trailing spaces are not allowed'; + this.warningMessage = ''; + this.invalid = true; + } else if (!(fdqn.startsWith('http') || fdqn.endsWith('https'))) { + this.warningMessage = 'FDQN can start with eighther http or https protocol'; + this.errorMessage = ''; + this.invalid = false; + + // } else if (name.includes(' ')) { + // this.errorMessage = 'More than one space is not allowed in VNFC Type'; + // this.invalid = true; + // } else if (name.length > 50) { + // this.errorMessage = 'VNFC Type should be of minimum one character and maximum 50 character'; + // this.invalid = true; + // + } else { + this.invalid = false; + this.errorMessage = ''; + this.warningMessage = ''; + } + } + + //validating the port + validatePort(port) { + if (port.trim().length < 1) { + this.porterrorMessage = 'Please enter port'; + this.portwarningMessage = ''; + this.invalid = true; + } else if (port.startsWith(' ') || port.endsWith(' ')) { + this.porterrorMessage = 'Leading and trailing spaces are not allowed'; + this.portwarningMessage = ''; + this.invalid = true; + + } else if (isNaN(port)) { + this.portwarningMessage = ''; + this.porterrorMessage = 'Port should be a number'; + this.invalid = true; + port = parseInt(port); + } else if (!(0 <= port && port <= 65535 )) { + this.portwarningMessage = 'Port should be a number in range of 0 to 65535'; + this.porterrorMessage = ''; + this.invalid = false; + } else { + this.invalid = false; + this.porterrorMessage = ''; + this.portwarningMessage = ''; + } + } + + validate() { + this.validateFdqn(this.item.server); + this.validatePort(this.item.port); + if(this.item.info.length <= 0) { + this.zeroTenantIdsErrorMsg = "Please add atleast one Tenant ID."; + this.invalid = true; + } + if(!this.invalid) { + this.update(); + } + } + + validateTenantId(){ + let valid = true; + if(this.sample.ownerid.trim().length == 0) { + this.ownerIdErrMsg = "Enter OwnerID"; + valid = false; + } else { + this.ownerIdErrMsg = ""; + } + if(this.sample.regionid.trim().length == 0) { + this.regionIdErrMsg = "Enter RegionID"; + valid = false; + } else { + this.regionIdErrMsg = ""; + } + if(this.sample.tenantid.trim().length == 0) { + this.tenantIdErrMsg = "Enter TenantID"; + valid = false; + } else { + this.tenantIdErrMsg = ""; + } + return valid; + } + + +} -- cgit 1.2.3-korg