diff options
34 files changed, 292 insertions, 145 deletions
diff --git a/cds-ui/client/src/app/feature-modules/blueprint/modify-template/modify-template.component.html b/cds-ui/client/src/app/feature-modules/blueprint/modify-template/modify-template.component.html index 546668d43..a6612d537 100644 --- a/cds-ui/client/src/app/feature-modules/blueprint/modify-template/modify-template.component.html +++ b/cds-ui/client/src/app/feature-modules/blueprint/modify-template/modify-template.component.html @@ -26,7 +26,7 @@ limitations under the License. <div style="display: flex;flex-direction: row"> <div style="width: 30em;"> - <button class="btn-active" (click)="changeView()">{{viewText}}</button> + <button class="btn-active" (click)="changeView()" disabled>{{viewText}}</button> </div> <div style="width: 16em"> </div> diff --git a/cds-ui/client/src/app/feature-modules/blueprint/select-template/template-options/template-options.component.html b/cds-ui/client/src/app/feature-modules/blueprint/select-template/template-options/template-options.component.html index 9c2d721e0..d66b559f3 100644 --- a/cds-ui/client/src/app/feature-modules/blueprint/select-template/template-options/template-options.component.html +++ b/cds-ui/client/src/app/feature-modules/blueprint/select-template/template-options/template-options.component.html @@ -19,7 +19,7 @@ limitations under the License. ============LICENSE_END============================================ --> <mat-radio-group> - <mat-radio-button value="1" (click)="selected(1)">Upload Template file</mat-radio-button><br><br> - <mat-radio-button value="2" (click)="selected(2)">Starter Template</mat-radio-button><br><br> - <mat-radio-button value="3" (click)="selected(3)">Existing Model File</mat-radio-button> -</mat-radio-group> + <mat-radio-button value="1" (click)="selected(1)">Upload Template file</mat-radio-button><br><br> + <!-- <mat-radio-button value="2" (click)="selected(2)">Starter Template</mat-radio-button><br><br> + <mat-radio-button value="3" (click)="selected(3)">Existing Model File</mat-radio-button> --> +</mat-radio-group>
\ No newline at end of file diff --git a/cds-ui/client/src/app/feature-modules/resource-definition/resource-creation/existing-model/search-resource/search-resource.component.html b/cds-ui/client/src/app/feature-modules/resource-definition/resource-creation/existing-model/search-resource/search-resource.component.html index d02cef429..a2e0a7c05 100644 --- a/cds-ui/client/src/app/feature-modules/resource-definition/resource-creation/existing-model/search-resource/search-resource.component.html +++ b/cds-ui/client/src/app/feature-modules/resource-definition/resource-creation/existing-model/search-resource/search-resource.component.html @@ -20,7 +20,7 @@ <form class="example-form" [formGroup]="myControl"> <mat-form-field class="example-full-width"> - <input type="text" [(ngModel)]="searchText" placeholder="Search Resources" matInput [matAutocomplete]="auto" formControlName="search_input"> + <input #resourceSelect type="text" [(ngModel)]="searchText" placeholder="Search Resources" matInput [matAutocomplete]="auto" formControlName="search_input"> <button matSuffix mat-icon-button (click)="fetchResourceByName()"><mat-icon>search</mat-icon></button> <mat-autocomplete #auto="matAutocomplete"> <mat-option (click)="selected(option)" *ngFor="let option of options | search : searchText" [value]="option.tags"> diff --git a/cds-ui/client/src/app/feature-modules/resource-definition/resource-creation/existing-model/search-resource/search-resource.component.ts b/cds-ui/client/src/app/feature-modules/resource-definition/resource-creation/existing-model/search-resource/search-resource.component.ts index 1850549aa..5aac51bed 100644 --- a/cds-ui/client/src/app/feature-modules/resource-definition/resource-creation/existing-model/search-resource/search-resource.component.ts +++ b/cds-ui/client/src/app/feature-modules/resource-definition/resource-creation/existing-model/search-resource/search-resource.component.ts @@ -21,6 +21,7 @@ import { Component, OnInit, ViewChild, EventEmitter, Output } from '@angular/core'; import {FormBuilder, FormGroup, Validators} from '@angular/forms'; import { ExsistingModelService } from '../exsisting-model.service'; +import { MatAutocompleteTrigger } from '@angular/material' @Component({ selector: 'app-search-resource', @@ -31,8 +32,9 @@ export class SearchResourceComponent implements OnInit { myControl: FormGroup; @Output() resourcesData = new EventEmitter(); - options: any[] = [] ; - // = ['One','One1', 'Two', 'Three']; + options: any[] = ['One','One1', 'Two', 'Three']; + // @ViewChild('resourceSelect') resourceSelect; + @ViewChild('resourceSelect', { read: MatAutocompleteTrigger }) resourceSelect: MatAutocompleteTrigger; searchText: string = ''; constructor(private _formBuilder: FormBuilder, @@ -53,8 +55,8 @@ export class SearchResourceComponent implements OnInit { console.log(data); data.forEach(element => { this.options.push(element) - }); - // this.options = data. + }); + this.resourceSelect.openPanel(); }, error=>{ window.alert('error' + error); }) diff --git a/cds-ui/client/src/app/feature-modules/resource-definition/resource-creation/resource-creation.component.html b/cds-ui/client/src/app/feature-modules/resource-definition/resource-creation/resource-creation.component.html index 9b4dbe5e1..afb2b9d55 100644 --- a/cds-ui/client/src/app/feature-modules/resource-definition/resource-creation/resource-creation.component.html +++ b/cds-ui/client/src/app/feature-modules/resource-definition/resource-creation/resource-creation.component.html @@ -29,10 +29,10 @@ <app-resource-template-options (option)="selectedOption($event)"></app-resource-template-options> <br> <div> - <button mat-button matStepperNext class="matStepNextBtn">Proceed</button> + <button mat-button matStepperNext class="matStepNextBtn" *ngIf="selectedValue == 1 || selectedValue == 3">Proceed</button> </div> </mat-step> - <mat-step [stepControl]="step2FormGroup"> + <mat-step [stepControl]="step2FormGroup" *ngIf="selectedValue == 1 || selectedValue == 3"> <ng-template matStepLabel>Browse or Search Resources</ng-template> <app-upload-resource (fileData)=upload($event) *ngIf="selectedValue == 1"></app-upload-resource><br><br> <app-existing-model *ngIf="selectedValue == 3"></app-existing-model> diff --git a/cds-ui/client/src/app/feature-modules/resource-definition/resource-edit/resource-edit.component.html b/cds-ui/client/src/app/feature-modules/resource-definition/resource-edit/resource-edit.component.html index de81e2394..4bd4ad456 100644 --- a/cds-ui/client/src/app/feature-modules/resource-definition/resource-edit/resource-edit.component.html +++ b/cds-ui/client/src/app/feature-modules/resource-definition/resource-edit/resource-edit.component.html @@ -19,7 +19,7 @@ */--> -<button (click) ="changeView()" class="toggle-view-btn">{{viewText}}</button> +<button disabled style="opacity: 0.5;" (click) ="changeView()" class="toggle-view-btn">{{viewText}}</button> <br><br> <div *ngIf="designerMode"> <mat-card class="metadata-card"> @@ -50,5 +50,5 @@ <div class="btn"> <button mat-button matStepperPrevious>Back</button></div> <div class="btn"> - <button mat-button matStepperNext type="submit" (click)="updateResourcesState()">Upload</button> + <button mat-button matStepperNext type="submit" (click)="saveToBackend()">Save</button> </div> diff --git a/cds-ui/client/src/app/feature-modules/resource-definition/resource-edit/resource-edit.component.scss b/cds-ui/client/src/app/feature-modules/resource-definition/resource-edit/resource-edit.component.scss index 280645e3d..9c2d42a83 100644 --- a/cds-ui/client/src/app/feature-modules/resource-definition/resource-edit/resource-edit.component.scss +++ b/cds-ui/client/src/app/feature-modules/resource-definition/resource-edit/resource-edit.component.scss @@ -41,7 +41,7 @@ background:#3f51b5; margin-top: 10px; position: absolute; - border-radius: 1em; + border-radius: 4px; } .mat-card-title{ padding-top: 7px; @@ -61,11 +61,12 @@ margin: 0 25px 25px 0; display: inline-block; vertical-align: top; + border-radius: 4px; } .toggle-view-btn{ color:white; background:#3f51b5; - border-radius: 1em; + border-radius: 4px; margin-top: 10px; padding:0.6em; border: 1px solid #3f51b5; diff --git a/cds-ui/client/src/app/feature-modules/resource-definition/resource-edit/resource-edit.component.ts b/cds-ui/client/src/app/feature-modules/resource-definition/resource-edit/resource-edit.component.ts index 55b80628d..77e901717 100644 --- a/cds-ui/client/src/app/feature-modules/resource-definition/resource-edit/resource-edit.component.ts +++ b/cds-ui/client/src/app/feature-modules/resource-definition/resource-edit/resource-edit.component.ts @@ -27,6 +27,7 @@ import { IAppState } from '../../../common/core/store/state/app.state'; import { JsonEditorComponent, JsonEditorOptions } from 'ang-jsoneditor'; import { Observable } from 'rxjs'; import { A11yModule } from '@angular/cdk/a11y'; +import { ResourceEditService } from './resource-edit.service'; @Component({ selector: 'app-resource-edit', @@ -38,13 +39,13 @@ export class ResourceEditComponent implements OnInit { resources:IResources; data:IResources; rdState: Observable<IResourcesState>; - designerMode: boolean = true; - editorMode: boolean = false; + designerMode: boolean = false; + editorMode: boolean = true; viewText: string = "Open in Editor Mode"; @ViewChild(JsonEditorComponent) editor: JsonEditorComponent; options = new JsonEditorOptions(); - constructor(private store: Store<IAppState>) { + constructor(private store: Store<IAppState>, private resourceEditService: ResourceEditService) { this.rdState = this.store.select('resources'); this.options.mode = 'text'; this.options.modes = [ 'text', 'tree', 'view']; @@ -92,5 +93,15 @@ export class ResourceEditComponent implements OnInit { this.designerMode = true; this.viewText = 'Open in Editor Mode' } - } + } + + saveToBackend() { + this.resourceEditService.saveResource(this.data) + .subscribe(response=>{ + window.alert("save success"); + }, + error=>{ + window.alert('Error saving resources'); + }) + } } diff --git a/cds-ui/client/src/app/feature-modules/resource-definition/resource-edit/resource-edit.module.ts b/cds-ui/client/src/app/feature-modules/resource-definition/resource-edit/resource-edit.module.ts index 2b25ae339..ae7143cfb 100644 --- a/cds-ui/client/src/app/feature-modules/resource-definition/resource-edit/resource-edit.module.ts +++ b/cds-ui/client/src/app/feature-modules/resource-definition/resource-edit/resource-edit.module.ts @@ -30,6 +30,7 @@ import { SourcesTemplateComponent } from './sources-template/sources-template.co import { ResourceMetadataComponent } from './resource-metadata/resource-metadata.component'; import { DragDropModule } from '@angular/cdk/drag-drop'; import { NgJsonEditorModule } from 'ang-jsoneditor'; +import { ResourceEditService } from './resource-edit.service'; @NgModule({ declarations: [ ResourceEditComponent,SourcesTemplateComponent,ResourceMetadataComponent ], @@ -42,6 +43,7 @@ import { NgJsonEditorModule } from 'ang-jsoneditor'; MatExpansionModule,MatToolbarModule,MatIconModule, MatButtonModule, MatSidenavModule, MatCheckboxModule, MatListModule, MatGridListModule, MatCardModule, MatMenuModule, MatTableModule, MatPaginatorModule, MatSortModule, MatInputModule, MatSelectModule, MatRadioModule, MatFormFieldModule, MatStepperModule, NgJsonEditorModule ], - exports: [ ResourceEditComponent,SharedModule ] + exports: [ ResourceEditComponent,SharedModule ], + providers: [ ResourceEditService ] }) export class ResourceEditModule { } diff --git a/cds-ui/client/src/app/feature-modules/resource-definition/resource-edit/resource-edit.service.ts b/cds-ui/client/src/app/feature-modules/resource-definition/resource-edit/resource-edit.service.ts new file mode 100644 index 000000000..982d71d58 --- /dev/null +++ b/cds-ui/client/src/app/feature-modules/resource-definition/resource-edit/resource-edit.service.ts @@ -0,0 +1,39 @@ +/* +============LICENSE_START========================================== +=================================================================== +Copyright (C) 2018-19 IBM 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 { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Observable, observable } from 'rxjs'; +import { ApiService } from '../../../common/core/services/api.service'; +import { LoopbackConfig, ResourceDictionaryURLs } from '../../../common/constants/app-constants'; + +@Injectable() +export class ResourceEditService { + // blueprintUrl = '../../constants/blueprint.json'; + + constructor(private _http: HttpClient, private api: ApiService) { + } + + saveResource(resource) { + return this.api.post(LoopbackConfig.url+ ResourceDictionaryURLs.saveResourceDictionary, resource); + } +}
\ No newline at end of file diff --git a/components/model-catalog/blueprint-model/test-blueprint/remote_scripts/Definitions/data_types.json b/components/model-catalog/blueprint-model/test-blueprint/remote_scripts/Definitions/data_types.json index df594ab9e..25efb3656 100644 --- a/components/model-catalog/blueprint-model/test-blueprint/remote_scripts/Definitions/data_types.json +++ b/components/model-catalog/blueprint-model/test-blueprint/remote_scripts/Definitions/data_types.json @@ -10,7 +10,7 @@ "constraints": [ { "valid_values": [ - "ANSIBLE", "PYTHON" + "ansible_galaxy", "pip" ] } ] diff --git a/components/model-catalog/blueprint-model/test-blueprint/remote_scripts/Definitions/node_types.json b/components/model-catalog/blueprint-model/test-blueprint/remote_scripts/Definitions/node_types.json index 0ee00b3ba..e5b8b3062 100644 --- a/components/model-catalog/blueprint-model/test-blueprint/remote_scripts/Definitions/node_types.json +++ b/components/model-catalog/blueprint-model/test-blueprint/remote_scripts/Definitions/node_types.json @@ -4,8 +4,12 @@ "description": "This is Remote Python Execution Component.", "version": "1.0.0", "attributes": { - "execution-logs": { - "required": true, + "prepare-environment-logs": { + "required": false, + "type": "string" + }, + "execute-command-logs": { + "required": false, "type": "string" } }, @@ -30,6 +34,11 @@ "required": false, "type": "json" }, + "argument-properties": { + "description": "Argument Json Content or DSL Json reference.", + "required": false, + "type": "json" + }, "command": { "description": "Command to execute.", "required": true, diff --git a/components/model-catalog/blueprint-model/test-blueprint/remote_scripts/Definitions/remote_scripts.json b/components/model-catalog/blueprint-model/test-blueprint/remote_scripts/Definitions/remote_scripts.json index 80ef02f4f..48992bd72 100644 --- a/components/model-catalog/blueprint-model/test-blueprint/remote_scripts/Definitions/remote_scripts.json +++ b/components/model-catalog/blueprint-model/test-blueprint/remote_scripts/Definitions/remote_scripts.json @@ -25,6 +25,13 @@ "file": "Definitions/policy_types.json" } ], + "dsl_definitions": { + "execute-argument-properties": { + "input": { + "get_input": "input" + } + } + }, "topology_template": { "workflows": { "execute-remote-python": { @@ -39,14 +46,28 @@ ] } }, - "inputs": {}, + "inputs": { + "input": { + "required": false, + "type": "string" + } + }, "outputs": { - "logs": { - "type": "json", + "prepare-environment-logs": { + "type": "string", "value": { "get_attribute": [ "execute-remote-python", - "execution-logs" + "prepare-environment-logs" + ] + } + }, + "execute-command-logs": { + "type": "string", + "value": { + "get_attribute": [ + "execute-remote-python", + "execute-command-logs" ] } } @@ -66,12 +87,21 @@ }, "inputs": {}, "outputs": { - "logs": { - "type": "json", + "prepare-environment-logs": { + "type": "string", "value": { "get_attribute": [ "execute-remote-ansible", - "execution-logs" + "prepare-environment-logs" + ] + } + }, + "execute-command-logs": { + "type": "string", + "value": { + "get_attribute": [ + "execute-remote-ansible", + "execute-command-logs" ] } } @@ -86,13 +116,19 @@ "operations": { "process": { "implementation": { - "primary": "component-script", - "dependencies": [ - "pyaml" - ] + "primary": "component-script" }, "inputs": { - "command": "python SamplePython.py blah" + "command": "python SamplePython.py $input", + "packages": [ + { + "type": "pip", + "package": [ + "pyaml" + ] + } + ], + "argument-properties": "*execute-argument-properties" } } } @@ -118,13 +154,13 @@ "command": "ansible-playbook first.yaml", "packages": [ { - "type": "PYTHON", + "type": "pip", "package": [ - "ansible" + "requirements.txt" ] }, { - "type": "ANSIBLE", + "type": "ansible_galaxy", "package": [ "juniper.junos" ] diff --git a/components/model-catalog/blueprint-model/test-blueprint/remote_scripts/Environments/requirements.txt b/components/model-catalog/blueprint-model/test-blueprint/remote_scripts/Environments/requirements.txt new file mode 100644 index 000000000..cabb1f519 --- /dev/null +++ b/components/model-catalog/blueprint-model/test-blueprint/remote_scripts/Environments/requirements.txt @@ -0,0 +1 @@ +ansible
\ No newline at end of file diff --git a/components/model-catalog/definition-type/starter-type/data_type/dt-system-packages.json b/components/model-catalog/definition-type/starter-type/data_type/dt-system-packages.json index 81bb030b4..e70aa5ec8 100644 --- a/components/model-catalog/definition-type/starter-type/data_type/dt-system-packages.json +++ b/components/model-catalog/definition-type/starter-type/data_type/dt-system-packages.json @@ -8,8 +8,8 @@ "constraints": [ { "valid_values": [ - "ANSIBLE", - "PYTHON" + "ansible_galaxy", + "pip" ] } ] diff --git a/components/model-catalog/definition-type/starter-type/node_type/component-remote-python-executor.json b/components/model-catalog/definition-type/starter-type/node_type/component-remote-python-executor.json index 3eaee761f..09b371c22 100644 --- a/components/model-catalog/definition-type/starter-type/node_type/component-remote-python-executor.json +++ b/components/model-catalog/definition-type/starter-type/node_type/component-remote-python-executor.json @@ -2,8 +2,12 @@ "description": "This is Remote Python Execution Component.", "version": "1.0.0", "attributes": { - "execution-logs": { - "required": true, + "prepare-environment-logs": { + "required": false, + "type": "string" + }, + "execute-command-logs": { + "required": false, "type": "string" } }, @@ -28,6 +32,11 @@ "required": false, "type": "json" }, + "argument-properties": { + "description": "Argument Json Content or DSL Json reference.", + "required": false, + "type": "json" + }, "command": { "description": "Command to execute.", "required": true, diff --git a/components/model-catalog/proto-definition/proto/CommandExecutor.proto b/components/model-catalog/proto-definition/proto/CommandExecutor.proto index 8f02b8aea..bc175dbc1 100644 --- a/components/model-catalog/proto-definition/proto/CommandExecutor.proto +++ b/components/model-catalog/proto-definition/proto/CommandExecutor.proto @@ -53,8 +53,8 @@ message Packages { } enum PackageType { - PYTHON = 0; - ANSIBLE = 1; + pip = 0; + ansible_galaxy = 1; } service CommandExecutorService { diff --git a/components/model-catalog/proto-definition/proto/README b/components/model-catalog/proto-definition/proto/README index 47bbf3f34..8cc9c485f 100644 --- a/components/model-catalog/proto-definition/proto/README +++ b/components/model-catalog/proto-definition/proto/README @@ -2,11 +2,10 @@ To create python bindings, Prerequisites: https://developers.google.com/protocol-buffers/docs/downloads - https://github.com/dropbox/mypy-protobuf - + or + https://grpc.io/docs/quickstart/python.html Command: - protoc -I=. --python_out=. --mypy_out=. CommandExecutor.proto - - - + protoc -I=. --python_out=. --grpc_python_out=. CommandExecutor.proto + or + python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. CommandExecutor.proto diff --git a/docs/CBA/index.rst b/docs/CBA/index.rst index 43317e6e5..27d69b086 100644 --- a/docs/CBA/index.rst +++ b/docs/CBA/index.rst @@ -93,13 +93,6 @@ Steps 3. cd cds ; mvn clean install ; cd .. 4. Open the cds-ui/client code for development -Data Flow: -========== -|image1| - -.. |image1| image:: media/image1.jpg - :width: 7.88889in - :height: 4.43750in Functional Decomposition: ========================= @@ -109,8 +102,3 @@ Functional Decomposition: :width: 7.88889in :height: 4.43750in -Controller design Studio Presentation: -====================================== - -Details about CDS Architecture and Design detail, Please click the link. -:download:`CDS_Architecture_Design.pptx`
\ No newline at end of file diff --git a/docs/CBA/CDS_Architecture_Design.pptx b/docs/CDS_Architecture_Design.pptx Binary files differindex a6c158d8d..a6c158d8d 100644 --- a/docs/CBA/CDS_Architecture_Design.pptx +++ b/docs/CDS_Architecture_Design.pptx diff --git a/docs/media/CDS_architecture.JPG b/docs/CDS_architecture.JPG Binary files differindex c8528ac1e..c8528ac1e 100644 --- a/docs/media/CDS_architecture.JPG +++ b/docs/CDS_architecture.JPG diff --git a/docs/bluePrintsProcessor.rst b/docs/bluePrintsProcessorMS.rst index 911f99945..911f99945 100644 --- a/docs/bluePrintsProcessor.rst +++ b/docs/bluePrintsProcessorMS.rst diff --git a/docs/controllerBlueprintStudioProcessor.rst b/docs/controllerBlueprintStudioProcessorMS.rst index 5c67d6c1d..5c67d6c1d 100644 --- a/docs/controllerBlueprintStudioProcessor.rst +++ b/docs/controllerBlueprintStudioProcessorMS.rst diff --git a/docs/datadictionary/complexResponse.rst b/docs/datadictionary/complexResponse.rst new file mode 100644 index 000000000..aa39ff235 --- /dev/null +++ b/docs/datadictionary/complexResponse.rst @@ -0,0 +1,20 @@ +.. This work is licensed under a Creative Commons Attribution 4.0 International License. +.. http://creativecommons.org/licenses/by/4.0 +.. Copyright (C) 2019 IBM. + +complex Response code +===================== + +{ + "id": 4, + "address": "192.168.10.2/32", + "vrf": null, + "tenant": null, + "status": 1, + "role": null, + "interface": null, + "description": "", + "nat_inside": null, + "created": "2018-08-30", + "last_updated": "2018-08-30T14:59:05.277820Z" +} diff --git a/docs/datadictionary/index.rst b/docs/datadictionary/index.rst index 24050000e..64abdece0 100644 --- a/docs/datadictionary/index.rst +++ b/docs/datadictionary/index.rst @@ -13,12 +13,9 @@ A Resource definition models the how a specific resource can be resolved. A resource is a variable/parameter in the context of the service. It can be anything, but it should not be confused with SDC or Openstack resources. -A Resource definition can have multiple sources to handle resolution in different ways. - -The main goal of Resource definition is to define re-usable entity that could be shared. - -Creation of data dictionaries is a standalone activity, separated from the blueprint design. +A Resource definition can have multiple sources to handle resolution in different ways. The main goal of Resource definition is to define re-usable entity that could be shared. +Creation of Resource definition is a standalone activity, separated from the blueprint design. As part of modelling a Resource definition entry, the following generic information should be provided: @@ -28,7 +25,7 @@ As part of modelling a Resource definition entry, the following generic informat :width: 7.88889in :height: 4.43750in -Bellow are properties that all the resource source have will have +Below are properties that all the resource source have will have The modeling does allow for data translation between external capability and CDS for both input and output key mapping. @@ -45,33 +42,12 @@ vf-module-model-customization-uuid and vf-module-label are two data dictionaries Here is how input-key-mapping, output-key-mapping and key-dependencies can be used: -vf-module-label Resource definition - -{ - "name" : "vf-module-label", - "tags" : "vf-module-label", - "updated-by" : "adetalhouet", - "property" : { - "description" : "vf-module-label", - "type" : "string" - }, - "sources" : { - "primary-db" : { - "type" : "source-primary-db", - "properties" : { - "type" : "SQL", - "query" : "select sdnctl.VF_MODULE_MODEL.vf_module_label as vf_module_label from sdnctl.VF_MODULE_MODEL where sdnctl.VF_MODULE_MODEL.customization_uuid=:customizationid", - "input-key-mapping" : { - "customizationid" : "vf-module-model-customization-uuid" - }, - "output-key-mapping" : { - "vf-module-label" : "vf_module_label" - }, - "key-dependencies" : [ "vf-module-model-customization-uuid" ] - } - } - } -} +.. toctree:: + :maxdepth: 1 + +resourceDefintionCode + + Resource source: @@ -81,7 +57,7 @@ Defines the contract to resolve a resource. A resource source is modeled, following TOSCA_ node type definition and derives from the Resource_ source. -Also please click below for detailed resource source details +Also please click below for resource source available details .. toctree:: :maxdepth: 1 @@ -89,4 +65,4 @@ Also please click below for detailed resource source details resourcesource .. _TOSCA: http://docs.oasis-open.org/tosca/TOSCA-Simple-Profile-YAML/v1.0/csprd01/TOSCA-Simple-Profile-YAML-v1.0-csprd01.html#DEFN_ENTITY_NODE_TYPE -.. _Resource_: https://wiki.onap.org/display/DW/Modeling+Concepts#ModelingConcepts-NodeResourceSource
\ No newline at end of file +.. _Resource: https://wiki.onap.org/display/DW/Modeling+Concepts#ModelingConcepts-NodeResourceSource
\ No newline at end of file diff --git a/docs/datadictionary/resourceDefinitionCode.rst b/docs/datadictionary/resourceDefinitionCode.rst new file mode 100644 index 000000000..a91767678 --- /dev/null +++ b/docs/datadictionary/resourceDefinitionCode.rst @@ -0,0 +1,48 @@ +.. This work is licensed under a Creative Commons Attribution 4.0 International License. +.. http://creativecommons.org/licenses/by/4.0 +.. Copyright (C) 2019 IBM. + +Source Capability Code +====================== + +{ + "description": "This is Component Resource Source Node Type", + "version": "1.0.0", + "properties": { + "script-type": { + "required": true, + "type": "string", + "default": "kotlin", + "constraints": [ + { + "valid_values": [ + "kotlin", + "jython" + ] + } + ] + }, + "script-class-reference": { + "description": "Capability reference name for internal and kotlin, for jython script file path", + "required": true, + "type": "string" + }, + "instance-dependencies": { + "required": false, + "description": "Instance dependency Names to Inject to Kotlin / Jython Script.", + "type": "list", + "entry_schema": { + "type": "string" + } + }, + "key-dependencies": { + "description": "Resource Resolution dependency dictionary names.", + "required": true, + "type": "list", + "entry_schema": { + "type": "string" + } + } + }, + "derived_from": "tosca.nodes.ResourceSource" +} diff --git a/docs/media/image0.jpg b/docs/image0.jpg Binary files differindex dce3cee25..dce3cee25 100644 --- a/docs/media/image0.jpg +++ b/docs/image0.jpg diff --git a/docs/index.rst b/docs/index.rst index 5f3902687..ed558bf59 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -27,7 +27,7 @@ Tosca Model Reference: |image0| -.. |image0| image:: media/image0.jpg +.. |image0| image:: image0.jpg :width: 7.88889in :height: 4.43750in @@ -46,8 +46,8 @@ MicroServices: :maxdepth: 1 :glob: - controllerBlueprintStudioProcessor - bluePrintsProcessor + controllerBlueprintStudioProcessorMS + bluePrintsProcessorMS Architecture: ============= @@ -58,7 +58,7 @@ The GUI handles direct user input and allows for displaying both design time and |image0| -.. |image0| image:: media/CDS_architecture.jpg +.. |image0| image:: CDS_architecture.jpg :height: 4.43750in :width: 7.88889in diff --git a/ms/blueprintsprocessor/functions/python-executor/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/python/executor/ComponentRemotePythonExecutor.kt b/ms/blueprintsprocessor/functions/python-executor/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/python/executor/ComponentRemotePythonExecutor.kt index 17d5fc76d..df92d7157 100644 --- a/ms/blueprintsprocessor/functions/python-executor/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/python/executor/ComponentRemotePythonExecutor.kt +++ b/ms/blueprintsprocessor/functions/python-executor/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/python/executor/ComponentRemotePythonExecutor.kt @@ -22,13 +22,13 @@ import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.* import org.onap.ccsdk.cds.blueprintsprocessor.services.execution.AbstractComponentFunction import org.onap.ccsdk.cds.blueprintsprocessor.services.execution.ExecutionServiceConstant import org.onap.ccsdk.cds.blueprintsprocessor.services.execution.RemoteScriptExecutionService -import org.onap.ccsdk.cds.controllerblueprints.command.api.ResponseStatus import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintProcessorException -import org.onap.ccsdk.cds.controllerblueprints.core.asJsonNode +import org.onap.ccsdk.cds.controllerblueprints.core.asJsonPrimitive import org.onap.ccsdk.cds.controllerblueprints.core.checkFileExists import org.onap.ccsdk.cds.controllerblueprints.core.checkNotBlank import org.onap.ccsdk.cds.controllerblueprints.core.data.OperationAssignment import org.onap.ccsdk.cds.controllerblueprints.core.normalizedFile +import org.onap.ccsdk.cds.controllerblueprints.core.service.BluePrintVelocityTemplateService import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils import org.slf4j.LoggerFactory import org.springframework.beans.factory.config.ConfigurableBeanFactory @@ -48,8 +48,12 @@ open class ComponentRemotePythonExecutor(private val remoteScriptExecutionServic companion object { const val INPUT_ENDPOINT_SELECTOR = "endpoint-selector" const val INPUT_DYNAMIC_PROPERTIES = "dynamic-properties" + const val INPUT_ARGUMENT_PROPERTIES = "argument-properties" const val INPUT_COMMAND = "command" const val INPUT_PACKAGES = "packages" + + const val ATTRIBUTE_PREPARE_ENV_LOG = "prepare-environment-logs" + const val ATTRIBUTE_EXEC_CMD_LOG = "execute-command-logs" } override suspend fun processNB(executionRequest: ExecutionServiceInput) { @@ -77,20 +81,17 @@ open class ComponentRemotePythonExecutor(private val remoteScriptExecutionServic val endPointSelector = getOperationInput(INPUT_ENDPOINT_SELECTOR) val dynamicProperties = getOperationInput(INPUT_DYNAMIC_PROPERTIES) - val command = getOperationInput(INPUT_COMMAND).asText() val packages = getOperationInput(INPUT_PACKAGES) + val argumentProperties = getOperationInput(INPUT_ARGUMENT_PROPERTIES) - // TODO("Python execution command and Resolve some expressions with dynamic properties") - val scriptCommand = command.replace(pythonScript.name, pythonScript.absolutePath) - -// val dependencies = operationAssignment.implementation?.dependencies + var command = getOperationInput(INPUT_COMMAND).asText() + command = command.replace(pythonScript.name, pythonScript.absolutePath) + val scriptCommand = BluePrintVelocityTemplateService.generateContent(command, json = JacksonUtils.getJson(argumentProperties)) try { // Open GRPC Connection remoteScriptExecutionService.init(endPointSelector.asText()) - var executionLogs = "" - // If packages are defined, then install in remote server if (packages !is MissingNode && packages !is NullNode) { val prepareEnvInput = PrepareRemoteEnvInput(requestId = processId, @@ -99,8 +100,7 @@ open class ComponentRemotePythonExecutor(private val remoteScriptExecutionServic packages = packages ) val prepareEnvOutput = remoteScriptExecutionService.prepareEnv(prepareEnvInput) - executionLogs = prepareEnvOutput.response - setOutput(executionLogs) + setAttribute(ATTRIBUTE_PREPARE_ENV_LOG, prepareEnvOutput.response.asJsonPrimitive()) check(prepareEnvOutput.status == StatusType.SUCCESS) { "failed to get prepare remote env response status for requestId(${prepareEnvInput.requestId})" } @@ -111,24 +111,18 @@ open class ComponentRemotePythonExecutor(private val remoteScriptExecutionServic remoteIdentifier = RemoteIdentifier(blueprintName = blueprintName, blueprintVersion = blueprintVersion), command = scriptCommand) val remoteExecutionOutput = remoteScriptExecutionService.executeCommand(remoteExecutionInput) - executionLogs += remoteExecutionOutput.response - setOutput(executionLogs) + setAttribute(ATTRIBUTE_EXEC_CMD_LOG, remoteExecutionOutput.response.asJsonPrimitive()) check(remoteExecutionOutput.status == StatusType.SUCCESS) { "failed to get prepare remote command response status for requestId(${remoteExecutionOutput.requestId})" } } catch (e: Exception) { - log.error("", e) + log.error("Failed to process on remote executor", e) } finally { remoteScriptExecutionService.close() } } - private fun setOutput(executionLogs: String) { - bluePrintRuntimeService.setNodeTemplateAttributeValue(nodeTemplateName, - "execution-logs", JacksonUtils.jsonNodeFromObject(executionLogs)) - } - override suspend fun recoverNB(runtimeException: RuntimeException, executionRequest: ExecutionServiceInput) { bluePrintRuntimeService.getBluePrintError() .addError("Failed in ComponentJythonExecutor : ${runtimeException.message}") diff --git a/ms/blueprintsprocessor/modules/services/execution-service/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/execution/RemoteScriptExecutionService.kt b/ms/blueprintsprocessor/modules/services/execution-service/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/execution/RemoteScriptExecutionService.kt index 99d4f8c24..7aee95e11 100644 --- a/ms/blueprintsprocessor/modules/services/execution-service/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/execution/RemoteScriptExecutionService.kt +++ b/ms/blueprintsprocessor/modules/services/execution-service/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/execution/RemoteScriptExecutionService.kt @@ -107,7 +107,7 @@ class GrpcRemoteScriptExecutionService(private val bluePrintGrpcLibPropertyServi val pckage = Packages.newBuilder() JsonFormat.parser().merge(it.toString(), pckage) packageList.add(pckage.build()) - } + } return PrepareEnvInput.newBuilder() .setIdentifiers(this.remoteIdentifier!!.asGrpcData()) diff --git a/ms/command-executor/src/main/docker/Dockerfile b/ms/command-executor/src/main/docker/Dockerfile index 50f592dd6..c0458bdf6 100644 --- a/ms/command-executor/src/main/docker/Dockerfile +++ b/ms/command-executor/src/main/docker/Dockerfile @@ -1,6 +1,9 @@ FROM python:3.6-slim -ENV GRPC_PYTHON_VERSION 1.19.0 +ENV HTTP_PROXY ${HTTP_PROXY} +ENV HTTPS_PROXY ${HTTPS_PROXY} + +ENV GRPC_PYTHON_VERSION 1.20.0 RUN python -m pip install --upgrade pip RUN pip install grpcio==${GRPC_PYTHON_VERSION} grpcio-tools==${GRPC_PYTHON_VERSION} RUN pip install virtualenv diff --git a/ms/command-executor/src/main/python/command_executor_handler.py b/ms/command-executor/src/main/python/command_executor_handler.py index 248e44308..1fb3e2679 100644 --- a/ms/command-executor/src/main/python/command_executor_handler.py +++ b/ms/command-executor/src/main/python/command_executor_handler.py @@ -24,6 +24,8 @@ import venv import utils import proto.CommandExecutor_pb2 as CommandExecutor_pb2 +REQUIREMENTS_TXT = "requirements.txt" + class CommandExecutorHandler(): @@ -47,11 +49,11 @@ class CommandExecutorHandler(): return False f = open(self.installed, "w+") - if not self.install_packages(request, CommandExecutor_pb2.PYTHON, f, results): + if not self.install_packages(request, CommandExecutor_pb2.pip, f, results): return False f.write("\r\n") results.append("\n") - if not self.install_packages(request, CommandExecutor_pb2.ANSIBLE, f, results): + if not self.install_packages(request, CommandExecutor_pb2.ansible_galaxy, f, results): return False f.close() else: @@ -63,8 +65,8 @@ class CommandExecutorHandler(): return True def execute_command(self, request, results): - if not self.activate_venv(): - return False + # if not self.activate_venv(): + # return False try: results.append(os.popen(request.command).read()) @@ -80,12 +82,12 @@ class CommandExecutorHandler(): for package in request.packages: if package.type == type: f.write("Installed %s packages:\r\n" % CommandExecutor_pb2.PackageType.Name(type)) - for python_package in package.package: - f.write(" %s\r\n" % python_package) - if package.type == CommandExecutor_pb2.PYTHON: - success = self.install_python_packages(python_package, results) + for p in package.package: + f.write(" %s\r\n" % p) + if package.type == CommandExecutor_pb2.pip: + success = self.install_python_packages(p, results) else: - success = self.install_ansible_packages(python_package, results) + success = self.install_ansible_packages(p, results) if not success: f.close() os.remove(self.installed) @@ -95,10 +97,15 @@ class CommandExecutorHandler(): def install_python_packages(self, package, results): self.logger.info( "{} - Install Python package({}) in Python Virtual Environment".format(self.blueprint_id, package)) - command = ["pip", "install", package] + + if REQUIREMENTS_TXT == package: + command = ["pip", "install", "-r", self.venv_home + "/Environments/" + REQUIREMENTS_TXT] + else: + command = ["pip", "install", package] env = dict(os.environ) - env['https_proxy'] = os.environ['https_proxy'] + if "https_proxy" in os.environ: + env['https_proxy'] = os.environ['https_proxy'] try: results.append(subprocess.run(command, check=True, stdout=PIPE, stderr=PIPE, env=env).stdout.decode()) @@ -111,11 +118,12 @@ class CommandExecutorHandler(): def install_ansible_packages(self, package, results): self.logger.info( "{} - Install Ansible Role package({}) in Python Virtual Environment".format(self.blueprint_id, package)) - command = ["ansible-galaxy", "install", package, "-p", "Scripts/ansible/roles"] + command = ["ansible-galaxy", "install", package, "-p", self.venv_home + "/Scripts/ansible/roles"] env = dict(os.environ) - # ansible galaxy uses https_proxy environment variable, but requires it to be set with http proxy value. - env['https_proxy'] = os.environ['http_proxy'] + if "http_proxy" in os.environ: + # ansible galaxy uses https_proxy environment variable, but requires it to be set with http proxy value. + env['https_proxy'] = os.environ['http_proxy'] try: results.append(subprocess.run(command, check=True, stdout=PIPE, stderr=PIPE, env=env).stdout.decode()) diff --git a/ms/command-executor/src/main/python/proto/CommandExecutor_pb2.py b/ms/command-executor/src/main/python/proto/CommandExecutor_pb2.py index 76fc19d02..3afeb35fc 100644 --- a/ms/command-executor/src/main/python/proto/CommandExecutor_pb2.py +++ b/ms/command-executor/src/main/python/proto/CommandExecutor_pb2.py @@ -22,7 +22,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( package='org.onap.ccsdk.cds.controllerblueprints.command.api', syntax='proto3', serialized_options=_b('P\001'), - serialized_pb=_b('\n\x15\x43ommandExecutor.proto\x12\x33org.onap.ccsdk.cds.controllerblueprints.command.api\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\x8f\x02\n\x0e\x45xecutionInput\x12\x11\n\trequestId\x18\x01 \x01(\t\x12\x15\n\rcorrelationId\x18\x02 \x01(\t\x12U\n\x0bidentifiers\x18\x03 \x01(\x0b\x32@.org.onap.ccsdk.cds.controllerblueprints.command.api.Identifiers\x12\x0f\n\x07\x63ommand\x18\x04 \x01(\t\x12\x0f\n\x07timeOut\x18\x05 \x01(\x05\x12+\n\nproperties\x18\x06 \x01(\x0b\x32\x17.google.protobuf.Struct\x12-\n\ttimestamp\x18\x07 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\"\xd0\x02\n\x0fPrepareEnvInput\x12U\n\x0bidentifiers\x18\x01 \x01(\x0b\x32@.org.onap.ccsdk.cds.controllerblueprints.command.api.Identifiers\x12\x11\n\trequestId\x18\x02 \x01(\t\x12\x15\n\rcorrelationId\x18\x03 \x01(\t\x12O\n\x08packages\x18\x04 \x03(\x0b\x32=.org.onap.ccsdk.cds.controllerblueprints.command.api.Packages\x12\x0f\n\x07timeOut\x18\x05 \x01(\x05\x12+\n\nproperties\x18\x06 \x01(\x0b\x32\x17.google.protobuf.Struct\x12-\n\ttimestamp\x18\x07 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\">\n\x0bIdentifiers\x12\x15\n\rblueprintName\x18\x01 \x01(\t\x12\x18\n\x10\x62lueprintVersion\x18\x02 \x01(\t\"\xba\x01\n\x0f\x45xecutionOutput\x12\x11\n\trequestId\x18\x01 \x01(\t\x12\x10\n\x08response\x18\x02 \x01(\t\x12S\n\x06status\x18\x03 \x01(\x0e\x32\x43.org.onap.ccsdk.cds.controllerblueprints.command.api.ResponseStatus\x12-\n\ttimestamp\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\"k\n\x08Packages\x12N\n\x04type\x18\x01 \x01(\x0e\x32@.org.onap.ccsdk.cds.controllerblueprints.command.api.PackageType\x12\x0f\n\x07package\x18\x02 \x03(\t**\n\x0eResponseStatus\x12\x0b\n\x07SUCCESS\x10\x00\x12\x0b\n\x07\x46\x41ILURE\x10\x01*&\n\x0bPackageType\x12\n\n\x06PYTHON\x10\x00\x12\x0b\n\x07\x41NSIBLE\x10\x01\x32\xd1\x02\n\x16\x43ommandExecutorService\x12\x98\x01\n\nprepareEnv\x12\x44.org.onap.ccsdk.cds.controllerblueprints.command.api.PrepareEnvInput\x1a\x44.org.onap.ccsdk.cds.controllerblueprints.command.api.ExecutionOutput\x12\x9b\x01\n\x0e\x65xecuteCommand\x12\x43.org.onap.ccsdk.cds.controllerblueprints.command.api.ExecutionInput\x1a\x44.org.onap.ccsdk.cds.controllerblueprints.command.api.ExecutionOutputB\x02P\x01\x62\x06proto3') + serialized_pb=_b('\n\x15\x43ommandExecutor.proto\x12\x33org.onap.ccsdk.cds.controllerblueprints.command.api\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\x8f\x02\n\x0e\x45xecutionInput\x12\x11\n\trequestId\x18\x01 \x01(\t\x12\x15\n\rcorrelationId\x18\x02 \x01(\t\x12U\n\x0bidentifiers\x18\x03 \x01(\x0b\x32@.org.onap.ccsdk.cds.controllerblueprints.command.api.Identifiers\x12\x0f\n\x07\x63ommand\x18\x04 \x01(\t\x12\x0f\n\x07timeOut\x18\x05 \x01(\x05\x12+\n\nproperties\x18\x06 \x01(\x0b\x32\x17.google.protobuf.Struct\x12-\n\ttimestamp\x18\x07 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\"\xd0\x02\n\x0fPrepareEnvInput\x12U\n\x0bidentifiers\x18\x01 \x01(\x0b\x32@.org.onap.ccsdk.cds.controllerblueprints.command.api.Identifiers\x12\x11\n\trequestId\x18\x02 \x01(\t\x12\x15\n\rcorrelationId\x18\x03 \x01(\t\x12O\n\x08packages\x18\x04 \x03(\x0b\x32=.org.onap.ccsdk.cds.controllerblueprints.command.api.Packages\x12\x0f\n\x07timeOut\x18\x05 \x01(\x05\x12+\n\nproperties\x18\x06 \x01(\x0b\x32\x17.google.protobuf.Struct\x12-\n\ttimestamp\x18\x07 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\">\n\x0bIdentifiers\x12\x15\n\rblueprintName\x18\x01 \x01(\t\x12\x18\n\x10\x62lueprintVersion\x18\x02 \x01(\t\"\xba\x01\n\x0f\x45xecutionOutput\x12\x11\n\trequestId\x18\x01 \x01(\t\x12\x10\n\x08response\x18\x02 \x01(\t\x12S\n\x06status\x18\x03 \x01(\x0e\x32\x43.org.onap.ccsdk.cds.controllerblueprints.command.api.ResponseStatus\x12-\n\ttimestamp\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\"k\n\x08Packages\x12N\n\x04type\x18\x01 \x01(\x0e\x32@.org.onap.ccsdk.cds.controllerblueprints.command.api.PackageType\x12\x0f\n\x07package\x18\x02 \x03(\t**\n\x0eResponseStatus\x12\x0b\n\x07SUCCESS\x10\x00\x12\x0b\n\x07\x46\x41ILURE\x10\x01**\n\x0bPackageType\x12\x07\n\x03pip\x10\x00\x12\x12\n\x0e\x61nsible_galaxy\x10\x01\x32\xd1\x02\n\x16\x43ommandExecutorService\x12\x98\x01\n\nprepareEnv\x12\x44.org.onap.ccsdk.cds.controllerblueprints.command.api.PrepareEnvInput\x1a\x44.org.onap.ccsdk.cds.controllerblueprints.command.api.ExecutionOutput\x12\x9b\x01\n\x0e\x65xecuteCommand\x12\x43.org.onap.ccsdk.cds.controllerblueprints.command.api.ExecutionInput\x1a\x44.org.onap.ccsdk.cds.controllerblueprints.command.api.ExecutionOutputB\x02P\x01\x62\x06proto3') , dependencies=[google_dot_protobuf_dot_struct__pb2.DESCRIPTOR,google_dot_protobuf_dot_timestamp__pb2.DESCRIPTOR,]) @@ -56,26 +56,26 @@ _PACKAGETYPE = _descriptor.EnumDescriptor( file=DESCRIPTOR, values=[ _descriptor.EnumValueDescriptor( - name='PYTHON', index=0, number=0, + name='pip', index=0, number=0, serialized_options=None, type=None), _descriptor.EnumValueDescriptor( - name='ANSIBLE', index=1, number=1, + name='ansible_galaxy', index=1, number=1, serialized_options=None, type=None), ], containing_type=None, serialized_options=None, serialized_start=1160, - serialized_end=1198, + serialized_end=1202, ) _sym_db.RegisterEnumDescriptor(_PACKAGETYPE) PackageType = enum_type_wrapper.EnumTypeWrapper(_PACKAGETYPE) SUCCESS = 0 FAILURE = 1 -PYTHON = 0 -ANSIBLE = 1 +pip = 0 +ansible_galaxy = 1 @@ -415,8 +415,8 @@ _COMMANDEXECUTORSERVICE = _descriptor.ServiceDescriptor( file=DESCRIPTOR, index=0, serialized_options=None, - serialized_start=1201, - serialized_end=1538, + serialized_start=1205, + serialized_end=1542, methods=[ _descriptor.MethodDescriptor( name='prepareEnv', diff --git a/ms/command-executor/src/main/python/server.py b/ms/command-executor/src/main/python/server.py index de620474b..453d751b2 100644 --- a/ms/command-executor/src/main/python/server.py +++ b/ms/command-executor/src/main/python/server.py @@ -1,3 +1,4 @@ + #!/usr/bin/python # |