Self Service Integration of IaaS Service

JSDN Self-Service integration provides the right tools for anyone who wants to on-board IaaS Services such as Cloud Stack in JSDN and can be managed through JSDN console.

A quick overview on how to on-board a IaaS Service:
  1. On Demand Provider (for Cloud Stack) should be created and the IaaS Service (Cloud Stack service subscription) should be configured in the JSDN.
  2. The service should be resold in the store. So that the users can consume.
  3. Ansible Script for Cloud Stack should be available and executed (in the database) in JSDN. - This section explains how the Ansible script structure should be and what query should be executed in db.
  4. IaaS Adapter skeleton.
  5. Few RESTApi (JDFs RESTApi’s) used to execute the Ansible script through the adapter.

Add On Demand Provider in JSDN

Let’s assume that you already created an On Demand Provider for Cloud Stack service in JSDN Marketplace. If not, see how to add an On Demand Provider in JSDN.

Integrate IaaS Service using Ansible

Now, to manage the service (Cloud Stack) in JSDN, there should be some communication between the ISV and JSDN. These communications are basically done through the Adapters using Ansible scripts. These Ansible Scripts should be in a proper structure and executed in the database. See cloud stack related Ansible Scripts that are supported in JSDN.
Note: Ansible should be installed before executing any Ansible scripts. Refer to Ansible Installation for more information.
Note: Check whether python is installed or not. If not, install python and execute the following command:
pip install cs
Note: Create a file with name “.cloudstack.ini” in jboss_home and add the following contents: [cloudstack]

timeout = 30

In Java.security file add the following line jdk.tls.disabledAlgorithms=DHE

File Path : /d01/jdk1.7.0_51/jre/lib/security/java.security

The below script is an example for creating a network. Click here for more scripts.
- hosts: localhost
  connection: local
  gather_facts: no 
  vars:
   api_region: ""
   name: ""
   network_offering: ""
   zone: ""
  tasks:
  - name: create a network
    cs_network:
     api_url: https://compute.rnp.br/client/api
     api_key: "{{ accessKey }}"
     api_secret: "{{ secretKey }}"
     api_region: "{{ api_region }}"
     api_http_method: post
     state: present
     poll_async: yes
     name: "{{ name }}"
     network_offering: "{{ network_offering }}"
     zone: "{{ zone }}"
    delegate_to: localhost

Request Parameters for Create a Network Ansible Script. For more information refer to Cloud Stack Parameters.

api_url

URL of the CloudStack API e.g. https://cloud.example.com/client/api.

If not given, the CLOUDSTACK_ENDPOINT env variable is considered.

As the last option, the value is taken from the config file.

api_key

API key of the CloudStack API.

If not given, the CLOUDSTACK_KEY env variable is considered.

As the last option, the value is taken from the ini config file.

api_secret

Secret key of the CloudStack API.

If not set, the CLOUDSTACK_SECRET env variable is considered.

As the last option, the value is taken from the ini config file

api_region

Name of the ini section in the cloustack.ini file.

If not given, the CLOUDSTACK_REGION env variable is considered.

api_http_method

HTTP method used to query the API endpoint.

If not given, the CLOUDSTACK_METHOD env variable is considered.

As the last option, the value is taken from the ini config file.

Fallback value is get if not specified.

state State of the network.
poll_async Poll async jobs until job has finished.
name Name (case sensitive) of the network. This is a mandatory parameter.
network_offering

Name of the offering for the network.

Required if state=present.

zone

Name of the zone in which the network should be deployed.

If not set, default zone is used.

How to add Ansible Scripts in JSDN?

Insert the Ansible script in Database, execute the below commands to insert the script.
Note: The UI Metadata quarries are specific to particular Ansible scripts.
INSERT INTO jci_provider_resource_template (
provider_resource_template_id,resource_template_type_id,provider_code,actor_id,resource_type_id,template_action_code,
template_name,template_content,status,created_date,created_by,updated_date,updated_by,script_input_json)
VALUES ((select MAX(provider_resource_template_id)+1 from jci_provider_resource_template),100000,'cloudstack',1000,29,'vm','createNetwork','- hosts: localhost
  connection: local
  gather_facts: no 
  vars:
   api_region: ""
   name: ""
   network_offering: ""
   zone: ""
  tasks:
  - name: create a network
    cs_network:
     api_url: https://compute.rnp.br/client/api
     api_key: "{{ accessKey }}"
     api_secret: "{{ secretKey }}"
     api_region: "{{ api_region }}"
     api_http_method: post
     state: present
     poll_async: yes
     name: "{{ name }}"
     network_offering: "{{ network_offering }}"
     zone: "{{ zone }}"
    delegate_to: localhost','A',now(),'1002',now(),'1002',null);

Please insert the following UI Metadata quarries for each Ansible Scripts:

To displays the help text.
insert into jcp_ui_metadata values('jsdn.resourcescript.help.network_offering',1000,'en_US','M','Enter the Network Offering');
To specify the validation condition.
insert into jcp_ui_metadata values('jsdn.resourcescript.validation.network_offering',1000,'en_US','M','.*');
To display the Label name.
insert into jcp_ui_metadata values('jsdn.resourcescript.lable.network_offering',1000,'en_US','M','Network Offering');
To display the validation message.
insert into jcp_ui_metadata values('jsdn.resourcescript.validationmsg.network_offering',1000,'en_US','M','Enter the correct Network Offering');
To display the script name.
insert into jcp_ui_metadata values('jsdn.store.resourcescript.template.createNetwork',1000,'en_US','M','Create Network');

IaaS Adapter Skeleton

Developing an Adapter

Before developing an Adapter, you must have the skeleton/format of the adapter. The image below shows how an Skeleton/format of the adapter looks like for creating a network.

For each of the actions (as mentioned below), the system will call a specific executor of the adapter for example Create a Network, where the code (executing the REST API (JDF REST APIs) for executing an Ansible Script (create a network) should be written. Once the Anisble Script respond the adapter sends the status back to JSDN. If it is successful, the action is considered as completed.

Life Cycle of VM (Actions)

  1. Launching a VM/Create a VM
  2. Start/Stop VM
  3. Reboot
  4. Terminate
  5. Create a Network
  6. Get Static Public IP
  7. Create Image
  8. Security Group
  9. Monitoring
    1. Deploy the adapter in JSDN (tomcat server).
    2. Configure the Adapter URL in the service.
Note: After inserting the scripts in database, login to your store account as a customer administrator and navigate to Manage > Resource Scripts page. Click Add Scripts icon, select Cloud Stack from the drop-down list, resource type as VM, template as “Create Network”. Click Save.

Using RestAPIs to execute Ansible Scripts

JDF is a very simple rest api for executing Ansible ad-hoc-commands. Here is a sample script to execute, you just need to modify the variables as per the Ansible script that you are going to execute.

To execute the script run "executeScript" command.

Request: (Header)

POST api/v2/resource-script/executeScript 
HTTP/1.1
Content-type: application/json 
Accept: application/json 
X-Auth-Token: v92jndh7saL1ZkCu0TrgkL3v347IQ31q 
proxy-store:storeCompanyAcronym 
proxy-end-customer:customerCompanyAcronym
Request Body [JSON]:
{ "executeScript":{ 
    "provider-code":"cloudstack", 
    "template-name":"createNetwork", 
    "template-code":"vm", 
    "script-type":"ansible", 
    "script-name":"createNetwork", 
    "tenant-active-state":"Y", 
    "parameters":{ 
        "iam_role_name":"<name of the script>", 
        "policy_name":"AmazonEC2ContainerServiceforEC2Role" 
    }, 
    "async":false 
    } 
} 

Response:

{
    "resource-script-response": {
        "response-code": "SUCCESS",
        "response-description": [
            "Execute script initiated successfully."
        ]
    }
}
To get the resource script status, execute "getResourceScriptStatus" command:

Request:

GET api/v2/resource-script/getResourceScriptStatus/{script-name} 
HTTP/1.1 
Content-type: application/json 
Accept: application/json 
X-Auth-Token: v92jndh7saL1ZkCu0TrgkL3v347IQ31q 
proxy-store:storeCompanyAcronym 
proxy-end-customer:customerCompanyAcronym
Response: The below response confirm the status of your request.
{"ExecuteScriptStatusResponse":{"resourceScript":{"scriptId":100000,"actorId":4015222,"providerResourceTemplate":{"providerTemplateId":100000,"resourceTemplateType":{"templateTypeId":100000,"templateTypeCode":"rds"},"providerCode":"aws","actorId":1000,"resourceTypeId":21,"providerName":"Amazon
    Web 
        Services","templatecode":"rds","templateName":"launchdb","templateContent":"-
    hosts: localhost\n 
          vars:\n    vpc_name: \"\"\n    private_subnet_name:  \"\"\n   
    private_subnet_name2: \"\"\n    rds_sg_cidr_ip: \"\"\n     rds_db_instance_name:
    \"\"\n   
        rds_db_name: \"\"\n    rds_db_username:
          \"\"\n    rds_db_password:
    \"\"\n    
        aws_region: \"\"\n    rds_sg_name:
          \"\"\n    rds_sg_desc:
    \"\"\n    
        db_engine: \"\"\n    size: \"\"\n    instance_type: \"\"\n   
    publicly_accessible: \"true\"\n    backup_retention: \"0\"\n  tasks:  \n  -
    ec2_vpc_net_facts:\n     filters:\n      
        \"tag:Name\": \"{{  vpc_name
        }}\"
    \n    
        aws_access_key: \"{{ accessKey }}\"\n     
        aws_secret_key: \"{{ secretKey
    }}\"\n    
        region: \"{{ aws_region  }}\"\n    register: vpc_net_facts\n\n  - name:
    Gather VPC facts\n    ec2_vpc_subnet_facts:\n    
        aws_access_key: \"{{ accessKey
    }}\"\n     
        aws_secret_key: \"{{ secretKey }}\"\n    
        region: \"{{ aws_region
    }}\"\n    
          filters:\n      \"tag:Name\": \"{{
          private_subnet_name  }}\"\n   
    register: vpc_facts \n  \n  - name:
        Gather VPC facts\n    ec2_vpc_subnet_facts:\n    
    aws_access_key: \"{{ accessKey 
          }}\"\n     aws_secret_key: \"{{
        secretKey }}\"\n    
    region: \"{{ 
        aws_region }}\"\n     filters:\n      \"tag:Name\": \"{{
    private_subnet_name2 }}\"\n    register: vpc_subnet_facts\n \n  - name:   Set
        subnets in
    variable
          \n    set_fact:\n       rds_subnet_id:          
        \"{{
    vpc_facts.subnets[0].id }}\"\n\n  -  name:               Set RDS endpoint
        in
    variable\n   
          set_fact:\n       rds_subnet_id2:           \"{{
    vpc_subnet_facts.subnets[0].id }}\"\n\n  - name: Create DB Subnet Group \n   
    rds_subnet_group:\n     state:  present\n     aws_access_key: \"{{ accessKey
    }}\"\n    
          aws_secret_key:  \"{{ secretKey
          }}\"\n     region: \"{{ aws_region
        }}\"
    \n    
          name:  \"default1-{{
        vpc_facts.subnets[0].vpc_id }}\"\n    
        description: My DB
    Subnet Group\n     subnets:\n      - \"{{  rds_subnet_id }}\"\n      - \"{{
    rds_subnet_id2 }}\" \n    register: 
          RDSSubnetGroup\n\n  - name: Create RDS
        security
    group\n    
          ec2_group:\n     aws_access_key: \"{{
        accessKey }}\"\n    
    aws_secret_key: \"{{ secretKey }}\"\n     name: \"{{ rds_sg_name }}\"\n    
    description: 
        \"{{ rds_sg_desc }}\"\n     region: \"{{
        aws_region }}\"\n     vpc_id:
    \"{{ vpc_facts.subnets[0].vpc_id }}\"\n     state: present\n      rules:\n     
      -
    proto: tcp\n        from_port: 5432\n       
        to_port: 5432\n       
    cidr_ip: \"{{ rds_sg_cidr_ip }}\"\n    register: 
          RDSGroup\n\n  - name: Create
        PostgreSQL
    on RDS\n   
          rds:\n        aws_access_key: \"{{
        accessKey }}\"\n      
    aws_secret_key: \"{{  secretKey }}\"\n       command:
          create\n      
    instance_name: \"{{ rds_db_instance_name
          }}\"\n       region: \"{{
        aws_region
    }}\"\n      
        subnet: \"default1-{{ vpc_facts.subnets[0].vpc_id }}\"  \n     
        
    vpc_security_groups: \"{{ RDSGroup.group_id
          }}\"\n        db_engine: \"{{
        db_engine
    }}\"\n      
        size: \"{{ size }}\"\n      
        instance_type: \"{{ instance_type
    }}\"\n      
        db_name: \"{{ rds_db_name  }}\"\n       username: \"{{
    rds_db_username }}\"\n       password: \"{{  rds_db_password }}\"\n     
        
    publicly_accessible: \"{{  publicly_accessible }}\"\n       backup_retention: \"{{
    backup_retention }}\"\n       wait: yes\n       wait_timeout: 600\n     
        
    tags:\n 
                   Environment:
          Development\n          
        Application:
    JSDN\n   
        register:
   
        rds\n\n","status":"A","createdBy":{"actorId":1002,"company":{"actorId":1000,"companyName":"jamcracker","companyShortName":"jamcracker","marketpalceId":1000,"status":"A"},"loginName":"root","firstName":"System","lastName":"User","email":"admin@newCompany.com","status":"A","primaryContact":true},"updatedBy":{"actorId":1002,"company":{"actorId":1000,"companyName":"jamcracker","companyShortName":"jamcracker","marketpalceId":1000,"status":"A"},"loginName":"root","firstName":"System","lastName":"User","email":"admin@newCompany.com","status":"A","primaryContact":true},"creationDate":1512498600000,"updateDate":1512498600000,"scriptInputJson":"{\"optional\":[{\"help\":\"jsdn.resourcescript.help.publicly_accessible\",\"validation\":\"jsdn.resourcescript.validation.publicly_accessible\",\"value\":[{\"value\":\"true\",\"key\":\"true\"},{\"value\":\"false\",\"key\":\"false\"}],\"label\":\"jsdn.resourcescript.lable.publicly_accessible\",\"key\":\"publicly_accessible\",\"validationmsg\":\"jsdn.resourcescript.validationmsg.publicly_accessible\"},{\"help\":\"jsdn.resourcescript.help.backup_retention\",\"validation\":\"jsdn.resourcescript.validation.backup_retention\",\"value\":\"0\",\"label\":\"jsdn.resourcescript.lable.backup_retention\",\"key\":\"backup_retention\",\"validationmsg\":\"jsdn.resourcescript.validationmsg.backup_retention\"}],\"layout\":\"double\",\"required\":[{\"help\":\"jsdn.resourcescript.help.aws_region\",\"validation\":\"jsdn.resourcescript.validation.aws_region\",\"value\":\"\",\"label\":\"jsdn.resourcescript.lable.aws_region\",\"key\":\"aws_region\",\"validationmsg\":\"jsdn.resourcescript.validationmsg.aws_region\"},{\"help\":\"jsdn.resourcescript.help.vpc_name\",\"validation\":\"jsdn.resourcescript.validation.vpc_name\",\"value\":\"\",\"label\":\"jsdn.resourcescript.lable.vpc_name\",\"key\":\"vpc_name\",\"validationmsg\":\"jsdn.resourcescript.validationmsg.vpc_name\"},{\"help\":\"jsdn.resourcescript.help.instance_type\",\"validation\":\"jsdn.resourcescript.validation.instance_type\",\"value\":\"\",\"label\":\"jsdn.resourcescript.lable.instance_type\",\"key\":\"instance_type\",\"validationmsg\":\"jsdn.resourcescript.validationmsg.instance_type\"},{\"help\":\"jsdn.resourcescript.help.rds_db_instance_name\",\"validation\":\"jsdn.resourcescript.validation.rds_db_instance_name\",\"value\":\"\",\"label\":\"jsdn.resourcescript.lable.rds_db_instance_name\",\"key\":\"rds_db_instance_name\",\"validationmsg\":\"jsdn.resourcescript.validationmsg.rds_db_instance_name\"},{\"help\":\"jsdn.resourcescript.help.rds_db_username\",\"validation\":\"jsdn.resourcescript.validation.rds_db_username\",\"value\":\"\",\"label\":\"jsdn.resourcescript.lable.rds_db_username\",\"key\":\"rds_db_username\",\"validationmsg\":\"jsdn.resourcescript.validationmsg.rds_db_username\"},{\"help\":\"jsdn.resourcescript.help.rds_sg_desc\",\"validation\":\"jsdn.resourcescript.validation.rds_sg_desc\",\"value\":\"\",\"label\":\"jsdn.resourcescript.lable.rds_sg_desc\",\"key\":\"rds_sg_desc\",\"validationmsg\":\"jsdn.resourcescript.validationmsg.rds_sg_desc\"},{\"help\":\"jsdn.resourcescript.help.size\",\"validation\":\"jsdn.resourcescript.validation.size\",\"value\":\"\",\"label\":\"jsdn.resourcescript.lable.size\",\"key\":\"size\",\"validationmsg\":\"jsdn.resourcescript.validationmsg.size\"},{\"help\":\"jsdn.resourcescript.help.private_subnet_name2\",\"validation\":\"jsdn.resourcescript.validation.private_subnet_name2\",\"value\":\"\",\"label\":\"jsdn.resourcescript.lable.private_subnet_name2\",\"key\":\"private_subnet_name2\",\"validationmsg\":\"jsdn.resourcescript.validationmsg.private_subnet_name2\"},{\"help\":\"jsdn.resourcescript.help.rds_sg_name\",\"validation\":\"jsdn.resourcescript.validation.rds_sg_name\",\"value\":\"\",\"label\":\"jsdn.resourcescript.lable.rds_sg_name\",\"key\":\"rds_sg_name\",\"validationmsg\":\"jsdn.resourcescript.validationmsg.rds_sg_name\"},{\"help\":\"jsdn.resourcescript.help.rds_db_name\",\"validation\":\"jsdn.resourcescript.validation.rds_db_name\",\"value\":\"\",\"label\":\"jsdn.resourcescript.lable.rds_db_name\",\"key\":\"rds_db_name\",\"validationmsg\":\"jsdn.resourcescript.validationmsg.rds_db_name\"},{\"help\":\"jsdn.resourcescript.help.private_subnet_name\",\"validation\":\"jsdn.resourcescript.validation.private_subnet_name\",\"value\":\"\",\"label\":\"jsdn.resourcescript.lable.private_subnet_name\",\"key\":\"private_subnet_name\",\"validationmsg\":\"jsdn.resourcescript.validationmsg.private_subnet_name\"},{\"help\":\"jsdn.resourcescript.help.rds_sg_cidr_ip\",\"validation\":\"jsdn.resourcescript.validation.rds_sg_cidr_ip\",\"value\":\"\",\"label\":\"jsdn.resourcescript.lable.rds_sg_cidr_ip\",\"key\":\"rds_sg_cidr_ip\",\"validationmsg\":\"jsdn.resourcescript.validationmsg.rds_sg_cidr_ip\"},{\"help\":\"jsdn.resourcescript.help.db_engine\",\"validation\":\"jsdn.resourcescript.validation.db_engine\",\"value\":\"\",\"label\":\"jsdn.resourcescript.lable.db_engine\",\"key\":\"db_engine\",\"validationmsg\":\"jsdn.resourcescript.validationmsg.db_engine\"},{\"help\":\"jsdn.resourcescript.help.rds_db_password\",\"validation\":\"jsdn.resourcescript.validation.rds_db_password\",\"value\":\"\",\"label\":\"jsdn.resourcescript.lable.rds_db_password\",\"key\":\"rds_db_password\",\"validationmsg\":\"jsdn.resourcescript.validationmsg.rds_db_password\"}]}"},"scriptName":"test1","scriptContent":"Launch
    DB
   
        Instance","status":"C","createdDate":1512564919928,"updatedDate":1512568950605,"createdBy":{"actorId":4015223,"company":{"actorId":4015222,"companyName":"mntstore","companyShortName":"mntstore","parentCompanyId":4015220,"marketpalceId":4015220,"status":"A"},"loginName":"mntstore.gmail.com","firstName":"mntstore","lastName":"mntstore","email":"mntstore@gmail.com","status":"A","primaryContact":true},"updatedBy":{"actorId":4015223,"company":{"actorId":4015222,"companyName":"mntstore","companyShortName":"mntstore","parentCompanyId":4015220,"marketpalceId":4015220,"status":"A"},"loginName":"mntstore.gmail.com","firstName":"mntstore","lastName":"mntstore","email":"mntstore@gmail.com","status":"A","primaryContact":true}},"resourceScriptLog":{"scriptLogId":100001,"resourceScriptId":100000,"scriptRequest":"Launch
    DB Instance","scriptResponse":"ansible-playbook
          2.4.1.0\n  config file =  None\n 
    configured module search path = 
        [u'/d01/jboss-as-7.1.1.Final/.ansible/plugins/modules',
    u'/usr/share/ansible/plugins/modules']\n  ansible python module location  =
    /usr/lib/python2.7/site-packages/ansible\n  executable location = /bin/ansible-playbook\n
        
    python version = 2.7.5  (default, Nov  6
        2016, 00:28:07) [GCC 4.8.5 20150623 (Red Hat
    4.8.5-11)]\nNo config file found;
        using
   
        defaults\n","isvResourceId":"NA","status":"C","createdDate":1512568950664,"createdBy":{"actorId":4015223,"company":{"actorId":4015222,"companyName":"mntstore","companyShortName":"mntstore","parentCompanyId":4015220,"marketpalceId":4015220,"status":"A"},"loginName":"mntstore.gmail.com","firstName":"mntstore","lastName":"mntstore","email":"mntstore@gmail.com","status":"A","primaryContact":true},"updatedDate":1512568950664,"updatedBy":{"actorId":4015223,"company":{"actorId":4015222,"companyName":"mntstore","companyShortName":"mntstore","parentCompanyId":4015220,"marketpalceId":4015220,"status":"A"},"loginName":"mntstore.gmail.com","firstName":"mntstore","lastName":"mntstore","email":"mntstore@gmail.com","status":"A","primaryContact":true}}}}