From 6bfbe0d748ddb9488c95e9c3fa59319b746cae67 Mon Sep 17 00:00:00 2001 From: Niculut <laurentiu.niculut@hpe.com> Date: Wed, 31 May 2023 18:35:43 +0200 Subject: [PATCH] first y3 public release --- .gitignore | 5 +- .gitlab-ci.yml | 66 +++ .gitmodules | 6 + Dockerfile | 5 +- README.md | 6 +- api/InfrastructureTemplateController.py | 67 ++- controller/Orchestrator.py | 72 ++- doc/KR-3.feature | 39 ++ doc/SIMPA_dolmv3.0/nio3.doml | 147 ++++++ doc/SIMPA_dolmv3.0/nio3.domlx | 58 +++ .../simpa_v2.2.0-1.doml | 150 ++++++ doc/SIMPA_example_domlv2.2/simpa_v2.2.domlx | 53 +++ doc/ericsson/uc3.doml | 370 +++++++++++++++ doc/ericsson/uc3.domlx | 248 ++++++++++ .../nginx_example_v2.0.doml | 98 ++++ .../nginx_example_v2.0.domlx | 49 ++ .../nginx_example_v2.1/nginx_aws.doml | 0 .../nginx_example_v2.1/nginx_openstack.doml | 98 ++++ .../nginx_example_v2.1/nginx_openstack.domlx | 49 ++ .../nginx_example_v2.2/nginx_openstack.doml | 98 ++++ .../nginx_example_v2.2/nginx_openstack.domlx | 49 ++ .../nginx_openstack/config.yaml | 6 + .../nginx_openstack/nginx/config.yaml | 8 + .../nginx_openstack/nginx/inventory.j2 | 9 + .../nginx_openstack/nginx/main.yml | 44 ++ .../nginx_openstack/nginx/ssh_key.j2 | 1 + .../piacere_monitoring/ansible.cfg | 0 .../ansible_requirements.yml | 4 +- .../piacere_monitoring/config.yaml | 8 + .../piacere_monitoring/hosts.yaml | 0 .../install_playbook_requirements.sh | 0 .../piacere_monitoring/inventory.j2 | 0 .../piacere_monitoring/main.yml | 0 .../piacere_monitoring/run-playbook.sh | 0 .../piacere_monitoring/site.yaml | 0 .../piacere_monitoring/site_requirements.yaml | 0 .../piacere_monitoring/ssh_key.j2 | 1 + .../piacere_monitoring/vars/main.yaml | 0 .../nginx_openstack/terraform/config.yaml | 16 + .../nginx_openstack/terraform/main.tf | 148 ++++++ .../nginx_openstack/terraform/output.tf | 14 + .../nginx_paper_domlv1.0/nginx_paper.doml | 108 +++++ .../nginx_paper_domlv1.0/nginx_paper.domlx | 50 ++ doc/posidonia/posidonia.doml | 0 doc/posidonia/posidoniav2.2.2.doml | 268 +++++++++++ doc/posidonia/posidoniav2.2.2.domlx | 107 +++++ doc/scenario1/scenario1.doml | 88 ++++ doc/scenario1/scenario1.domlx | 36 ++ doc/scenario1/scenario1.png | Bin 0 -> 77408 bytes doc/scenario1/scenario1_openstack.png | Bin 0 -> 76292 bytes doc/scenario2/scenario2.doml | 112 +++++ doc/scenario2/scenario2.domlx | 47 ++ doc/scenario3/scenario3.doml | 128 ++++++ doc/scenario3/scenario3.domlx | 55 +++ doc/wordpress_example/wordpress.doml | 121 +++++ doc/wordpress_example/wordpress.domlx | 56 +++ doc/wordpress_example/wordpress/config.yaml | 7 + .../wordpress/mysql/config.yaml | 8 + .../wordpress/mysql/inventory.j2 | 9 + .../wordpress/mysql/main.yml | 2 + .../wordpress/mysql/ssh_key.j2 | 1 + .../wordpress/piacere_monitoring}/ansible.cfg | 0 .../ansible_requirements.yml | 8 + .../wordpress/piacere_monitoring/config.yaml | 8 + .../wordpress/piacere_monitoring}/hosts.yaml | 0 .../install_playbook_requirements.sh | 0 .../wordpress/piacere_monitoring/inventory.j2 | 9 + .../wordpress/piacere_monitoring/main.yml | 22 + .../piacere_monitoring}/run-playbook.sh | 0 .../wordpress/piacere_monitoring}/site.yaml | 0 .../site_requirements.yaml | 0 .../wordpress/piacere_monitoring/ssh_key.j2 | 1 + .../piacere_monitoring}/vars/main.yaml | 0 .../wordpress/terraform/config.yaml | 20 + .../wordpress/terraform/main.tf | 131 ++++++ .../wordpress/terraform/output.tf | 28 ++ .../wordpress/wordpress/config.yaml | 8 + .../wordpress/wordpress/inventory.j2 | 9 + .../wordpress/wordpress/main.yml | 4 +- .../wordpress/wordpress/ssh_key.j2 | 1 + external-plugins.properties | 2 + icgparser/DomlParserUtilities.py | 130 +++++- .../IntermediateRepresentationUtility.py | 46 +- icgparser/ModelParser.py | 147 ++++-- icgparser/ModelResourcesUtilities.py | 84 ++++ icgparser/PiacereInternalToolsIntegrator.py | 85 +++- icgparser/doml/v1/nginx-openstack_v1.domlx | 83 ++-- icgparser/doml/v1/posidonia_example.domlx | 133 ++++++ icgparser/doml/v2/doml.ecore | 423 +++++++++++++++++ icgparser/doml/v2/doml_v2.0.ecore | 390 ++++++++++++++++ icgparser/doml/v2/doml_v2.1-2.ecore | 423 +++++++++++++++++ icgparser/doml/v2/doml_v2.1.ecore | 396 ++++++++++++++++ icgparser/doml/v2/doml_v2.2.ecore | 407 +++++++++++++++++ icgparser/doml/v2/nio3.domlx | 17 + icgparser/doml/v2/posidonia_aws.domlx | 133 ++++++ icgparser/doml/v2/posidonia_openstack.domlx | 127 ++++++ icgparser/doml/v2/uc3.domlx | 116 +++++ icgparser/doml/v2/wordpress.domlx | 54 +++ icgparser/test_ModelResources.py | 27 ++ input_file_example/nginx/parameter.json | 55 --- input_file_example/wordpress/parameters.json | 77 ---- input_file_generated/ir.json | 426 ++++++++++++++---- main.py | 4 +- output_file_example/gitlab_1/config.yaml | 7 + output_file_example/gitlab_1/git/config.yaml | 11 + .../gitlab_1/portainer}/config.yaml | 2 +- .../playbooks/pma/ansible_requirements.yml | 4 +- .../ansible/config.yaml | 7 + .../ansible/inventory.j2 | 7 + .../ansible/ssh_key.j2 | 1 + .../config.yaml | 6 + .../monitoring/ansible.cfg | 7 + .../monitoring/ansible_requirements.yml | 8 + .../monitoring/config.yaml | 7 + .../monitoring/hosts.yaml | 4 + .../install_playbook_requirements.sh | 33 ++ .../monitoring/inventory.j2 | 7 + .../monitoring/main.yml | 0 .../monitoring/run-playbook.sh | 33 ++ .../monitoring/site.yaml | 30 ++ .../monitoring/site_requirements.yaml | 9 + .../monitoring/ssh_key.j2 | 1 + .../monitoring/vars/main.yaml | 27 ++ .../terraform/config.yaml | 8 + .../terraform/main.tf | 116 +++++ .../terraform/output.tf | 11 + .../nginx_openstack/config.yaml | 3 +- .../nginx_openstack/nginx/config.yaml | 4 +- .../nginx_openstack/nginx/inventory.j2 | 2 +- .../nginx_openstack/nginx/ssh_key.j2 | 3 +- .../piacere_monitoring/ssh_key.j2 | 1 - .../nginx_openstack/terraform/config.yaml | 6 +- .../nginx_openstack/terraform/main.tf | 160 +++---- .../nginx_openstack/terraform/output.tf | 12 +- .../posidonia/terraform/config.yaml | 5 - .../wordpress_azure/terraform/main.tf | 47 -- plugin/AnsiblePlugin.py | 29 +- plugin/DockerComposePlugin.py | 60 +++ plugin/PluginUtility.py | 33 +- plugin/TemplateUtils.py | 10 +- plugin/TerraformPlugin.py | 21 +- requirements.txt | 4 +- service.yml | 16 - template-location.properties | 53 ++- templates/ansible/centos/config.tpl | 35 ++ templates/ansible/centos/elasticsearch.tpl | 21 + .../ansible/centos/elasticsearch_main.tpl | 12 + templates/ansible/centos/inventory.tpl | 25 + .../centos/performance_monitoring_main.tpl | 42 ++ .../centos/security_monitoring_main.tpl | 2 + templates/ansible/centos/ssh_key.tpl | 4 + .../performance_monitoring/.gitignore | 1 + .../performance_monitoring/LICENSE | 201 +++++++++ .../performance_monitoring/README.md | 31 ++ .../ansible_requirements.yml | 2 +- .../performance_monitoring/config.yaml | 12 + .../performance_monitoring/inventory.j2 | 9 + .../performance_monitoring/inventory.txt | 2 + .../performance_monitoring/main.yml | 42 ++ .../performance_monitoring/ssh_key.j2 | 27 ++ .../performance_monitoring/vars/main.yaml | 28 ++ .../security_monitoring/README.md | 88 ++++ .../security_monitoring/build-wazuh-agent.yml | 20 + .../security_monitoring/config.yaml | 10 + .../security_monitoring/config/ossec.conf.j2 | 203 +++++++++ .../deploy-wazuh-agent.yml | 110 +++++ .../deploy-wazuh-docker-agent.yml | 34 ++ .../docker-deploy/Dockerfile | 16 + .../docker-deploy/entrypoint.sh | 21 + .../docker-deploy/ossec.conf | 203 +++++++++ .../security_monitoring/inventory.j2 | 9 + .../security_monitoring/inventory.txt | 5 + .../security_monitoring/main.yml | 1 + .../security_monitoring/ssh_key.j2 | 27 ++ .../security_monitoring/vars.yml | 13 + templates/ansible/ubuntu/config.tpl | 16 +- templates/ansible/ubuntu/elasticsearch.tpl | 21 + .../ansible/ubuntu/elasticsearch_main.tpl | 12 + templates/ansible/ubuntu/inventory.tpl | 4 +- .../ubuntu/performance_monitoring_main.tpl | 42 ++ .../ubuntu/security_monitoring_main.tpl | 2 + templates/ansible/ubuntu/ssh_key.tpl | 5 +- .../common/gaiax_self_description.yaml.tpl | 65 +++ templates/docker_compose/config.tpl | 24 + templates/docker_compose/docker_compose.tpl | 9 + templates/docker_compose/inventory.tpl | 27 ++ templates/docker_compose/main.tpl | 12 + templates/docker_compose/ssh_key.tpl | 4 + templates/terraform/aws/autoscaling_group.tpl | 19 + templates/terraform/aws/config.tpl | 29 ++ templates/terraform/aws/network.tpl | 30 +- templates/terraform/aws/port_rule.tpl | 6 +- .../terraform/aws/ssh_key.tpl | 15 +- templates/terraform/aws/temp.tpl | 0 templates/terraform/aws/virtual_machine.tpl | 13 +- .../terraform/aws/virtual_machine_out.tpl | 27 ++ templates/terraform/azure/port_rule.tpl | 37 ++ templates/terraform/google_cloud/vm.tpl | 6 +- templates/terraform/ionos/config.tpl | 28 ++ templates/terraform/ionos/datacenter.tpl | 21 + templates/terraform/ionos/init.tpl | 25 + templates/terraform/ionos/network.tpl | 22 + templates/terraform/ionos/virtual_machine.tpl | 115 +++++ .../terraform/ionos/virtual_machine_out.tpl | 27 ++ .../playbooks/pma/ansible_requirements.yml | 4 +- templates/terraform/open_stack/config.tpl | 6 +- templates/terraform/open_stack/network.tpl | 49 +- templates/terraform/open_stack/port_rule.tpl | 24 +- templates/terraform/open_stack/ssh_key.tpl | 2 +- .../terraform/open_stack/virtual_machine.tpl | 33 +- .../open_stack/virtual_machine_out.tpl | 6 +- templates/terraform/vsphere/config.tpl | 30 ++ .../terraform/vsphere/data_resources.tpl | 6 + templates/terraform/vsphere/datacenter.tpl | 13 + templates/terraform/vsphere/datastore.tpl | 8 + templates/terraform/vsphere/init.tpl | 22 + templates/terraform/vsphere/network.tpl | 13 + templates/terraform/vsphere/ssh_key.tpl | 19 + .../terraform/vsphere/virtual_machine.tpl | 77 ++++ .../terraform/vsphere/virtual_machine_out.tpl | 15 + templates/terraform/vsphere/vm_image.tpl | 8 + utility/PropertiesReaderUtility.py | 44 ++ 222 files changed, 9637 insertions(+), 705 deletions(-) create mode 100644 .gitlab-ci.yml create mode 100644 .gitmodules create mode 100644 doc/KR-3.feature create mode 100644 doc/SIMPA_dolmv3.0/nio3.doml create mode 100644 doc/SIMPA_dolmv3.0/nio3.domlx create mode 100644 doc/SIMPA_example_domlv2.2/simpa_v2.2.0-1.doml create mode 100644 doc/SIMPA_example_domlv2.2/simpa_v2.2.domlx create mode 100644 doc/ericsson/uc3.doml create mode 100644 doc/ericsson/uc3.domlx create mode 100644 doc/nginx_example/nginx_example_v2.0/nginx_example_v2.0.doml create mode 100644 doc/nginx_example/nginx_example_v2.0/nginx_example_v2.0.domlx create mode 100644 doc/nginx_example/nginx_example_v2.1/nginx_aws.doml create mode 100644 doc/nginx_example/nginx_example_v2.1/nginx_openstack.doml create mode 100644 doc/nginx_example/nginx_example_v2.1/nginx_openstack.domlx create mode 100644 doc/nginx_example/nginx_example_v2.2/nginx_openstack.doml create mode 100644 doc/nginx_example/nginx_example_v2.2/nginx_openstack.domlx create mode 100644 doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/config.yaml create mode 100644 doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/nginx/config.yaml create mode 100644 doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/nginx/inventory.j2 create mode 100644 doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/nginx/main.yml create mode 100644 doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/nginx/ssh_key.j2 rename {output_files_generated => doc/nginx_example/nginx_paper_domlv1.0}/nginx_openstack/piacere_monitoring/ansible.cfg (100%) rename {output_files_generated => doc/nginx_example/nginx_paper_domlv1.0}/nginx_openstack/piacere_monitoring/ansible_requirements.yml (81%) create mode 100644 doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/piacere_monitoring/config.yaml rename {output_files_generated => doc/nginx_example/nginx_paper_domlv1.0}/nginx_openstack/piacere_monitoring/hosts.yaml (100%) rename {output_files_generated => doc/nginx_example/nginx_paper_domlv1.0}/nginx_openstack/piacere_monitoring/install_playbook_requirements.sh (100%) rename {output_files_generated => doc/nginx_example/nginx_paper_domlv1.0}/nginx_openstack/piacere_monitoring/inventory.j2 (100%) rename {output_files_generated => doc/nginx_example/nginx_paper_domlv1.0}/nginx_openstack/piacere_monitoring/main.yml (100%) rename {output_files_generated => doc/nginx_example/nginx_paper_domlv1.0}/nginx_openstack/piacere_monitoring/run-playbook.sh (100%) rename {output_files_generated => doc/nginx_example/nginx_paper_domlv1.0}/nginx_openstack/piacere_monitoring/site.yaml (100%) rename {output_files_generated => doc/nginx_example/nginx_paper_domlv1.0}/nginx_openstack/piacere_monitoring/site_requirements.yaml (100%) create mode 100644 doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/piacere_monitoring/ssh_key.j2 rename {output_files_generated => doc/nginx_example/nginx_paper_domlv1.0}/nginx_openstack/piacere_monitoring/vars/main.yaml (100%) create mode 100644 doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/terraform/config.yaml create mode 100644 doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/terraform/main.tf create mode 100644 doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/terraform/output.tf create mode 100644 doc/nginx_example/nginx_paper_domlv1.0/nginx_paper.doml create mode 100644 doc/nginx_example/nginx_paper_domlv1.0/nginx_paper.domlx create mode 100644 doc/posidonia/posidonia.doml create mode 100644 doc/posidonia/posidoniav2.2.2.doml create mode 100644 doc/posidonia/posidoniav2.2.2.domlx create mode 100644 doc/scenario1/scenario1.doml create mode 100644 doc/scenario1/scenario1.domlx create mode 100644 doc/scenario1/scenario1.png create mode 100644 doc/scenario1/scenario1_openstack.png create mode 100644 doc/scenario2/scenario2.doml create mode 100644 doc/scenario2/scenario2.domlx create mode 100644 doc/scenario3/scenario3.doml create mode 100644 doc/scenario3/scenario3.domlx create mode 100644 doc/wordpress_example/wordpress.doml create mode 100644 doc/wordpress_example/wordpress.domlx create mode 100644 doc/wordpress_example/wordpress/config.yaml create mode 100644 doc/wordpress_example/wordpress/mysql/config.yaml create mode 100644 doc/wordpress_example/wordpress/mysql/inventory.j2 rename output_files_generated/wordpress_azure/ansible/mysql.play => doc/wordpress_example/wordpress/mysql/main.yml (99%) create mode 100644 doc/wordpress_example/wordpress/mysql/ssh_key.j2 rename {templates/ansible/ubuntu/monitoring => doc/wordpress_example/wordpress/piacere_monitoring}/ansible.cfg (100%) create mode 100644 doc/wordpress_example/wordpress/piacere_monitoring/ansible_requirements.yml create mode 100644 doc/wordpress_example/wordpress/piacere_monitoring/config.yaml rename {templates/ansible/ubuntu/monitoring => doc/wordpress_example/wordpress/piacere_monitoring}/hosts.yaml (100%) rename {templates/ansible/ubuntu/monitoring => doc/wordpress_example/wordpress/piacere_monitoring}/install_playbook_requirements.sh (100%) create mode 100644 doc/wordpress_example/wordpress/piacere_monitoring/inventory.j2 create mode 100644 doc/wordpress_example/wordpress/piacere_monitoring/main.yml rename {templates/ansible/ubuntu/monitoring => doc/wordpress_example/wordpress/piacere_monitoring}/run-playbook.sh (100%) rename {templates/ansible/ubuntu/monitoring => doc/wordpress_example/wordpress/piacere_monitoring}/site.yaml (100%) rename {templates/ansible/ubuntu/monitoring => doc/wordpress_example/wordpress/piacere_monitoring}/site_requirements.yaml (100%) create mode 100644 doc/wordpress_example/wordpress/piacere_monitoring/ssh_key.j2 rename {templates/ansible/ubuntu/monitoring => doc/wordpress_example/wordpress/piacere_monitoring}/vars/main.yaml (100%) create mode 100644 doc/wordpress_example/wordpress/terraform/config.yaml create mode 100644 doc/wordpress_example/wordpress/terraform/main.tf create mode 100644 doc/wordpress_example/wordpress/terraform/output.tf create mode 100644 doc/wordpress_example/wordpress/wordpress/config.yaml create mode 100644 doc/wordpress_example/wordpress/wordpress/inventory.j2 rename output_files_generated/wordpress_azure/ansible/wordpress.play => doc/wordpress_example/wordpress/wordpress/main.yml (97%) create mode 100644 doc/wordpress_example/wordpress/wordpress/ssh_key.j2 create mode 100644 external-plugins.properties create mode 100644 icgparser/ModelResourcesUtilities.py create mode 100644 icgparser/doml/v1/posidonia_example.domlx create mode 100644 icgparser/doml/v2/doml.ecore create mode 100644 icgparser/doml/v2/doml_v2.0.ecore create mode 100644 icgparser/doml/v2/doml_v2.1-2.ecore create mode 100644 icgparser/doml/v2/doml_v2.1.ecore create mode 100644 icgparser/doml/v2/doml_v2.2.ecore create mode 100644 icgparser/doml/v2/nio3.domlx create mode 100644 icgparser/doml/v2/posidonia_aws.domlx create mode 100644 icgparser/doml/v2/posidonia_openstack.domlx create mode 100644 icgparser/doml/v2/uc3.domlx create mode 100644 icgparser/doml/v2/wordpress.domlx create mode 100644 icgparser/test_ModelResources.py delete mode 100644 input_file_example/nginx/parameter.json delete mode 100644 input_file_example/wordpress/parameters.json create mode 100644 output_file_example/gitlab_1/config.yaml create mode 100644 output_file_example/gitlab_1/git/config.yaml rename {output_files_generated/nginx_openstack/piacere_monitoring => output_file_example/gitlab_1/portainer}/config.yaml (79%) create mode 100644 output_file_example/nginx_openstack_with_agents_new/ansible/config.yaml create mode 100644 output_file_example/nginx_openstack_with_agents_new/ansible/inventory.j2 create mode 100644 output_file_example/nginx_openstack_with_agents_new/ansible/ssh_key.j2 create mode 100644 output_file_example/nginx_openstack_with_agents_new/config.yaml create mode 100644 output_file_example/nginx_openstack_with_agents_new/monitoring/ansible.cfg create mode 100644 output_file_example/nginx_openstack_with_agents_new/monitoring/ansible_requirements.yml create mode 100644 output_file_example/nginx_openstack_with_agents_new/monitoring/config.yaml create mode 100644 output_file_example/nginx_openstack_with_agents_new/monitoring/hosts.yaml create mode 100644 output_file_example/nginx_openstack_with_agents_new/monitoring/install_playbook_requirements.sh create mode 100644 output_file_example/nginx_openstack_with_agents_new/monitoring/inventory.j2 rename templates/ansible/ubuntu/piacere_main.tpl => output_file_example/nginx_openstack_with_agents_new/monitoring/main.yml (100%) create mode 100644 output_file_example/nginx_openstack_with_agents_new/monitoring/run-playbook.sh create mode 100644 output_file_example/nginx_openstack_with_agents_new/monitoring/site.yaml create mode 100644 output_file_example/nginx_openstack_with_agents_new/monitoring/site_requirements.yaml create mode 100644 output_file_example/nginx_openstack_with_agents_new/monitoring/ssh_key.j2 create mode 100644 output_file_example/nginx_openstack_with_agents_new/monitoring/vars/main.yaml create mode 100644 output_file_example/nginx_openstack_with_agents_new/terraform/config.yaml create mode 100644 output_file_example/nginx_openstack_with_agents_new/terraform/main.tf create mode 100644 output_file_example/nginx_openstack_with_agents_new/terraform/output.tf delete mode 100644 output_files_generated/nginx_openstack/piacere_monitoring/ssh_key.j2 delete mode 100644 output_files_generated/posidonia/terraform/config.yaml delete mode 100644 output_files_generated/wordpress_azure/terraform/main.tf create mode 100644 plugin/DockerComposePlugin.py delete mode 100644 service.yml create mode 100644 templates/ansible/centos/config.tpl create mode 100644 templates/ansible/centos/elasticsearch.tpl create mode 100644 templates/ansible/centos/elasticsearch_main.tpl create mode 100644 templates/ansible/centos/inventory.tpl create mode 100644 templates/ansible/centos/performance_monitoring_main.tpl create mode 100644 templates/ansible/centos/security_monitoring_main.tpl create mode 100644 templates/ansible/centos/ssh_key.tpl create mode 100644 templates/ansible/cross-platform/performance_monitoring/.gitignore create mode 100644 templates/ansible/cross-platform/performance_monitoring/LICENSE create mode 100644 templates/ansible/cross-platform/performance_monitoring/README.md rename templates/ansible/{ubuntu/monitoring => cross-platform/performance_monitoring}/ansible_requirements.yml (90%) create mode 100644 templates/ansible/cross-platform/performance_monitoring/config.yaml create mode 100644 templates/ansible/cross-platform/performance_monitoring/inventory.j2 create mode 100644 templates/ansible/cross-platform/performance_monitoring/inventory.txt create mode 100644 templates/ansible/cross-platform/performance_monitoring/main.yml create mode 100644 templates/ansible/cross-platform/performance_monitoring/ssh_key.j2 create mode 100644 templates/ansible/cross-platform/performance_monitoring/vars/main.yaml create mode 100644 templates/ansible/cross-platform/security_monitoring/README.md create mode 100644 templates/ansible/cross-platform/security_monitoring/build-wazuh-agent.yml create mode 100644 templates/ansible/cross-platform/security_monitoring/config.yaml create mode 100644 templates/ansible/cross-platform/security_monitoring/config/ossec.conf.j2 create mode 100644 templates/ansible/cross-platform/security_monitoring/deploy-wazuh-agent.yml create mode 100644 templates/ansible/cross-platform/security_monitoring/deploy-wazuh-docker-agent.yml create mode 100644 templates/ansible/cross-platform/security_monitoring/docker-deploy/Dockerfile create mode 100644 templates/ansible/cross-platform/security_monitoring/docker-deploy/entrypoint.sh create mode 100644 templates/ansible/cross-platform/security_monitoring/docker-deploy/ossec.conf create mode 100644 templates/ansible/cross-platform/security_monitoring/inventory.j2 create mode 100644 templates/ansible/cross-platform/security_monitoring/inventory.txt create mode 100644 templates/ansible/cross-platform/security_monitoring/main.yml create mode 100644 templates/ansible/cross-platform/security_monitoring/ssh_key.j2 create mode 100644 templates/ansible/cross-platform/security_monitoring/vars.yml create mode 100644 templates/ansible/ubuntu/elasticsearch.tpl create mode 100644 templates/ansible/ubuntu/elasticsearch_main.tpl create mode 100644 templates/ansible/ubuntu/performance_monitoring_main.tpl create mode 100644 templates/ansible/ubuntu/security_monitoring_main.tpl create mode 100644 templates/common/gaiax_self_description.yaml.tpl create mode 100644 templates/docker_compose/config.tpl create mode 100644 templates/docker_compose/docker_compose.tpl create mode 100644 templates/docker_compose/inventory.tpl create mode 100644 templates/docker_compose/main.tpl create mode 100644 templates/docker_compose/ssh_key.tpl create mode 100644 templates/terraform/aws/autoscaling_group.tpl create mode 100644 templates/terraform/aws/config.tpl rename controller/test_Orchestrator.py => templates/terraform/aws/ssh_key.tpl (76%) create mode 100644 templates/terraform/aws/temp.tpl create mode 100644 templates/terraform/aws/virtual_machine_out.tpl create mode 100644 templates/terraform/azure/port_rule.tpl create mode 100644 templates/terraform/ionos/config.tpl create mode 100644 templates/terraform/ionos/datacenter.tpl create mode 100644 templates/terraform/ionos/init.tpl create mode 100644 templates/terraform/ionos/network.tpl create mode 100644 templates/terraform/ionos/virtual_machine.tpl create mode 100644 templates/terraform/ionos/virtual_machine_out.tpl create mode 100644 templates/terraform/vsphere/config.tpl create mode 100644 templates/terraform/vsphere/data_resources.tpl create mode 100644 templates/terraform/vsphere/datacenter.tpl create mode 100644 templates/terraform/vsphere/datastore.tpl create mode 100644 templates/terraform/vsphere/init.tpl create mode 100644 templates/terraform/vsphere/network.tpl create mode 100644 templates/terraform/vsphere/ssh_key.tpl create mode 100644 templates/terraform/vsphere/virtual_machine.tpl create mode 100644 templates/terraform/vsphere/virtual_machine_out.tpl create mode 100644 templates/terraform/vsphere/vm_image.tpl create mode 100644 utility/PropertiesReaderUtility.py diff --git a/.gitignore b/.gitignore index 49f0b2a..ad56d0c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ .idea *.tar.gz icgparser/doml/*domlx -# output_files_generated/* +input_file_generated/ir.json # Byte-compiled / optimized / DLL files __pycache__/ @@ -154,4 +154,5 @@ cython_debug/ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ \ No newline at end of file +#.idea/ +/output_files_generated/* diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..6566d74 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,66 @@ +include: + - project: piacere/private/t23-ci-setup + ref: main + file: + - gitlab-ci-scripts/utils.gitlab-ci.yml + + # Image tag variables generation job ------------- + # Stage: variable-generation --------------------- + - gitlab-ci-scripts/generate-variables.gitlab-ci.yml + + # Downstream t23-ci-setup pipeline trigger job --- + # Stage: integration-tests-publish-deploy -------- + - gitlab-ci-scripts/trigger-downstream.gitlab-ci.yml + +variables: + # ------------------------ + # Component image tag data + # ------------------------ + COMPONENT_WP: wp3 + ICG_IMAGE_NAME: icg + + # ------------------------------------------ + # Space-separated component image abbreviation list + # used to generate image tags and related variables. + # It is also passed to the downstream integration tests, + # publication and deployment pipeline. + # ------------------------------------------ + IMAGE_NAMES: "$ICG_IMAGE_NAME" + +stages: + - variable-generation + - quality + - build + - security +# TODO: - unit-tests + - integration-tests-publish-deploy + +# Quality jobs ---------------------- + +# TODO: quality checks + +# Build jobs ---------------------- + +build-temp-icg: + stage: build + variables: + TMP_IMAGE: "$TMP_IMAGE_ICG" + DOCKERFILE_PATH: "." + GIT_SUBMODULE_STRATEGY: recursive + trigger: !reference [.trigger-build] + +# Security job ------------------------ + +security-trivy-icg: + stage: security + variables: + TMP_IMAGE: "$TMP_IMAGE_ICG" + trigger: !reference [.trigger-security-trivy] + needs: + - job: build-temp-icg + - job: generate-variables + artifacts: true + +# Unit tests jobs ------------------------ + +# TODO: unit tests \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..60f06fe --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "templates/ansible/cross-platform/performance_monitoring"] + path = templates/ansible/cross-platform/performance_monitoring + url = ../../../../piacere/public/agents/pma-playbook.git +[submodule "templates/ansible/cross-platform/security_monitoring"] + path = templates/ansible/cross-platform/security_monitoring + url = ../../../../piacere/public/agents/sma-playbook.git diff --git a/Dockerfile b/Dockerfile index fad3448..162ba01 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,8 +16,9 @@ FROM python:3.10.1-alpine WORKDIR /opt -COPY . /opt/ - +COPY requirements.txt /opt/requirements.txt RUN pip install -r requirements.txt +COPY . /opt/ +expose 5000 CMD ["uvicorn", "main:fast_api", "--host", "0.0.0.0", "--port", "5000"] \ No newline at end of file diff --git a/README.md b/README.md index 71f8c37..52b4665 100644 --- a/README.md +++ b/README.md @@ -6,15 +6,15 @@ Requirements ------------- - Docker -Installation +Installation ------------- To have a functional ICG application the following steps can be used. -- Download the full content of this repository +- Download the full content of this repository, there are git submodules, so add them using this command: `git submodule update --init --recursive` - Build the docker image launching the following command: `docker build -t icg:1.0.0 .` - Run the container: `docker run --name icg -d -p 5000:5000 icg:1.0.0` - + Usage ------------ diff --git a/api/InfrastructureTemplateController.py b/api/InfrastructureTemplateController.py index ccab6e0..7fcddee 100644 --- a/api/InfrastructureTemplateController.py +++ b/api/InfrastructureTemplateController.py @@ -11,19 +11,32 @@ # 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. -#------------------------------------------------------------------------- +# ------------------------------------------------------------------------- import logging -from fastapi import APIRouter, Body +from typing import Optional + +import aiofiles +import shutil +from fastapi import APIRouter, Body, File, UploadFile, status from fastapi.responses import FileResponse +from fastapi.exceptions import HTTPException +import os from controller import Orchestrator +from pydantic import BaseModel api_router = APIRouter() base_compress_file_name = "iac_files_" -@api_router.post("/infrastructure/files") +class Doml(BaseModel): + ecore: str + model: Optional[str] = None + external_iac_folder: float + + +@api_router.post("/infrastructure/files", deprecated=True) def create_iac_from_intermediate_representation(intermediate_representation: dict = Body(...)): logging.info("Received intermediate representation create_iac_from_intermediate_representation request") compress_folder_info = Orchestrator.create_iac_from_intermediate_representation(intermediate_representation) @@ -34,7 +47,53 @@ def create_iac_from_intermediate_representation(intermediate_representation: dic @api_router.post("/iac/files") def create_iac_from_doml(data: str = Body(..., media_type="application/xml")): logging.info("Received create_iac_from_doml request") - compress_folder_info = Orchestrator.create_iac_from_doml(model=data, metamodel_directory="icgparser/doml/v1", + compress_folder_info = Orchestrator.create_iac_from_doml(model=data, metamodel_directory="icgparser/doml/v2", is_multiecore_metamodel=False) return FileResponse(path=compress_folder_info.file_path, media_type='application/octet-stream', filename=compress_folder_info.filename) + + +CHUNK_SIZE = 1024 * 1024 # adjust the chunk size as desired + + +@api_router.post("/iac/files/upload") +async def create_iac_from_doml_model(file: UploadFile = File(...)): + try: + filepath = os.path.join('./', os.path.basename(file.filename)) + async with aiofiles.open(filepath, 'wb') as f: + while chunk := await file.read(CHUNK_SIZE): + await f.write(chunk) + except Exception: + raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail='There was an error uploading the file') + finally: + await file.close() + #logging.info(f"Removing file {file.filename}") + data, outputpath = Orchestrator.extract_file_zip('./'+file.filename) + compress_folder_info = Orchestrator.create_iac_from_doml(model=data, metamodel_directory=outputpath, + is_multiecore_metamodel=False) + shutil.unpack_archive(compress_folder_info.filename, outputpath) + shutil.make_archive(outputpath, 'zip', outputpath ) + logging.info(f"Successfuly uploaded {file.filename}") + return FileResponse(path=outputpath+'.zip', media_type='application/octet-stream', + filename=file.filename) + + +@api_router.post("/iac/files/extension/intermediate_representation") +async def create_iac_intermediate_representation(file: UploadFile = File(...)): + try: + filepath = os.path.join('./', os.path.basename(file.filename)) + async with aiofiles.open(filepath, 'wb') as f: + while chunk := await file.read(CHUNK_SIZE): + await f.write(chunk) + except Exception: + raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail='There was an error uploading the file') + finally: + await file.close() + #logging.info(f"Removing file {file.filename}") + + logging.info(f"Successfuly uploaded {file.filename}") + return FileResponse(path=filepath, media_type='application/octet-stream', + filename=file.filename) + diff --git a/controller/Orchestrator.py b/controller/Orchestrator.py index f806073..6dcf09b 100644 --- a/controller/Orchestrator.py +++ b/controller/Orchestrator.py @@ -11,19 +11,19 @@ # 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. -#------------------------------------------------------------------------- +# ------------------------------------------------------------------------- import json import logging import os +import shutil import tarfile -import time import uuid import yaml from icgparser import ModelParser, PiacereInternalToolsIntegrator, IntermediateRepresentationUtility -from icgparser.IntermediateRepresentationUtility import IntermediateRepresentationResources -from plugin import AnsiblePlugin, TerraformPlugin +from icgparser.ModelResourcesUtilities import ModelResources, get_ir_key_name +from plugin import AnsiblePlugin, TerraformPlugin, TemplateUtils, DockerComposePlugin from utility.FileParsingUtility import replace_none_with_empty_str @@ -40,6 +40,17 @@ def create_infrastructure_files(intermediate_representation: dict): return template_generated_folder +def create_gaiax_file(parameters): + template_for_gaiax_path = TemplateUtils.find_template_path(None, "common", "gaiax_self_description") + if template_for_gaiax_path: + template = TemplateUtils.read_template(template_for_gaiax_path) + template_filled = TemplateUtils.edit_template(template, parameters) + else: + logging.warning("No GaiaX template found") + return template_filled + + + def choose_plugin(parameters, template_generated_folder): # os.system('rm -f /opt/output_files_generated/*') logging.info("Choosing plugin") @@ -47,7 +58,7 @@ def choose_plugin(parameters, template_generated_folder): for step in parameters["steps"]: if step["programming_language"] == "ansible": logging.info("Ansible Plugin chosen") - step_name = step[IntermediateRepresentationResources.STEP_NAME.value] + step_name = step[get_ir_key_name(ModelResources.STEP_NAME)] metadata_root_folder["iac"].append(step_name) # input_data = step["data"] AnsiblePlugin.create_files(step, template_generated_folder) @@ -60,6 +71,13 @@ def choose_plugin(parameters, template_generated_folder): plugin_metadata = {"input": [], "output": [], "engine": "terraform"} save_file(plugin_metadata, iac_output_folder + "/config.yaml", output_extensions="YAML") TerraformPlugin.create_files(input_data, iac_output_folder) + elif step["programming_language"] == "docker-compose": + logging.info("Docker Compose Plugin chosen") + # input_data = step["data"] + metadata_root_folder["iac"].append("docker-compose") + DockerComposePlugin.create_files(step, template_generated_folder) + gaiax_file = create_gaiax_file(parameters) + save_file(gaiax_file, template_generated_folder + "/gaiax_self_description.yaml", output_extensions="YAML") save_file(metadata_root_folder, template_generated_folder + "/config.yaml", output_extensions="YAML") @@ -93,10 +111,10 @@ def reorganize_info(intermediate_repr): def random_file_name_generation(base_name): - return base_name + str(uuid.uuid4().hex) + ".tar.gz" + return base_name + str(uuid.uuid4().hex) ## + ".zip" -def compress_file(source_folder, dest_file_name): +def compress_file_targz(source_folder, dest_file_name): # prefix_path = "/opt/" prefix_path = "" folder_path = prefix_path + dest_file_name + "" @@ -105,6 +123,25 @@ def compress_file(source_folder, dest_file_name): tar.add(source_folder, arcname='.') return folder_path +def compress_file_zip(source_folder, dest_file_name): + # prefix_path = "/opt/" + prefix_path = "" + folder_path = prefix_path + dest_file_name + logging.info(f"Compressing folder {source_folder} into destination {folder_path}") + shutil.make_archive(folder_path, 'zip', source_folder) + return folder_path + ".zip" + +def extract_file_zip(source_file): + outputpath = "./" + os.path.basename(source_file).split(".")[0] + os.mkdir(outputpath) + shutil.unpack_archive(source_file, outputpath) + #newfilepath = outputpath + os.path.basename(source_file).split(".")[0] + "/" + domlx_files = [f for f in os.listdir(outputpath) if f.endswith('.domlx')] + domlx_file = open(outputpath+'/'+domlx_files[0], "r") + data = domlx_file.read() + domlx_file.close() + #outputfile = outputpath+os.path.basename(source_file).split(".")[0] + return data, outputpath #outputpath def create_temp_model_file(model_xml): logging.info("Saving model in temp file") @@ -115,18 +152,18 @@ def create_temp_model_file(model_xml): def create_intermediate_representation(model_path, is_multiecore_metamodel, metamodel_directory): + logging.info("Calling ICG Parser for creating intermediate representation") intermediate_representation = ModelParser.parse_model(model_path=model_path, is_multiecore_metamodel=is_multiecore_metamodel, metamodel_directory=metamodel_directory) - # intermediate_representation = reorganize_info(intermediate_representation) logging.info(f"Successfully created intermediate representation {intermediate_representation}") logging.info("Calling ICG PiacereInternalToolsIntegrator to add info for PIACERE internal tools") intermediate_representation = PiacereInternalToolsIntegrator.add_internal_tool_information(intermediate_representation) - logging.warning("Force adding sg information in network") ## TODO fix from doml + logging.warning("Force adding sg information in network") ## TODO fix from doml intermediate_representation = IntermediateRepresentationUtility.force_add_resources_name( - IntermediateRepresentationResources.NETWORKS, - IntermediateRepresentationResources.SECURITY_GROUPS, + ModelResources.NETWORKS, + ModelResources.SECURITY_GROUPS, intermediate_representation) intermediate_representation_path = "input_file_generated/ir.json" save_file(intermediate_representation, intermediate_representation_path) @@ -137,9 +174,9 @@ def create_intermediate_representation(model_path, is_multiecore_metamodel, meta def compress_iac_folder(template_generated_folder): base_compress_file_name = "iac_files_" compress_file_name = random_file_name_generation(base_compress_file_name) - compress_file_folder_path = compress_file(template_generated_folder, compress_file_name) - logging.info(f"Successfully created iac files, available at {compress_file_folder_path}") - compress_folder_info = CompressFolder(file_path=compress_file_folder_path, filename=compress_file_name) + compress_file_folder_name = compress_file_zip(template_generated_folder, compress_file_name) + logging.info(f"Successfully created iac files, available at {compress_file_folder_name}") + compress_folder_info = CompressFolder(file_path=compress_file_folder_name, filename=compress_file_folder_name) return compress_folder_info @@ -167,8 +204,9 @@ def create_iac_from_doml(model, is_multiecore_metamodel, metamodel_directory): ## TODO: same as def create_iac_from_doml_path a part from the model storage in xml intermediate_representation = create_intermediate_representation(model_path, is_multiecore_metamodel, metamodel_directory) - template_generated_folder = create_iac_from_intermediate_representation(intermediate_representation) + template_generated_folder = intermediate_representation["output_path"] PiacereInternalToolsIntegrator.add_files_for_piacere_internal_tools(template_generated_folder) + create_iac_from_intermediate_representation(intermediate_representation) compress_folder_info = compress_iac_folder(template_generated_folder) return compress_folder_info @@ -186,9 +224,11 @@ def create_iac_from_doml_path(model_path, is_multiecore_metamodel, metamodel_dir :returns: path to the zip folder containing the IaC files :type: str """ + logging.info("Creating iac files: parse and plugins will be called") intermediate_representation = create_intermediate_representation(model_path, is_multiecore_metamodel, metamodel_directory) - template_generated_folder = create_iac_from_intermediate_representation(intermediate_representation) + template_generated_folder = intermediate_representation["output_path"] PiacereInternalToolsIntegrator.add_files_for_piacere_internal_tools(template_generated_folder) + create_iac_from_intermediate_representation(intermediate_representation) compress_folder_info = compress_iac_folder(template_generated_folder) return compress_folder_info diff --git a/doc/KR-3.feature b/doc/KR-3.feature new file mode 100644 index 0000000..d409798 --- /dev/null +++ b/doc/KR-3.feature @@ -0,0 +1,39 @@ +Feature: PIACERE Design time + + As a PIACERE user I want to generate IaC code for the provisioning and configuration of my infrastructure. + + +### SCENARIO 1: Generation of infrastructure provisioning code +Given a verified DOML model containing the infrastructure definition +When a user navigates to the DOMLx document +And right-clicks on it +And selects "Piacere" +And selects "Generate IaC code" +Then a compressed folder containing the infrastructural IaC code is generated + +### SCENARIO 2: Generation of infrastructure provisioning code for multiple providers +Given a verified DOML model containing the infrastructure definition +And two different providers between the supported ones +And a user selects one of the two as active +When a user navigates to the DOML document +And right-clicks on it +And selects "Piacere" +And selects "Generate IaC code" +Then a compressed folder containing the infrastructural IaC code for the active provider is generated + +### SCENARIO 3: Generation of contingent provisioning and configuration code +Given a verified DOML model containing the infrastructure and coherent application definition +When a user navigates to the DOMLx document +And right-clicks on it +And selects "Piacere" +And selects "Generate IaC code" +Then a compressed folder containing the infrastructural and subsequent application configuration IaC code is generated + +### SCENARIO 4: Generation of PIACERE monitoring and security agents +Given a verified DOML model containing the infrastructure definition +And at least a virtual machine is defined +When a user navigates to the DOMLx document +And right-clicks on it +And selects "Piacere" +And selects "Generate IaC code" +Then a compressed folder is generated that contains also the monitoring and security agents configuration IaC code \ No newline at end of file diff --git a/doc/SIMPA_dolmv3.0/nio3.doml b/doc/SIMPA_dolmv3.0/nio3.doml new file mode 100644 index 0000000..9272eac --- /dev/null +++ b/doc/SIMPA_dolmv3.0/nio3.doml @@ -0,0 +1,147 @@ +doml nio3_test_exec_env + +application app { + + software_component nio3 { + properties {} + } +} + +infrastructure infra { + + net net1 { + cidr "/24" + protocol "tcp/ip" + gateway g1 { + address "10.83.18.65" + } + } + + key_pair ssh_key { + algorithm "RSA" + bits 4096 + } + + vm_image img { + generates vm1, vm2 + } + + vm vm1 { + os "centos7_64Guest" + cpu_count 2 + mem_mb 1024.0 + iface i1 { + address "10.83.18.92" + belongs_to net1 + } + credentials ssh_key + } + + vm vm2 { + os "centos7_64Guest" + cpu_count 2 + mem_mb 1024.0 + iface i1 { + address "10.83.18.88" + belongs_to net1 + } + credentials ssh_key + } + + storage disk0 { + label "disk0" + size_gb 100 + } +} + +deployment conf { + nio3 => vm1, nio3 => vm2 +} + +active deployment conf + +concretizations { + concrete_infrastructure con_infra { + provider vsphere { + properties { + username = "vc_username" + password = "vc_password" + vsphere_server = "psvc10000002.cd.sigov.si" + allow_unverified_ssl = true + } + + generic_resource dc { + preexisting true + type 'vsphere_datacenter' + gname 'PIACDC' + } + + generic_resource cl { + preexisting true + refs_to dc + type 'vsphere_compute_cluster' + gname 'PIACC' + } + + generic_resource pool { + preexisting true + refs_to dc + type 'vsphere_resource_pool' + gname 'Piacere' + } + + vm_image template { + preexisting true + refs_to dc + properties { + vsphere_virtual_machine_name = "c7tmp" + } + maps img + } + + storage datastore { + preexisting true + refs_to dc + properties { + vsphere_datastore_name = 'NFSShare01' + } + maps disk0 + } + + net network { + preexisting true + refs_to dc + properties { + vsphere_network_name = "Nested-ESXi" + } + maps net1 + } + + vm con_vm1 { + refs_to pool, datastore, template + properties { + host_name = "simpa-test00-piacere" + domain = "tri.lan" + guest_id = "centos7_64Guest" + disk = "disk0" + disk_size = 100 + + } + maps vm1 + } + + vm con_vm2 { + refs_to pool, datastore, template + properties { + host_name = "simpa-test00-piacere" + domain = "tri.lan" + guest_id = "centos7_64Guest" + disk = "disk1" + disk_size = 100 + } + maps vm2 + } + } + } + active con_infra +} \ No newline at end of file diff --git a/doc/SIMPA_dolmv3.0/nio3.domlx b/doc/SIMPA_dolmv3.0/nio3.domlx new file mode 100644 index 0000000..433f440 --- /dev/null +++ b/doc/SIMPA_dolmv3.0/nio3.domlx @@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="ASCII"?> +<commons:DOMLModel xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:app="http://www.piacere-project.eu/doml/application" xmlns:commons="http://www.piacere-project.eu/doml/commons" xmlns:infra="http://www.piacere-project.eu/doml/infrastructure" name="nio3_test_exec_env" activeConfiguration="//@configurations.0" activeInfrastructure="//@concretizations.0"> + <application name="app"> + <components xsi:type="app:SoftwareComponent" name="nio3"/> + </application> + <infrastructure name="infra"> + <nodes xsi:type="infra:VirtualMachine" name="vm1" os="centos7_64Guest" memory_mb="1024.0" cpu_count="2" credentials="//@infrastructure/@credentials.0" generatedFrom="//@infrastructure/@generators.0"> + <ifaces name="i1" endPoint="10.83.18.92" belongsTo="//@infrastructure/@networks.0"/> + </nodes> + <nodes xsi:type="infra:VirtualMachine" name="vm2" os="centos7_64Guest" memory_mb="1024.0" cpu_count="2" credentials="//@infrastructure/@credentials.0" generatedFrom="//@infrastructure/@generators.0"> + <ifaces name="i1" endPoint="10.83.18.88" belongsTo="//@infrastructure/@networks.0"/> + </nodes> + <generators xsi:type="infra:VMImage" name="img" generatedVMs="//@infrastructure/@nodes.0 //@infrastructure/@nodes.1"/> + <storages name="disk0" label="disk0" size_gb="100"/> + <credentials xsi:type="commons:KeyPair" name="ssh_key" algorithm="RSA" bits="4096"/> + <networks name="net1" protocol="tcp/ip" addressRange="/24" connectedIfaces="//@infrastructure/@nodes.0/@ifaces.0 //@infrastructure/@nodes.1/@ifaces.0"> + <gateways name="g1" address="10.83.18.65"/> + </networks> + </infrastructure> + <concretizations name="con_infra"> + <providers name="vsphere"> + <annotations xsi:type="commons:SProperty" key="username" value="vc_username"/> + <annotations xsi:type="commons:SProperty" key="password" value="vc_password"/> + <annotations xsi:type="commons:SProperty" key="vsphere_server" value="psvc10000002.cd.sigov.si"/> + <annotations xsi:type="commons:BProperty" key="allow_unverified_ssl" value="true"/> + <resources name="dc" preexisting="true" type="vsphere_datacenter" gname="PIACDC"/> + <resources name="cl" preexisting="true" refs="//@concretizations.0/@providers.0/@resources.0" type="vsphere_compute_cluster" gname="PIACC"/> + <resources name="pool" preexisting="true" refs="//@concretizations.0/@providers.0/@resources.0" type="vsphere_resource_pool" gname="Piacere"/> + <vms name="con_vm1" refs="//@concretizations.0/@providers.0/@resources.2 //@concretizations.0/@providers.0/@storages.0 //@concretizations.0/@providers.0/@vmImages.0" maps="//@infrastructure/@nodes.0"> + <annotations xsi:type="commons:SProperty" key="host_name" value="piac-0"/> + <annotations xsi:type="commons:SProperty" key="domain" value="ad.sigov.si"/> + <annotations xsi:type="commons:SProperty" key="guest_id" value="centos7_64Guest"/> + <annotations xsi:type="commons:SProperty" key="disk" value="disk0"/> + <annotations xsi:type="commons:IProperty" key="disk_size" value="100"/> + </vms> + <vms name="con_vm2" refs="//@concretizations.0/@providers.0/@resources.2 //@concretizations.0/@providers.0/@storages.0 //@concretizations.0/@providers.0/@vmImages.0" maps="//@infrastructure/@nodes.1"> + <annotations xsi:type="commons:SProperty" key="host_name" value="piac-1"/> + <annotations xsi:type="commons:SProperty" key="domain" value="ad.sigov.si"/> + <annotations xsi:type="commons:SProperty" key="guest_id" value="centos7_64Guest"/> + <annotations xsi:type="commons:SProperty" key="disk" value="disk1"/> + <annotations xsi:type="commons:IProperty" key="disk_size" value="100"/> + </vms> + <vmImages name="template" preexisting="true" refs="//@concretizations.0/@providers.0/@resources.0" maps="//@infrastructure/@generators.0"> + <annotations xsi:type="commons:SProperty" key="vsphere_virtual_machine_name" value="c7tmp"/> + </vmImages> + <networks name="network" preexisting="true" refs="//@concretizations.0/@providers.0/@resources.0" maps="//@infrastructure/@networks.0"> + <annotations xsi:type="commons:SProperty" key="vsphere_network_name" value="Nested-ESXi"/> + </networks> + <storages name="datastore" preexisting="true" refs="//@concretizations.0/@providers.0/@resources.0" maps="//@infrastructure/@storages.0"> + <annotations xsi:type="commons:SProperty" key="vsphere_datastore_name" value="NFSShare01"/> + </storages> + </providers> + </concretizations> + <configurations name="conf"> + <deployments component="//@application/@components.0" node="//@infrastructure/@nodes.0"/> + <deployments component="//@application/@components.0" node="//@infrastructure/@nodes.1"/> + </configurations> +</commons:DOMLModel> diff --git a/doc/SIMPA_example_domlv2.2/simpa_v2.2.0-1.doml b/doc/SIMPA_example_domlv2.2/simpa_v2.2.0-1.doml new file mode 100644 index 0000000..8fb0785 --- /dev/null +++ b/doc/SIMPA_example_domlv2.2/simpa_v2.2.0-1.doml @@ -0,0 +1,150 @@ +doml nio3_test_exec_env + +application app { + + software_component nio3 { + properties {} + } +} + +infrastructure infra { + + net net1 { + cidr "/24" + protocol "tcp/ip" + gateway g1 { + address "10.83.18.65" + } + } + + user_pass ssh_pass { + user "root" + pass "pa$$w0rd" + } + + key_pair ssh_key { + keyfile "/home/rmandal/.ssh/id_rsa.pub" + } + + vm_image img { + generates vm1, vm2 + } + + vm vm1 { + os "centos7_64Guest" + cpu_count 2 + mem_mb 1024.0 + iface i1 { + address "10.83.18.92" + belongs_to net1 + } + credentials ssh_pass + } + + vm vm2 { + os "centos7_64Guest" + cpu_count 2 + mem_mb 1024.0 + iface i1 { + address "10.83.18.88" + belongs_to net1 + } + credentials ssh_pass + } + + sto disk0 { + label "disk0" + size_gb 100 + } +} + +deployment conf { + nio3 -> vm1, nio3 -> vm2 +} + +active deployment conf + +concretizations { + concrete_infrastructure con_infra { + provider vsphere { + properties { + username = "vc_username"; + password = "vc_password"; + vsphere_server = "psvc10000002.cd.sigov.si"; + allow_unverified_ssl = true; + } + + generic_resource dc { + preexisting true + type 'vsphere_datacenter' + gname 'MB' + } + + generic_resource cl { + preexisting true + refs_to dc + type 'vsphere_compute_cluster' + gname 'MB-PIAC-NIC-1' + } + + generic_resource pool { + preexisting true + refs_to dc + type 'vsphere_resource_pool' + gname 'PIAC' + } + + vm_image template { + preexisting true + refs_to dc + properties { + vsphere_virtual_machine_name = "Centos7_PIAC"; + } + maps img + } + + storage datastore { + preexisting true + refs_to dc + properties { + vsphere_datastore_name = 'VNX01-0200-NIC-TA-PIAC-DRO-VMW-P'; + } + maps disk0 + } + + net network { + preexisting true + refs_to dc + properties { + vsphere_network_name = "DRO-MB-P-BG001-2098"; + } + maps net1 + } + + vm con_vm1 { + refs_to pool, datastore, template + properties { + host_name = "piac-0"; + domain = "ad.sigov.si"; + disk = "disk0"; + disk_size = "100"; + guest_id = "centos7_64Guest"; + } + maps vm1 + } + + vm con_vm2 { + refs_to pool, datastore, template + properties { + host_name = "piac-1"; + domain = "ad.sigov.si"; + disk = "disk1"; + disk_size = "100"; + guest_id = "centos7_64Guest"; + } + maps vm2 + } + } + } + active con_infra +} \ No newline at end of file diff --git a/doc/SIMPA_example_domlv2.2/simpa_v2.2.domlx b/doc/SIMPA_example_domlv2.2/simpa_v2.2.domlx new file mode 100644 index 0000000..99050ef --- /dev/null +++ b/doc/SIMPA_example_domlv2.2/simpa_v2.2.domlx @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="ASCII"?> +<commons:DOMLModel xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:app="http://www.piacere-project.eu/doml/application" xmlns:commons="http://www.piacere-project.eu/doml/commons" xmlns:infra="http://www.piacere-project.eu/doml/infrastructure" name="nio3_test_exec_env" activeConfiguration="//@configurations.0" activeInfrastructure="//@concretizations.0"> + <application name="app"> + <components xsi:type="app:SoftwareComponent" name="nio3"/> + </application> + <infrastructure name="infra"> + <nodes xsi:type="infra:VirtualMachine" name="vm1" os="centos7_64Guest" memory_mb="1024.0" cpu_count="2" credentials="//@infrastructure/@credentials.0" generatedFrom="//@infrastructure/@generators.0"> + <ifaces name="i1" endPoint="10.83.18.92" belongsTo="//@infrastructure/@networks.0"/> + </nodes> + <nodes xsi:type="infra:VirtualMachine" name="vm2" os="centos7_64Guest" memory_mb="1024.0" cpu_count="2" credentials="//@infrastructure/@credentials.0" generatedFrom="//@infrastructure/@generators.0"> + <ifaces name="i1" endPoint="10.83.18.88" belongsTo="//@infrastructure/@networks.0"/> + </nodes> + <generators xsi:type="infra:VMImage" name="img" generatedVMs="//@infrastructure/@nodes.0 //@infrastructure/@nodes.1"/> + <storages name="disk0" label="disk0" size_gb="100"/> + <credentials xsi:type="commons:UserPass" name="ssh_pass" username="root" password="pa$$w0rd"/> + <credentials xsi:type="commons:KeyPair" name="ssh_key" keyfile="/home/rmandal/.ssh/id_rsa.pub"/> + <networks name="net1" protocol="tcp/ip" addressRange="/24" connectedIfaces="//@infrastructure/@nodes.0/@ifaces.0 //@infrastructure/@nodes.1/@ifaces.0"> + <gateways name="g1" address="10.83.18.65"/> + </networks> + </infrastructure> + <concretizations name="con_infra"> + <providers name="vsphere"> + <annotations xsi:type="commons:SProperty" key="username" value="vc_username"/> + <annotations xsi:type="commons:SProperty" key="password" value="vc_password"/> + <annotations xsi:type="commons:SProperty" key="vsphere_server" value="psvc10000002.cd.sigov.si"/> + <annotations xsi:type="commons:BProperty" key="allow_unverified_ssl" value="true"/> + <resources name="dc" preexisting="true" type="vsphere_datacenter" gname="MB"/> + <resources name="cl" preexisting="true" refs="//@concretizations.0/@providers.0/@resources.0" type="vsphere_compute_cluster" gname="MB-PIAC-NIC-1"/> + <resources name="pool" preexisting="true" refs="//@concretizations.0/@providers.0/@resources.0" type="vsphere_resource_pool" gname="PIAC"/> + <vms name="con_vm1" refs="//@concretizations.0/@providers.0/@resources.2 //@concretizations.0/@providers.0/@storages.0" maps="//@infrastructure/@nodes.0"> + <annotations xsi:type="commons:SProperty" key="host_name" value="piac-0"/> + <annotations xsi:type="commons:SProperty" key="domain" value="ad.sigov.si"/> + </vms> + <vms name="con_vm2" refs="//@concretizations.0/@providers.0/@resources.2 //@concretizations.0/@providers.0/@storages.0" maps="//@infrastructure/@nodes.1"> + <annotations xsi:type="commons:SProperty" key="host_name" value="piac-1"/> + <annotations xsi:type="commons:SProperty" key="domain" value="ad.sigov.si"/> + </vms> + <vmImages name="template" preexisting="true" refs="//@concretizations.0/@providers.0/@resources.0" maps="//@infrastructure/@generators.0"> + <annotations xsi:type="commons:SProperty" key="vsphere_virtual_machine_name" value="Centos7_PIAC"/> + </vmImages> + <networks name="network" preexisting="true" refs="//@concretizations.0/@providers.0/@resources.0" maps="//@infrastructure/@networks.0"> + <annotations xsi:type="commons:SProperty" key="vsphere_network_name" value="DRO-MB-P-BG001-2098"/> + </networks> + <storages name="datastore" preexisting="true" refs="//@concretizations.0/@providers.0/@resources.0" maps="//@infrastructure/@storages.0"> + <annotations xsi:type="commons:SProperty" key="vsphere_datastore_name" value="VNX01-0200-NIC-TA-PIAC-DRO-VMW-P"/> + </storages> + </providers> + </concretizations> + <configurations name="conf"> + <deployments component="//@application/@components.0" node="//@infrastructure/@nodes.0"/> + <deployments component="//@application/@components.0" node="//@infrastructure/@nodes.1"/> + </configurations> +</commons:DOMLModel> diff --git a/doc/ericsson/uc3.doml b/doc/ericsson/uc3.doml new file mode 100644 index 0000000..b6e1f7f --- /dev/null +++ b/doc/ericsson/uc3.doml @@ -0,0 +1,370 @@ +doml uc3_openstack + +application app { + + // need to define all sw components of the project this is a placeholder + // need to understand what is really needed in this spec + // need to specify all provides/consumes + + software_component iwg { + provides { net_info } + } + software_component osint { + provides { osint_info } + consumes { net_info, get_twitter, ewcf_rest_interface } + } + software_component ewcf { + provides { ewcf_rest_interface } + consumes { get_firebase } + } + saas external_twitter { + provides { get_twitter @ "https://twitter_api/get" } + } + saas external_firebase { + provides { get_firebase @ "https://firebase_api/get" } + } + +} + +infrastructure infra { + // oam is common to all VMs + // igw should have: oam, net1 to osint and net2 to external 5g + + // VMs region + vm igw_vm { + os "CentOS-7-2111" + size "small-centos" + + iface igw_vm_oam { + belongs_to subnet_oam_igw + } + + iface igw_vm_net1 { + belongs_to subnet_net1_igw + } + + iface igw_vm_net2 { + belongs_to subnet_net2_igw + } + + credentials ssh_key + } + + // sint should have: oam, net1 to igw, net3 to internet + vm osint_vm { + os "CentOS-7-2111" + size "small-centos" + + iface osint_vm_oam { + belongs_to subnet_oam_osint + } + + iface osint_vm_net1 { + belongs_to subnet_net1_osint + } + + iface osint_vm_net3 { + belongs_to subnet_net3_osint + } + + credentials ssh_key + } + + // ewcf should have: oam, net1 to osint, and net3 to internet + vm ewcf_vm { + os "CentOS-7-2111" + size "small-centos" + + iface ewcf_vm_oam { + belongs_to subnet_oam_ewcf + } + + iface ewcf_vm_net1 { + belongs_to subnet_net1_ewcf + } + + iface ewcf_vm_net3 { + belongs_to subnet_net3_ewcf + } + + credentials ssh_key + } + + // Containers region + container c1 { + host igw_vm { + container_port 82 + vm_port 8082 + iface igw_vm_oam + } + + host osint_vm { + + // Exposed port + container_port 80 + // Port on the VM where the container will map to + vm_port 8080 + + // Most of the interfaces should be on the internal network, but some of them need access through Internet + // Containers should have two interfaces: an exposed interface to be accessed through Internet and an interface for internal access + // TODO: However, we can specify only one network interface connected to the container + iface osint_vm_oam + + } + + } + + container c2 { + host igw_vm { + container_port 83 + vm_port 8083 + iface igw_vm_net1 + } + } + + + container cont_mongodb { + host ewcf_vm { + container_port 85 + vm_port 8085 + iface ewcf_vm_oam + } + } + + cont_image c_img { + generates c1, c2 + image "docker.hub.io/ericsson/c:1.0" // Esempio + // Sergio: Si potrebbe anche usare "script" al posto di "image", ma avevamo deciso di usare "image" per questo caso perché ci sembrava più appropriato + } + + cont_image mongodb_img { + generates cont_mongodb + image "..." + } + + // Network region + + // Internal Network + net oam { + protocol "TCP/IP" + cidr "16.0.0.0/24" + + subnet subnet_oam_igw { + protocol "TCP/IP" + cidr "16.0.1.0/26" + } + + subnet subnet_oam_osint { + protocol "TCP/IP" + cidr "16.0.1.64/26" + } + + subnet subnet_oam_ewcf { + protocol "TCP/IP" + cidr "16.0.1.128/26" + } + } + + // Internal network + net net1 { + protocol "TCP/IP" + cidr "16.0.1.0/24" + + // Subnets definition + subnet subnet_net1_igw { + connections { + subnet_net1_osint + } + protocol "TCP/IP" + cidr "16.0.1.0/25" + } + + subnet subnet_net1_osint { + connections { + subnet_net1_igw + subnet_net1_ewcf + } + protocol "TCP/IP" + cidr "16.0.1.64/26" + } + + subnet subnet_net1_ewcf { + connections { + subnet_net1_osint + } + protocol "TCP/IP" + cidr "16.0.1.128/26" + } + } + + // Network connecting igw to 5G + net net2 { + protocol "TCP/IP" + cidr "16.0.2.0/24" + + subnet subnet_net2_igw { + protocol "TCP/IP" + cidr "16.0.2.0/25" + } + + } + + // Network connecting osint and ewcf to Internet + net net3 { + protocol "TCP/IP" + cidr "16.0.3.0/24" + + subnet subnet_net3_osint { + protocol "TCP/IP" + cidr "16.0.3.0/25" + } + + subnet subnet_net3_ewcf { + protocol "TCP/IP" + cidr "16.0.3.128/25" + } + + } + + // credentials region + key_pair ssh_key { + user "ubuntu" + keyfile "/home/ubuntu/.ssh/openstack.key" + algorithm "RSA" + bits 4096 + } + + // Computing groups region + + // Currently not used since it is not implemented + // Autoscale groups should currently be removed (leave them commented in order to be eventually reused, + // even though the name could probably be different since there are not autoscale groups on Openstack) + + + // Security region + + // security group is left as originally defined needs to be updated + security_group sg { + egress icmp { + protocol "ICMP" + from_port -1 + to_port -1 + cidr ["0.0.0.0/0"] + } + ingress http { + protocol "TCP" + from_port 80 + to_port 80 + cidr ["0.0.0.0/0"] + } + ingress https { + protocol "TCP" + from_port 443 + to_port 443 + cidr ["0.0.0.0/0"] + } + ingress ssh { + protocol "TCP" + from_port 22 + to_port 22 + cidr ["0.0.0.0/0"] + } + + ifaces igw_vm_oam, igw_vm_net1, igw_vm_net2, osint_vm_oam, osint_vm_net1, osint_vm_net3, ewcf_vm_oam, ewcf_vm_net1, ewcf_vm_net3 + } +} + +deployment config1 { + osint -> osint_vm, + iwg -> igw_vm, + ewcf -> ewcf_vm +} + +active deployment config1 + +concretizations { + concrete_infrastructure con_infra { + + provider openstack { + + // Concrete computing nodes region + + vm concrete_osint_vm { + properties { + // Actually, this is not recognized by ICG, so it's useless + vm_name = "osint"; + // vm_flavor property moved to "size" attribute + vm_key_name = "ubuntu"; + } + maps osint_vm + } + + vm concrete_igw_vm { + properties { + vm_name = "igw"; + vm_key_name = "ubuntu"; + } + maps igw_vm + } + + vm concrete_ewcf_vm { + properties { + vm_name = "ewcf"; + vm_key_name = "ubuntu"; + } + maps ewcf_vm + } + + cont_image concrete_c_img { + maps c_img + } + + cont_image concrete_mongodb_img { + maps mongodb_img + } + + // Concrete Network region + net concrete_oam { + properties { + name = "uc3_oam"; + } + maps oam + } + + net concrete_net1 { + properties { + name = "uc3_net1"; + } + maps net1 + } + + net concrete_net2 { + properties { + name = "uc3_net2"; + } + maps net2 + } + + net concrete_net3 { + properties { + name = "uc3_net3"; + } + maps net3 + } + + } + } + active con_infra +} +optimization opt { + objectives { + "cost" => min + "performance" => max + "availability" => max + } + nonfunctional_requirements { + req1 "cost <= 300" max 300.0 => "cost"; + req2 "performance >= 7%" min 7.0 => "performance"; + req3 "elements" => "VM, Storage"; + } +} \ No newline at end of file diff --git a/doc/ericsson/uc3.domlx b/doc/ericsson/uc3.domlx new file mode 100644 index 0000000..26e41e8 --- /dev/null +++ b/doc/ericsson/uc3.domlx @@ -0,0 +1,248 @@ +<?xml version="1.0" encoding="ASCII"?> +<commons:DOMLModel xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:app="http://www.piacere-project.eu/doml/application" xmlns:commons="http://www.piacere-project.eu/doml/commons" xmlns:infra="http://www.piacere-project.eu/doml/infrastructure" xmlns:optimization="http://www.piacere-project.eu/doml/optimization" name="uc3_openstack" activeConfiguration="//@configurations.0" activeInfrastructure="//@concretizations.0"> + <application name="app"> + <components xsi:type="app:SoftwareComponent" name="iwg"> + <exposedInterfaces name="net_info"/> + </components> + <components xsi:type="app:SoftwareComponent" name="osint" consumedInterfaces="//@application/@components.0/@exposedInterfaces.0 //@application/@components.3/@exposedInterfaces.0 //@application/@components.2/@exposedInterfaces.0"> + <exposedInterfaces name="osint_info"/> + </components> + <components xsi:type="app:SoftwareComponent" name="ewcf" consumedInterfaces="//@application/@components.4/@exposedInterfaces.0"> + <exposedInterfaces name="ewcf_rest_interface"/> + </components> + <components xsi:type="app:SaaS" name="external_twitter"> + <exposedInterfaces name="get_twitter" endPoint="https://twitter_api/get"/> + </components> + <components xsi:type="app:SaaS" name="external_firebase"> + <exposedInterfaces name="get_firebase" endPoint="https://firebase_api/get"/> + </components> + </application> + <infrastructure name="infra"> + <nodes xsi:type="infra:VirtualMachine" name="igw_vm" os="CentOS-7-2111" credentials="//@infrastructure/@credentials.0" sizeDescription="small-centos"> + <ifaces name="igw_vm_oam" belongsTo="//@infrastructure/@networks.0/@subnets.0" associated="//@infrastructure/@securityGroups.0"/> + <ifaces name="igw_vm_net1" belongsTo="//@infrastructure/@networks.1/@subnets.0" associated="//@infrastructure/@securityGroups.0"/> + <ifaces name="igw_vm_net2" belongsTo="//@infrastructure/@networks.2/@subnets.0" associated="//@infrastructure/@securityGroups.0"/> + </nodes> + <nodes xsi:type="infra:VirtualMachine" name="osint_vm" os="CentOS-7-2111" credentials="//@infrastructure/@credentials.0" sizeDescription="small-centos"> + <ifaces name="osint_vm_oam" belongsTo="//@infrastructure/@networks.0/@subnets.1" associated="//@infrastructure/@securityGroups.0"/> + <ifaces name="osint_vm_net1" belongsTo="//@infrastructure/@networks.1/@subnets.1" associated="//@infrastructure/@securityGroups.0"/> + <ifaces name="osint_vm_net3" belongsTo="//@infrastructure/@networks.3/@subnets.0" associated="//@infrastructure/@securityGroups.0"/> + </nodes> + <nodes xsi:type="infra:VirtualMachine" name="ewcf_vm" os="CentOS-7-2111" credentials="//@infrastructure/@credentials.0" sizeDescription="small-centos"> + <ifaces name="ewcf_vm_oam" belongsTo="//@infrastructure/@networks.0/@subnets.2" associated="//@infrastructure/@securityGroups.0"/> + <ifaces name="ewcf_vm_net1" belongsTo="//@infrastructure/@networks.1/@subnets.2" associated="//@infrastructure/@securityGroups.0"/> + <ifaces name="ewcf_vm_net3" belongsTo="//@infrastructure/@networks.3/@subnets.1" associated="//@infrastructure/@securityGroups.0"/> + </nodes> + <nodes xsi:type="infra:Container" name="c1" generatedFrom="//@infrastructure/@generators.0"> + <configs container_port="82" vm_port="8082" host="//@infrastructure/@nodes.0" iface="//@infrastructure/@nodes.0/@ifaces.0"/> + <configs container_port="80" vm_port="8080" host="//@infrastructure/@nodes.1" iface="//@infrastructure/@nodes.1/@ifaces.0"/> + </nodes> + <nodes xsi:type="infra:Container" name="c2" generatedFrom="//@infrastructure/@generators.0"> + <configs container_port="83" vm_port="8083" host="//@infrastructure/@nodes.0" iface="//@infrastructure/@nodes.0/@ifaces.1"/> + </nodes> + <nodes xsi:type="infra:Container" name="cont_mongodb" generatedFrom="//@infrastructure/@generators.1"> + <configs container_port="85" vm_port="8085" host="//@infrastructure/@nodes.2" iface="//@infrastructure/@nodes.2/@ifaces.0"/> + </nodes> + <generators xsi:type="infra:ContainerImage" name="c_img" uri="docker.hub.io/ericsson/c:1.0" kind="IMAGE" generatedContainers="//@infrastructure/@nodes.3 //@infrastructure/@nodes.4"/> + <generators xsi:type="infra:ContainerImage" name="mongodb_img" uri="..." kind="IMAGE" generatedContainers="//@infrastructure/@nodes.5"/> + <credentials xsi:type="commons:KeyPair" name="ssh_key" user="ubuntu" keyfile="/home/ubuntu/.ssh/openstack.key" algorithm="RSA" bits="4096"/> + <securityGroups name="sg" ifaces="//@infrastructure/@nodes.0/@ifaces.0 //@infrastructure/@nodes.0/@ifaces.1 //@infrastructure/@nodes.0/@ifaces.2 //@infrastructure/@nodes.1/@ifaces.0 //@infrastructure/@nodes.1/@ifaces.1 //@infrastructure/@nodes.1/@ifaces.2 //@infrastructure/@nodes.2/@ifaces.0 //@infrastructure/@nodes.2/@ifaces.1 //@infrastructure/@nodes.2/@ifaces.2"> + <rules name="icmp" protocol="ICMP" fromPort="-1" toPort="-1"> + <cidr>0.0.0.0/0</cidr> + </rules> + <rules name="http" kind="INGRESS" protocol="TCP" fromPort="80" toPort="80"> + <cidr>0.0.0.0/0</cidr> + </rules> + <rules name="https" kind="INGRESS" protocol="TCP" fromPort="443" toPort="443"> + <cidr>0.0.0.0/0</cidr> + </rules> + <rules name="ssh" kind="INGRESS" protocol="TCP" fromPort="22" toPort="22"> + <cidr>0.0.0.0/0</cidr> + </rules> + </securityGroups> + <networks name="oam" protocol="TCP/IP" addressRange="16.0.0.0/24"> + <subnets name="subnet_oam_igw" protocol="TCP/IP" addressRange="16.0.1.0/26" connectedIfaces="//@infrastructure/@nodes.0/@ifaces.0"/> + <subnets name="subnet_oam_osint" protocol="TCP/IP" addressRange="16.0.1.64/26" connectedIfaces="//@infrastructure/@nodes.1/@ifaces.0"/> + <subnets name="subnet_oam_ewcf" protocol="TCP/IP" addressRange="16.0.1.128/26" connectedIfaces="//@infrastructure/@nodes.2/@ifaces.0"/> + </networks> + <networks name="net1" protocol="TCP/IP" addressRange="16.0.1.0/24"> + <subnets name="subnet_net1_igw" protocol="TCP/IP" addressRange="16.0.1.0/25" connectedIfaces="//@infrastructure/@nodes.0/@ifaces.1" connectedTo="//@infrastructure/@networks.1/@subnets.1"/> + <subnets name="subnet_net1_osint" protocol="TCP/IP" addressRange="16.0.1.64/26" connectedIfaces="//@infrastructure/@nodes.1/@ifaces.1" connectedTo="//@infrastructure/@networks.1/@subnets.0 //@infrastructure/@networks.1/@subnets.2"/> + <subnets name="subnet_net1_ewcf" protocol="TCP/IP" addressRange="16.0.1.128/26" connectedIfaces="//@infrastructure/@nodes.2/@ifaces.1" connectedTo="//@infrastructure/@networks.1/@subnets.1"/> + </networks> + <networks name="net2" protocol="TCP/IP" addressRange="16.0.2.0/24"> + <subnets name="subnet_net2_igw" protocol="TCP/IP" addressRange="16.0.2.0/25" connectedIfaces="//@infrastructure/@nodes.0/@ifaces.2"/> + </networks> + <networks name="net3" protocol="TCP/IP" addressRange="16.0.3.0/24"> + <subnets name="subnet_net3_osint" protocol="TCP/IP" addressRange="16.0.3.0/25" connectedIfaces="//@infrastructure/@nodes.1/@ifaces.2"/> + <subnets name="subnet_net3_ewcf" protocol="TCP/IP" addressRange="16.0.3.128/25" connectedIfaces="//@infrastructure/@nodes.2/@ifaces.2"/> + </networks> + </infrastructure> + <concretizations name="con_infra"> + <providers name="openstack"> + <vms name="concrete_osint_vm" maps="//@infrastructure/@nodes.1"> + <annotations xsi:type="commons:SProperty" key="vm_name" value="osint"/> + <annotations xsi:type="commons:SProperty" key="vm_key_name" value="ubuntu"/> + </vms> + <vms name="concrete_igw_vm" maps="//@infrastructure/@nodes.0"> + <annotations xsi:type="commons:SProperty" key="vm_name" value="igw"/> + <annotations xsi:type="commons:SProperty" key="vm_key_name" value="ubuntu"/> + </vms> + <vms name="concrete_ewcf_vm" maps="//@infrastructure/@nodes.2"> + <annotations xsi:type="commons:SProperty" key="vm_name" value="ewcf"/> + <annotations xsi:type="commons:SProperty" key="vm_key_name" value="ubuntu"/> + </vms> + <containerImages name="concrete_c_img" maps="//@infrastructure/@generators.0"/> + <containerImages name="concrete_mongodb_img" maps="//@infrastructure/@generators.1"/> + <networks name="concrete_oam" maps="//@infrastructure/@networks.0"> + <annotations xsi:type="commons:SProperty" key="name" value="uc3_oam"/> + </networks> + <networks name="concrete_net1" maps="//@infrastructure/@networks.1"> + <annotations xsi:type="commons:SProperty" key="name" value="uc3_net1"/> + </networks> + <networks name="concrete_net2" maps="//@infrastructure/@networks.2"> + <annotations xsi:type="commons:SProperty" key="name" value="uc3_net2"/> + </networks> + <networks name="concrete_net3" maps="//@infrastructure/@networks.3"> + <annotations xsi:type="commons:SProperty" key="name" value="uc3_net3"/> + </networks> + </providers> + </concretizations> + <optimization name="opt"> + <objectives xsi:type="optimization:MeasurableObjective" kind="min" property="cost"/> + <objectives xsi:type="optimization:MeasurableObjective" kind="max" property="performance"/> + <objectives xsi:type="optimization:MeasurableObjective" kind="max" property="availability"/> + <nonfunctionalRequirements xsi:type="commons:RangedRequirement" name="req1" description="cost <= 300" property="cost" max="300.0"/> + <nonfunctionalRequirements xsi:type="commons:RangedRequirement" name="req2" description="performance >= 7%" property="performance" min="7.0"/> + <nonfunctionalRequirements name="req3" description="elements" property="VM, Storage"/> + </optimization> + <configurations name="config1"> + <deployments component="//@application/@components.1" node="//@infrastructure/@nodes.1"/> + <deployments component="//@application/@components.0" node="//@infrastructure/@nodes.0"/> + <deployments component="//@application/@components.2" node="//@infrastructure/@nodes.2"/> + </configurations> +</commons:DOMLModel> +<?xml version="1.0" encoding="ASCII"?> +<commons:DOMLModel xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:app="http://www.piacere-project.eu/doml/application" xmlns:commons="http://www.piacere-project.eu/doml/commons" xmlns:infra="http://www.piacere-project.eu/doml/infrastructure" xmlns:optimization="http://www.piacere-project.eu/doml/optimization" name="uc3_openstack" activeConfiguration="//@configurations.0" activeInfrastructure="//@concretizations.0"> + <application name="app"> + <components xsi:type="app:SoftwareComponent" name="iwg"> + <exposedInterfaces name="net_info"/> + </components> + <components xsi:type="app:SoftwareComponent" name="osint" consumedInterfaces="//@application/@components.0/@exposedInterfaces.0 //@application/@components.3/@exposedInterfaces.0 //@application/@components.2/@exposedInterfaces.0"> + <exposedInterfaces name="osint_info"/> + </components> + <components xsi:type="app:SoftwareComponent" name="ewcf" consumedInterfaces="//@application/@components.4/@exposedInterfaces.0"> + <exposedInterfaces name="ewcf_rest_interface"/> + </components> + <components xsi:type="app:SaaS" name="external_twitter"> + <exposedInterfaces name="get_twitter" endPoint="https://twitter_api/get"/> + </components> + <components xsi:type="app:SaaS" name="external_firebase"> + <exposedInterfaces name="get_firebase" endPoint="https://firebase_api/get"/> + </components> + </application> + <infrastructure name="infra"> + <nodes xsi:type="infra:VirtualMachine" name="igw_vm" os="CentOS-7-2111" credentials="//@infrastructure/@credentials.0" sizeDescription="small-centos"> + <ifaces name="igw_vm_oam" belongsTo="//@infrastructure/@networks.0/@subnets.0" associated="//@infrastructure/@securityGroups.0"/> + <ifaces name="igw_vm_net1" belongsTo="//@infrastructure/@networks.1/@subnets.0" associated="//@infrastructure/@securityGroups.0"/> + <ifaces name="igw_vm_net2" belongsTo="//@infrastructure/@networks.2/@subnets.0" associated="//@infrastructure/@securityGroups.0"/> + </nodes> + <nodes xsi:type="infra:VirtualMachine" name="osint_vm" os="CentOS-7-2111" credentials="//@infrastructure/@credentials.0" sizeDescription="small-centos"> + <ifaces name="osint_vm_oam" belongsTo="//@infrastructure/@networks.0/@subnets.1" associated="//@infrastructure/@securityGroups.0"/> + <ifaces name="osint_vm_net1" belongsTo="//@infrastructure/@networks.1/@subnets.1" associated="//@infrastructure/@securityGroups.0"/> + <ifaces name="osint_vm_net3" belongsTo="//@infrastructure/@networks.3/@subnets.0" associated="//@infrastructure/@securityGroups.0"/> + </nodes> + <nodes xsi:type="infra:VirtualMachine" name="ewcf_vm" os="CentOS-7-2111" credentials="//@infrastructure/@credentials.0" sizeDescription="small-centos"> + <ifaces name="ewcf_vm_oam" belongsTo="//@infrastructure/@networks.0/@subnets.2" associated="//@infrastructure/@securityGroups.0"/> + <ifaces name="ewcf_vm_net1" belongsTo="//@infrastructure/@networks.1/@subnets.2" associated="//@infrastructure/@securityGroups.0"/> + <ifaces name="ewcf_vm_net3" belongsTo="//@infrastructure/@networks.3/@subnets.1" associated="//@infrastructure/@securityGroups.0"/> + </nodes> + <nodes xsi:type="infra:Container" name="c1" generatedFrom="//@infrastructure/@generators.0"> + <configs container_port="82" vm_port="8082" host="//@infrastructure/@nodes.0" iface="//@infrastructure/@nodes.0/@ifaces.0"/> + <configs container_port="80" vm_port="8080" host="//@infrastructure/@nodes.1" iface="//@infrastructure/@nodes.1/@ifaces.0"/> + </nodes> + <nodes xsi:type="infra:Container" name="c2" generatedFrom="//@infrastructure/@generators.0"> + <configs container_port="83" vm_port="8083" host="//@infrastructure/@nodes.0" iface="//@infrastructure/@nodes.0/@ifaces.1"/> + </nodes> + <nodes xsi:type="infra:Container" name="cont_mongodb" generatedFrom="//@infrastructure/@generators.1"> + <configs container_port="85" vm_port="8085" host="//@infrastructure/@nodes.2" iface="//@infrastructure/@nodes.2/@ifaces.0"/> + </nodes> + <generators xsi:type="infra:ContainerImage" name="c_img" uri="docker.hub.io/ericsson/c:1.0" kind="IMAGE" generatedContainers="//@infrastructure/@nodes.3 //@infrastructure/@nodes.4"/> + <generators xsi:type="infra:ContainerImage" name="mongodb_img" uri="..." kind="IMAGE" generatedContainers="//@infrastructure/@nodes.5"/> + <credentials xsi:type="commons:KeyPair" name="ssh_key" user="ubuntu" keyfile="/home/ubuntu/.ssh/openstack.key" algorithm="RSA" bits="4096"/> + <securityGroups name="sg" ifaces="//@infrastructure/@nodes.0/@ifaces.0 //@infrastructure/@nodes.0/@ifaces.1 //@infrastructure/@nodes.0/@ifaces.2 //@infrastructure/@nodes.1/@ifaces.0 //@infrastructure/@nodes.1/@ifaces.1 //@infrastructure/@nodes.1/@ifaces.2 //@infrastructure/@nodes.2/@ifaces.0 //@infrastructure/@nodes.2/@ifaces.1 //@infrastructure/@nodes.2/@ifaces.2"> + <rules name="icmp" protocol="ICMP" fromPort="-1" toPort="-1"> + <cidr>0.0.0.0/0</cidr> + </rules> + <rules name="http" kind="INGRESS" protocol="TCP" fromPort="80" toPort="80"> + <cidr>0.0.0.0/0</cidr> + </rules> + <rules name="https" kind="INGRESS" protocol="TCP" fromPort="443" toPort="443"> + <cidr>0.0.0.0/0</cidr> + </rules> + <rules name="ssh" kind="INGRESS" protocol="TCP" fromPort="22" toPort="22"> + <cidr>0.0.0.0/0</cidr> + </rules> + </securityGroups> + <networks name="oam" protocol="TCP/IP" addressRange="16.0.0.0/24"> + <subnets name="subnet_oam_igw" protocol="TCP/IP" addressRange="16.0.1.0/26" connectedIfaces="//@infrastructure/@nodes.0/@ifaces.0"/> + <subnets name="subnet_oam_osint" protocol="TCP/IP" addressRange="16.0.1.64/26" connectedIfaces="//@infrastructure/@nodes.1/@ifaces.0"/> + <subnets name="subnet_oam_ewcf" protocol="TCP/IP" addressRange="16.0.1.128/26" connectedIfaces="//@infrastructure/@nodes.2/@ifaces.0"/> + </networks> + <networks name="net1" protocol="TCP/IP" addressRange="16.0.1.0/24"> + <subnets name="subnet_net1_igw" protocol="TCP/IP" addressRange="16.0.1.0/25" connectedIfaces="//@infrastructure/@nodes.0/@ifaces.1" connectedTo="//@infrastructure/@networks.1/@subnets.1"/> + <subnets name="subnet_net1_osint" protocol="TCP/IP" addressRange="16.0.1.64/26" connectedIfaces="//@infrastructure/@nodes.1/@ifaces.1" connectedTo="//@infrastructure/@networks.1/@subnets.0 //@infrastructure/@networks.1/@subnets.2"/> + <subnets name="subnet_net1_ewcf" protocol="TCP/IP" addressRange="16.0.1.128/26" connectedIfaces="//@infrastructure/@nodes.2/@ifaces.1" connectedTo="//@infrastructure/@networks.1/@subnets.1"/> + </networks> + <networks name="net2" protocol="TCP/IP" addressRange="16.0.2.0/24"> + <subnets name="subnet_net2_igw" protocol="TCP/IP" addressRange="16.0.2.0/25" connectedIfaces="//@infrastructure/@nodes.0/@ifaces.2"/> + </networks> + <networks name="net3" protocol="TCP/IP" addressRange="16.0.3.0/24"> + <subnets name="subnet_net3_osint" protocol="TCP/IP" addressRange="16.0.3.0/25" connectedIfaces="//@infrastructure/@nodes.1/@ifaces.2"/> + <subnets name="subnet_net3_ewcf" protocol="TCP/IP" addressRange="16.0.3.128/25" connectedIfaces="//@infrastructure/@nodes.2/@ifaces.2"/> + </networks> + </infrastructure> + <concretizations name="con_infra"> + <providers name="openstack"> + <vms name="concrete_osint_vm" maps="//@infrastructure/@nodes.1"> + <annotations xsi:type="commons:SProperty" key="vm_name" value="osint"/> + <annotations xsi:type="commons:SProperty" key="vm_key_name" value="ubuntu"/> + </vms> + <vms name="concrete_igw_vm" maps="//@infrastructure/@nodes.0"> + <annotations xsi:type="commons:SProperty" key="vm_name" value="igw"/> + <annotations xsi:type="commons:SProperty" key="vm_key_name" value="ubuntu"/> + </vms> + <vms name="concrete_ewcf_vm" maps="//@infrastructure/@nodes.2"> + <annotations xsi:type="commons:SProperty" key="vm_name" value="ewcf"/> + <annotations xsi:type="commons:SProperty" key="vm_key_name" value="ubuntu"/> + </vms> + <containerImages name="concrete_c_img" maps="//@infrastructure/@generators.0"/> + <containerImages name="concrete_mongodb_img" maps="//@infrastructure/@generators.1"/> + <networks name="concrete_oam" maps="//@infrastructure/@networks.0"> + <annotations xsi:type="commons:SProperty" key="name" value="uc3_oam"/> + </networks> + <networks name="concrete_net1" maps="//@infrastructure/@networks.1"> + <annotations xsi:type="commons:SProperty" key="name" value="uc3_net1"/> + </networks> + <networks name="concrete_net2" maps="//@infrastructure/@networks.2"> + <annotations xsi:type="commons:SProperty" key="name" value="uc3_net2"/> + </networks> + <networks name="concrete_net3" maps="//@infrastructure/@networks.3"> + <annotations xsi:type="commons:SProperty" key="name" value="uc3_net3"/> + </networks> + </providers> + </concretizations> + <optimization name="opt"> + <objectives xsi:type="optimization:MeasurableObjective" kind="min" property="cost"/> + <objectives xsi:type="optimization:MeasurableObjective" kind="max" property="performance"/> + <objectives xsi:type="optimization:MeasurableObjective" kind="max" property="availability"/> + <nonfunctionalRequirements xsi:type="commons:RangedRequirement" name="req1" description="cost <= 300" property="cost" max="300.0"/> + <nonfunctionalRequirements xsi:type="commons:RangedRequirement" name="req2" description="performance >= 7%" property="performance" min="7.0"/> + <nonfunctionalRequirements name="req3" description="elements" property="VM, Storage"/> + </optimization> + <configurations name="config1"> + <deployments component="//@application/@components.1" node="//@infrastructure/@nodes.1"/> + <deployments component="//@application/@components.0" node="//@infrastructure/@nodes.0"/> + <deployments component="//@application/@components.2" node="//@infrastructure/@nodes.2"/> + </configurations> +</commons:DOMLModel> diff --git a/doc/nginx_example/nginx_example_v2.0/nginx_example_v2.0.doml b/doc/nginx_example/nginx_example_v2.0/nginx_example_v2.0.doml new file mode 100644 index 0000000..da36cde --- /dev/null +++ b/doc/nginx_example/nginx_example_v2.0/nginx_example_v2.0.doml @@ -0,0 +1,98 @@ +doml nginx_openstack + +application app { + + software_component nginx { + properties { + // site + source_code="/usr/share/nginx/html/index.html"; + } + } +} + +infrastructure infra { + vm nginx_vm { + os "CentOS-7-2111" + iface i1 { + belongs_to subnet1 + } + credentials nginx_vm_credentials + } + + key_pair nginx_vm_credentials { + user "vm2user" + keyfile "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD3F6tyPEFEzV0LX3X8BsXdMsQz1x2cEikKDEY0aIj41qgxMCP/iteneqXSIFZBp5vizPvaoIR3Um9xK7PGoW8giupGn+EPuxIA4cDM4vzOqOkiMPhz5XK0whEjkVzTo4+S0puvDZuwIsdiW9mxhJc7tgBNL0cYlWSYVkz4G/fslNfRPW5mYAM49f4fhtxPb5ok4Q2Lg9dPKVHO/Bgeu5woMc7RY0p1ej6D4CKFE6lymSDJpW0YHX/wqE9+cfEauh7xZcG0q9t2ta6F6fmX0agvpFyZo8aFbXeUBr7osSCJNgvavWbM/06niWrOvYX2xwWdhXmXSrbX8ZbabVohBK41 email@example.com" + } + + net net1 { + address "16.0.0.0/16" + protocol "tcp/ip" + subnet subnet1 { + address "10.100.1.0/24" + protocol "tcp/ip" + } + } + + security_group sg { + egress icmp { + from_port -1 + to_port -1 + protocol "icmp" + cidr ["0.0.0.0/0"] + } + ingress http { + from_port 80 + to_port 80 + protocol "tcp" + cidr ["0.0.0.0/0"] + } + ingress https { + from_port 443 + to_port 443 + protocol "tcp" + cidr ["0.0.0.0/0"] + } + ingress ssh { + from_port 22 + to_port 22 + protocol "tcp" + cidr ["0.0.0.0/0"] + } + ifaces i1 + } +} + +deployment config { + nginx -> nginx_vm +} + +active deployment config + +concretizations { + concrete_infrastructure con_infra { + provider openstack { + vm nginx_host { + properties { + vm_flavor = "small-centos"; + } + maps nginx_vm + } + + net nginx_net { + properties {} + maps net1 + } + } + } + active con_infra +} + +optimization opt { + objectives { + "cost" => min + } + nonfunctional_requirements { + req1 "Cost <= 200" max 200.0 => "cost"; + req2 "Provider" values "OPEN" => "provider"; + } +} diff --git a/doc/nginx_example/nginx_example_v2.0/nginx_example_v2.0.domlx b/doc/nginx_example/nginx_example_v2.0/nginx_example_v2.0.domlx new file mode 100644 index 0000000..a6fa917 --- /dev/null +++ b/doc/nginx_example/nginx_example_v2.0/nginx_example_v2.0.domlx @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="ASCII"?> +<commons:DOMLModel xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:app="http://www.piacere-project.eu/doml/application" xmlns:commons="http://www.piacere-project.eu/doml/commons" xmlns:infra="http://www.piacere-project.eu/doml/infrastructure" xmlns:optimization="http://www.piacere-project.eu/doml/optimization" name="nginx_openstack" activeConfiguration="//@configurations.0" activeInfrastructure="//@concretizations.0"> + <application name="app"> + <components xsi:type="app:SoftwareComponent" name="nginx"> + <annotations xsi:type="commons:SProperty" key="source_code" value="/usr/share/nginx/html/index.html"/> + </components> + </application> + <infrastructure name="infra"> + <nodes xsi:type="infra:VirtualMachine" name="nginx_vm" os="CentOS-7-2111" credentials="//@infrastructure/@credentials.0"> + <ifaces name="i1" belongsTo="//@infrastructure/@networks.0/@subnets.0" associated="//@infrastructure/@securityGroups.0"/> + </nodes> + <credentials xsi:type="infra:KeyPair" name="nginx_vm_credentials" user="vm2user" keyfile="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD3F6tyPEFEzV0LX3X8BsXdMsQz1x2cEikKDEY0aIj41qgxMCP/iteneqXSIFZBp5vizPvaoIR3Um9xK7PGoW8giupGn+EPuxIA4cDM4vzOqOkiMPhz5XK0whEjkVzTo4+S0puvDZuwIsdiW9mxhJc7tgBNL0cYlWSYVkz4G/fslNfRPW5mYAM49f4fhtxPb5ok4Q2Lg9dPKVHO/Bgeu5woMc7RY0p1ej6D4CKFE6lymSDJpW0YHX/wqE9+cfEauh7xZcG0q9t2ta6F6fmX0agvpFyZo8aFbXeUBr7osSCJNgvavWbM/06niWrOvYX2xwWdhXmXSrbX8ZbabVohBK41 email@example.com"/> + <securityGroups name="sg" ifaces="//@infrastructure/@nodes.0/@ifaces.0"> + <rules name="icmp" protocol="icmp" fromPort="-1" toPort="-1"> + <cidr>0.0.0.0/0</cidr> + </rules> + <rules name="http" kind="INGRESS" protocol="tcp" fromPort="80" toPort="80"> + <cidr>0.0.0.0/0</cidr> + </rules> + <rules name="https" kind="INGRESS" protocol="tcp" fromPort="443" toPort="443"> + <cidr>0.0.0.0/0</cidr> + </rules> + <rules name="ssh" kind="INGRESS" protocol="tcp" fromPort="22" toPort="22"> + <cidr>0.0.0.0/0</cidr> + </rules> + </securityGroups> + <networks name="net1" protocol="tcp/ip" addressRange="16.0.0.0/16"> + <subnets name="subnet1" protocol="tcp/ip" addressRange="10.100.1.0/24" connectedIfaces="//@infrastructure/@nodes.0/@ifaces.0"/> + </networks> + </infrastructure> + <concretizations name="con_infra"> + <providers name="openstack"> + <vms name="nginx_host" maps="//@infrastructure/@nodes.0"> + <annotations xsi:type="commons:SProperty" key="vm_flavor" value="small-centos"/> + </vms> + <networks name="nginx_net" maps="//@infrastructure/@networks.0"/> + </providers> + </concretizations> + <optimization name="opt"> + <objectives xsi:type="optimization:MeasurableObjective" kind="min" property="cost"/> + <nonfunctionalRequirements xsi:type="commons:RangedRequirement" name="req1" description="Cost <= 200" property="cost" max="200.0"/> + <nonfunctionalRequirements xsi:type="commons:EnumeratedRequirement" name="req2" description="Provider" property="provider"> + <values>OPEN</values> + </nonfunctionalRequirements> + </optimization> + <configurations name="config"> + <deployments component="//@application/@components.0" node="//@infrastructure/@nodes.0"/> + </configurations> +</commons:DOMLModel> diff --git a/doc/nginx_example/nginx_example_v2.1/nginx_aws.doml b/doc/nginx_example/nginx_example_v2.1/nginx_aws.doml new file mode 100644 index 0000000..e69de29 diff --git a/doc/nginx_example/nginx_example_v2.1/nginx_openstack.doml b/doc/nginx_example/nginx_example_v2.1/nginx_openstack.doml new file mode 100644 index 0000000..81c734b --- /dev/null +++ b/doc/nginx_example/nginx_example_v2.1/nginx_openstack.doml @@ -0,0 +1,98 @@ +doml nginx_openstack + +application app { + + software_component nginx { + properties { + // site + source_code="/usr/share/nginx/html/index.html"; + } + } +} + +infrastructure infra { + vm nginx_vm { + os "CentOS-7-2111" + iface i1 { + belongs_to subnet1 + } + credentials user1 + } + + key_pair user1 { + user "ubuntu" + name "ubuntu" + } + + net net1 { + address "16.0.0.0/16" + protocol "tcp/ip" + subnet subnet1 { + address "10.100.1.0/24" + protocol "tcp/ip" + } + } + + security_group sg { + egress icmp { + from_port -1 + to_port -1 + protocol "icmp" + cidr ["0.0.0.0/0"] + } + ingress http { + from_port 80 + to_port 80 + protocol "tcp" + cidr ["0.0.0.0/0"] + } + ingress https { + from_port 443 + to_port 443 + protocol "tcp" + cidr ["0.0.0.0/0"] + } + ingress ssh { + from_port 22 + to_port 22 + protocol "tcp" + cidr ["0.0.0.0/0"] + } + ifaces i1 + } +} + +deployment config { + nginx -> nginx_vm +} + +active deployment config + +concretizations { + concrete_infrastructure con_infra { + provider openstack { + vm nginx_host { + properties { + vm_flavor = "small-centos"; + } + maps nginx_vm + } + + net nginx_net { + properties {} + maps net1 + } + } + } + active con_infra +} + +optimization opt { + objectives { + "cost" => min + } + nonfunctional_requirements { + req1 "Cost <= 200" max 200.0 => "cost"; + req2 "Provider" values "OPEN" => "provider"; + } +} diff --git a/doc/nginx_example/nginx_example_v2.1/nginx_openstack.domlx b/doc/nginx_example/nginx_example_v2.1/nginx_openstack.domlx new file mode 100644 index 0000000..0e77e5a --- /dev/null +++ b/doc/nginx_example/nginx_example_v2.1/nginx_openstack.domlx @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="ASCII"?> +<commons:DOMLModel xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:app="http://www.piacere-project.eu/doml/application" xmlns:commons="http://www.piacere-project.eu/doml/commons" xmlns:infra="http://www.piacere-project.eu/doml/infrastructure" xmlns:optimization="http://www.piacere-project.eu/doml/optimization" name="nginx_openstack" activeConfiguration="//@configurations.0" activeInfrastructure="//@concretizations.0"> + <application name="app"> + <components xsi:type="app:SoftwareComponent" name="nginx"> + <annotations xsi:type="commons:SProperty" key="source_code" value="/usr/share/nginx/html/index.html"/> + </components> + </application> + <infrastructure name="infra"> + <nodes xsi:type="infra:VirtualMachine" name="nginx_vm" os="CentOS-7-2111" credentials="//@infrastructure/@credentials.0"> + <ifaces name="i1" belongsTo="//@infrastructure/@networks.0/@subnets.0" associated="//@infrastructure/@securityGroups.0"/> + </nodes> + <credentials xsi:type="infra:KeyPair" name="user1" user="user-test"/> + <securityGroups name="sg" ifaces="//@infrastructure/@nodes.0/@ifaces.0"> + <rules name="icmp" protocol="icmp" fromPort="-1" toPort="-1"> + <cidr>0.0.0.0/0</cidr> + </rules> + <rules name="http" kind="INGRESS" protocol="tcp" fromPort="80" toPort="80"> + <cidr>0.0.0.0/0</cidr> + </rules> + <rules name="https" kind="INGRESS" protocol="tcp" fromPort="443" toPort="443"> + <cidr>0.0.0.0/0</cidr> + </rules> + <rules name="ssh" kind="INGRESS" protocol="tcp" fromPort="22" toPort="22"> + <cidr>0.0.0.0/0</cidr> + </rules> + </securityGroups> + <networks name="net1" protocol="tcp/ip" addressRange="16.0.0.0/16"> + <subnets name="subnet1" protocol="tcp/ip" addressRange="10.100.1.0/24" connectedIfaces="//@infrastructure/@nodes.0/@ifaces.0"/> + </networks> + </infrastructure> + <concretizations name="con_infra"> + <providers name="openstack"> + <vms name="nginx_host" maps="//@infrastructure/@nodes.0"> + <annotations xsi:type="commons:SProperty" key="vm_flavor" value="small-centos"/> + </vms> + <networks name="nginx_net" maps="//@infrastructure/@networks.0"/> + </providers> + </concretizations> + <optimization name="opt"> + <objectives xsi:type="optimization:MeasurableObjective" kind="min" property="cost"/> + <nonfunctionalRequirements xsi:type="commons:RangedRequirement" name="req1" description="Cost <= 200" property="cost" max="200.0"/> + <nonfunctionalRequirements xsi:type="commons:EnumeratedRequirement" name="req2" description="Provider" property="provider"> + <values>OPEN</values> + </nonfunctionalRequirements> + </optimization> + <configurations name="config"> + <deployments component="//@application/@components.0" node="//@infrastructure/@nodes.0"/> + </configurations> +</commons:DOMLModel> diff --git a/doc/nginx_example/nginx_example_v2.2/nginx_openstack.doml b/doc/nginx_example/nginx_example_v2.2/nginx_openstack.doml new file mode 100644 index 0000000..3cfdcc7 --- /dev/null +++ b/doc/nginx_example/nginx_example_v2.2/nginx_openstack.doml @@ -0,0 +1,98 @@ +doml nginx_openstack +version "2.2.2" + +application app { + + software_component nginx { + properties { + // site + source_code="/usr/share/nginx/html/index.html"; + } + } +} + +infrastructure infra { + vm nginx_vm { + os "Ubuntu-Focal-20.04-Daily-2022-04-19" + iface i1 { + belongs_to subnet1 + } + credentials ubuntu + } + + key_pair ubuntu { + user "ubuntu" + } + + net net1 { + cidr "16.0.0.0/16" + protocol "tcp/ip" + subnet subnet1 { + cidr "16.0.0.1/24" + protocol "tcp/ip" + } + } + + security_group sg { + egress icmp { + from_port -1 + to_port -1 + protocol "icmp" + cidr ["0.0.0.0/0"] + } + ingress http { + from_port 80 + to_port 80 + protocol "tcp" + cidr ["0.0.0.0/0"] + } + ingress https { + from_port 443 + to_port 443 + protocol "tcp" + cidr ["0.0.0.0/0"] + } + ingress ssh { + from_port 22 + to_port 22 + protocol "tcp" + cidr ["0.0.0.0/0"] + } + ifaces i1 + } +} + +deployment conf { + nginx -> nginx_vm +} + +active deployment conf + +concretizations { + concrete_infrastructure con_infra { + provider openstack { + vm nginx_host { + properties { + vm_flavor = "small-centos"; + } + maps nginx_vm + } + + net nginx_net { + properties {} + maps net1 + } + } + } + active con_infra +} + +optimization opt { + objectives { + "cost" => min + } + nonfunctional_requirements { + req1 "Cost <= 200" max 200.0 => "cost"; + req2 "Provider" values "OPEN" => "provider"; + } +} diff --git a/doc/nginx_example/nginx_example_v2.2/nginx_openstack.domlx b/doc/nginx_example/nginx_example_v2.2/nginx_openstack.domlx new file mode 100644 index 0000000..a3038f1 --- /dev/null +++ b/doc/nginx_example/nginx_example_v2.2/nginx_openstack.domlx @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="ASCII"?> +<commons:DOMLModel xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:app="http://www.piacere-project.eu/doml/application" xmlns:commons="http://www.piacere-project.eu/doml/commons" xmlns:infra="http://www.piacere-project.eu/doml/infrastructure" xmlns:optimization="http://www.piacere-project.eu/doml/optimization" name="nginx_openstack" version="2.2.2" activeConfiguration="//@configurations.0" activeInfrastructure="//@concretizations.0"> + <application name="app"> + <components xsi:type="app:SoftwareComponent" name="nginx"> + <annotations xsi:type="commons:SProperty" key="source_code" value="/usr/share/nginx/html/index.html"/> + </components> + </application> + <infrastructure name="infra"> + <nodes xsi:type="infra:VirtualMachine" name="nginx_vm" os="Ubuntu-Focal-20.04-Daily-2022-04-19" credentials="//@infrastructure/@credentials.0"> + <ifaces name="i1" belongsTo="//@infrastructure/@networks.0/@subnets.0" associated="//@infrastructure/@securityGroups.0"/> + </nodes> + <credentials xsi:type="commons:KeyPair" name="ubuntu" user="ubuntu"/> + <securityGroups name="sg" ifaces="//@infrastructure/@nodes.0/@ifaces.0"> + <rules name="icmp" protocol="icmp" fromPort="-1" toPort="-1"> + <cidr>0.0.0.0/0</cidr> + </rules> + <rules name="http" kind="INGRESS" protocol="tcp" fromPort="80" toPort="80"> + <cidr>0.0.0.0/0</cidr> + </rules> + <rules name="https" kind="INGRESS" protocol="tcp" fromPort="443" toPort="443"> + <cidr>0.0.0.0/0</cidr> + </rules> + <rules name="ssh" kind="INGRESS" protocol="tcp" fromPort="22" toPort="22"> + <cidr>0.0.0.0/0</cidr> + </rules> + </securityGroups> + <networks name="net1" protocol="tcp/ip" addressRange="16.0.0.0/16"> + <subnets name="subnet1" protocol="tcp/ip" addressRange="16.0.0.1/24" connectedIfaces="//@infrastructure/@nodes.0/@ifaces.0"/> + </networks> + </infrastructure> + <concretizations name="con_infra"> + <providers name="openstack"> + <vms name="nginx_host" maps="//@infrastructure/@nodes.0"> + <annotations xsi:type="commons:SProperty" key="vm_flavor" value="small-centos"/> + </vms> + <networks name="nginx_net" maps="//@infrastructure/@networks.0"/> + </providers> + </concretizations> + <optimization name="opt"> + <objectives xsi:type="optimization:MeasurableObjective" kind="min" property="cost"/> + <nonfunctionalRequirements xsi:type="commons:RangedRequirement" name="req1" description="Cost <= 200" property="cost" max="200.0"/> + <nonfunctionalRequirements xsi:type="commons:EnumeratedRequirement" name="req2" description="Provider" property="provider"> + <values>OPEN</values> + </nonfunctionalRequirements> + </optimization> + <configurations name="conf"> + <deployments component="//@application/@components.0" node="//@infrastructure/@nodes.0"/> + </configurations> +</commons:DOMLModel> \ No newline at end of file diff --git a/doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/config.yaml b/doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/config.yaml new file mode 100644 index 0000000..082e5e5 --- /dev/null +++ b/doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/config.yaml @@ -0,0 +1,6 @@ +--- +iac: +- terraform +- piacere_monitoring +- nginx +... \ No newline at end of file diff --git a/doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/nginx/config.yaml b/doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/nginx/config.yaml new file mode 100644 index 0000000..dff8ba5 --- /dev/null +++ b/doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/nginx/config.yaml @@ -0,0 +1,8 @@ + +--- +input: + - instance_ip_vm1 + - instance_server_private_key_ssh_key +output: [] +engine: ansible +... diff --git a/doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/nginx/inventory.j2 b/doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/nginx/inventory.j2 new file mode 100644 index 0000000..c869825 --- /dev/null +++ b/doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/nginx/inventory.j2 @@ -0,0 +1,9 @@ + + +[servers_for_nginx] +{{ instance_ip_vm1 }} + +[servers_for_nginx:vars] +ansible_connection=ssh +ansible_user=ubuntu +ansible_ssh_private_key_file=ssh_key diff --git a/doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/nginx/main.yml b/doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/nginx/main.yml new file mode 100644 index 0000000..7cd932f --- /dev/null +++ b/doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/nginx/main.yml @@ -0,0 +1,44 @@ + + +--- +- hosts: servers_for_nginx + gather_facts: no + become: yes + tasks: + - name: Update repositories + apt: + update_cache: yes + + - name: Install nginx + package: + name: nginx + + - name: Start nginx + service: + name: nginx + enabled: yes + state: started + + - name: Set attributes + set_stats: + data: + site_config_dir: /etc/nginx/conf.d + + - name: Install sample site + copy: + dest: "{{ item }}" + content: | + <!doctype html> + <html lang="en"> + <head> + <title>Hello World!</title> + </head> + <body> + <h1>Sample web page</h1> + <p>With little content ;)</p> + </body> + </html> + with_items: + - /var/www/html/index.html + - /usr/share/nginx/html/index.html + diff --git a/doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/nginx/ssh_key.j2 b/doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/nginx/ssh_key.j2 new file mode 100644 index 0000000..4d512f8 --- /dev/null +++ b/doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/nginx/ssh_key.j2 @@ -0,0 +1 @@ +{{ instance_server_private_key_ssh_key }} diff --git a/output_files_generated/nginx_openstack/piacere_monitoring/ansible.cfg b/doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/piacere_monitoring/ansible.cfg similarity index 100% rename from output_files_generated/nginx_openstack/piacere_monitoring/ansible.cfg rename to doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/piacere_monitoring/ansible.cfg diff --git a/output_files_generated/nginx_openstack/piacere_monitoring/ansible_requirements.yml b/doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/piacere_monitoring/ansible_requirements.yml similarity index 81% rename from output_files_generated/nginx_openstack/piacere_monitoring/ansible_requirements.yml rename to doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/piacere_monitoring/ansible_requirements.yml index 58c0cb3..47808cf 100644 --- a/output_files_generated/nginx_openstack/piacere_monitoring/ansible_requirements.yml +++ b/doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/piacere_monitoring/ansible_requirements.yml @@ -1,8 +1,8 @@ roles: # - name: dj-wasabi.telegraf -# version: 0.13.2 +# version: 0.13.3 # source: https://galaxy.ansible.com - name: dj-wasabi.telegraf src: https://github.com/dj-wasabi/ansible-telegraf.git scm: git - version: 0.13.2 + version: 0.13.3 diff --git a/doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/piacere_monitoring/config.yaml b/doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/piacere_monitoring/config.yaml new file mode 100644 index 0000000..dff8ba5 --- /dev/null +++ b/doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/piacere_monitoring/config.yaml @@ -0,0 +1,8 @@ + +--- +input: + - instance_ip_vm1 + - instance_server_private_key_ssh_key +output: [] +engine: ansible +... diff --git a/output_files_generated/nginx_openstack/piacere_monitoring/hosts.yaml b/doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/piacere_monitoring/hosts.yaml similarity index 100% rename from output_files_generated/nginx_openstack/piacere_monitoring/hosts.yaml rename to doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/piacere_monitoring/hosts.yaml diff --git a/output_files_generated/nginx_openstack/piacere_monitoring/install_playbook_requirements.sh b/doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/piacere_monitoring/install_playbook_requirements.sh similarity index 100% rename from output_files_generated/nginx_openstack/piacere_monitoring/install_playbook_requirements.sh rename to doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/piacere_monitoring/install_playbook_requirements.sh diff --git a/output_files_generated/nginx_openstack/piacere_monitoring/inventory.j2 b/doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/piacere_monitoring/inventory.j2 similarity index 100% rename from output_files_generated/nginx_openstack/piacere_monitoring/inventory.j2 rename to doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/piacere_monitoring/inventory.j2 diff --git a/output_files_generated/nginx_openstack/piacere_monitoring/main.yml b/doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/piacere_monitoring/main.yml similarity index 100% rename from output_files_generated/nginx_openstack/piacere_monitoring/main.yml rename to doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/piacere_monitoring/main.yml diff --git a/output_files_generated/nginx_openstack/piacere_monitoring/run-playbook.sh b/doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/piacere_monitoring/run-playbook.sh similarity index 100% rename from output_files_generated/nginx_openstack/piacere_monitoring/run-playbook.sh rename to doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/piacere_monitoring/run-playbook.sh diff --git a/output_files_generated/nginx_openstack/piacere_monitoring/site.yaml b/doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/piacere_monitoring/site.yaml similarity index 100% rename from output_files_generated/nginx_openstack/piacere_monitoring/site.yaml rename to doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/piacere_monitoring/site.yaml diff --git a/output_files_generated/nginx_openstack/piacere_monitoring/site_requirements.yaml b/doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/piacere_monitoring/site_requirements.yaml similarity index 100% rename from output_files_generated/nginx_openstack/piacere_monitoring/site_requirements.yaml rename to doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/piacere_monitoring/site_requirements.yaml diff --git a/doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/piacere_monitoring/ssh_key.j2 b/doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/piacere_monitoring/ssh_key.j2 new file mode 100644 index 0000000..4d512f8 --- /dev/null +++ b/doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/piacere_monitoring/ssh_key.j2 @@ -0,0 +1 @@ +{{ instance_server_private_key_ssh_key }} diff --git a/output_files_generated/nginx_openstack/piacere_monitoring/vars/main.yaml b/doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/piacere_monitoring/vars/main.yaml similarity index 100% rename from output_files_generated/nginx_openstack/piacere_monitoring/vars/main.yaml rename to doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/piacere_monitoring/vars/main.yaml diff --git a/doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/terraform/config.yaml b/doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/terraform/config.yaml new file mode 100644 index 0000000..21942c2 --- /dev/null +++ b/doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/terraform/config.yaml @@ -0,0 +1,16 @@ + + +--- +engine: terraform +input: + - OS_USERNAME + - OS_PASSWORD + - OS_AUTH_URL + - OS_PROJECT_NAME +output: + + - instance_server_public_key_ssh_key + - instance_server_private_key_ssh_key + - instance_ip_vm1 + +... diff --git a/doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/terraform/main.tf b/doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/terraform/main.tf new file mode 100644 index 0000000..0760aa2 --- /dev/null +++ b/doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/terraform/main.tf @@ -0,0 +1,148 @@ + + +terraform { +required_version = ">= 0.14.0" + required_providers { + openstack = { + source = "terraform-provider-openstack/openstack" + version = "~> 1.35.0" + } + } +} + +# Configure the OpenStack Provider +provider "openstack" { + insecure = true +} + +# Retrieve data +data "openstack_networking_network_v2" "external" { + name = "external" +} + + +# Create virtual machine +resource "openstack_compute_instance_v2" "vm1" { + name = "nginx-host" + image_name = "ubuntu-20.04.3" + flavor_name = "small" + key_pair = openstack_compute_keypair_v2.ssh_key.name + network { + port = openstack_networking_port_v2.net1.id + } +} + +# Create floating ip +resource "openstack_networking_floatingip_v2" "vm1_floating_ip" { + pool = "external" + # fixed_ip = "" +} + +# Attach floating ip to instance +resource "openstack_compute_floatingip_associate_v2" "vm1_floating_ip_association" { + floating_ip = openstack_networking_floatingip_v2.vm1_floating_ip.address + instance_id = openstack_compute_instance_v2.vm1.id +} + + + +## Network + +# Create Network +resource "openstack_networking_network_v2" "net1" { + name = "concrete_net" +} + +# Create Subnet +resource "openstack_networking_subnet_v2" "net1_subnet" { + name = "concrete_net_subnet" + network_id = openstack_networking_network_v2.net1.id + cidr = "10.0.0.0/24" + dns_nameservers = ["8.8.8.8", "8.8.8.4"] +} + +# Attach networking port +resource "openstack_networking_port_v2" "net1" { + name = "concrete_net" + network_id = openstack_networking_network_v2.net1.id + admin_state_up = true + security_group_ids = [ + openstack_compute_secgroup_v2.icmp.id, + openstack_compute_secgroup_v2.http.id, + openstack_compute_secgroup_v2.https.id, + openstack_compute_secgroup_v2.ssh.id, + + ] + fixed_ip { + subnet_id = openstack_networking_subnet_v2.net1_subnet.id + } +} + +# Create router +resource "openstack_networking_router_v2" "net1_router" { + name = "net1_router" + external_network_id = data.openstack_networking_network_v2.external.id #External network id +} +# Router interface configuration +resource "openstack_networking_router_interface_v2" "net1_router_interface" { + router_id = openstack_networking_router_v2.net1_router.id + subnet_id = openstack_networking_subnet_v2.net1_subnet.id +} + + + +# CREATING SECURITY_GROUP + +resource "openstack_compute_secgroup_v2" "icmp" { + name = "icmp" + description = "Security group rule for port -1" + rule { + from_port = -1 + to_port = -1 + ip_protocol = "icmp" + cidr = "0.0.0.0/0" + } +} + +resource "openstack_compute_secgroup_v2" "http" { + name = "http" + description = "Security group rule for port 80" + rule { + from_port = 80 + to_port = 80 + ip_protocol = "tcp" + cidr = "0.0.0.0/0" + } +} + +resource "openstack_compute_secgroup_v2" "https" { + name = "https" + description = "Security group rule for port 443" + rule { + from_port = 443 + to_port = 443 + ip_protocol = "tcp" + cidr = "0.0.0.0/0" + } +} + +resource "openstack_compute_secgroup_v2" "ssh" { + name = "ssh" + description = "Security group rule for port 22" + rule { + from_port = 22 + to_port = 22 + ip_protocol = "tcp" + cidr = "0.0.0.0/0" + } +} + + + + +# Create ssh keys +resource "openstack_compute_keypair_v2" "ssh_key" { + name = "ubuntu" + # public_key = "ubuntu" +} + diff --git a/doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/terraform/output.tf b/doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/terraform/output.tf new file mode 100644 index 0000000..5c4fe27 --- /dev/null +++ b/doc/nginx_example/nginx_paper_domlv1.0/nginx_openstack/terraform/output.tf @@ -0,0 +1,14 @@ + + +output "instance_server_public_key_ssh_key" { + value = openstack_compute_keypair_v2.ssh_key.public_key +} + +output "instance_server_private_key_ssh_key" { + value = openstack_compute_keypair_v2.ssh_key.private_key +} + +output "instance_ip_vm1" { + value = openstack_compute_floatingip_associate_v2.vm1_floating_ip_association.floating_ip +} + diff --git a/doc/nginx_example/nginx_paper_domlv1.0/nginx_paper.doml b/doc/nginx_example/nginx_paper_domlv1.0/nginx_paper.doml new file mode 100644 index 0000000..8b99d3a --- /dev/null +++ b/doc/nginx_example/nginx_paper_domlv1.0/nginx_paper.doml @@ -0,0 +1,108 @@ +doml nginx_openstack + +application app { + + software_component nginx { + properties { + // site + source_code="/usr/share/nginx/html/index.html"; + } + } +} + +infrastructure infra { + + vm_image vm_img { + generates vm1 + } + + net net1 { + address "10.0.0.0/24" + protocol "tcp/ip" + } + + security_group sg { + egress icmp { + from_port -1 + to_port -1 + protocol "icmp" + cidr ["0.0.0.0/0"] + } + ingress http { + from_port 80 + to_port 80 + protocol "tcp" + cidr ["0.0.0.0/0"] + } + ingress https { + from_port 443 + to_port 443 + protocol "tcp" + cidr ["0.0.0.0/0"] + } + ingress ssh { + from_port 22 + to_port 22 + protocol "tcp" + cidr ["0.0.0.0/0"] + } + ifaces i1 + nodes vm1 + } + + key_pair ssh_key { + user "ubuntu" + keyfile "/home/user1/.ssh/openstack.key" + algorithm "RSA" + bits 4096 + } + + autoscale_group ag { + vm vm1 { + os "ubuntu-20.04.3" + iface i1 { + address "10.0.0.1" + belongs_to net1 + security sg + } + credentials ssh_key + } + network net1 + } +} + +deployment config { + nginx -> vm1 +} + +active deployment config + +concretizations { + concrete_infrastructure con_infra { + provider openstack { + vm concrete_vm { + properties { + vm_name = "nginx-host"; + vm_flavor = "small"; + vm_key_name = "user1"; + } + maps vm1 + } + + vm_image concrete_vm_image { + properties { + name = "ubuntu-20.04.3"; + } + maps vm_img + } + + net concrete_net { + properties { + name = "ostack2"; + } + maps net1 + } + } + } + active con_infra +} \ No newline at end of file diff --git a/doc/nginx_example/nginx_paper_domlv1.0/nginx_paper.domlx b/doc/nginx_example/nginx_paper_domlv1.0/nginx_paper.domlx new file mode 100644 index 0000000..1ca3bc2 --- /dev/null +++ b/doc/nginx_example/nginx_paper_domlv1.0/nginx_paper.domlx @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="ASCII"?> +<commons:DOMLModel xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:app="http://www.piacere-project.eu/doml/application" xmlns:commons="http://www.piacere-project.eu/doml/commons" xmlns:infra="http://www.piacere-project.eu/doml/infrastructure" name="nginx_openstack" activeConfiguration="//@configurations.0" activeInfrastructure="//@concretizations.0"> + <application name="app"> + <components xsi:type="app:SoftwareComponent" name="nginx"> + <annotations xsi:type="commons:SProperty" key="source_code" value="/usr/share/nginx/html/index.html"/> + </components> + </application> + <infrastructure name="infra"> + <nodes xsi:type="infra:AutoScalingGroup" name="ag" deploymentNetwork="//@infrastructure/@networks.0"> + <machineDefinition name="vm1" os="ubuntu-20.04.3" credentials="//@infrastructure/@credentials.0" group="//@infrastructure/@groups.0" generatedFrom="//@infrastructure/@generators.0"> + <ifaces name="i1" endPoint="10.0.0.1" belongsTo="//@infrastructure/@networks.0" associated="//@infrastructure/@groups.0"/> + </machineDefinition> + </nodes> + <networks name="net1" protocol="tcp/ip" addressRange="10.0.0.0/24" connectedIfaces="//@infrastructure/@nodes.0/@machineDefinition/@ifaces.0"/> + <generators xsi:type="infra:VMImage" name="vm_img" generatedVMs="//@infrastructure/@nodes.0/@machineDefinition"/> + <credentials xsi:type="infra:KeyPair" name="ssh_key" user="ubuntu" keyfile="/home/user1/.ssh/openstack.key" algorithm="RSA" bits="4096"/> + <groups xsi:type="infra:SecurityGroup" name="sg" groupedNodes="//@infrastructure/@nodes.0/@machineDefinition" ifaces="//@infrastructure/@nodes.0/@machineDefinition/@ifaces.0"> + <rules name="icmp" protocol="icmp" fromPort="-1" toPort="-1"> + <cidr>0.0.0.0/0</cidr> + </rules> + <rules name="http" kind="INGRESS" protocol="tcp" fromPort="80" toPort="80"> + <cidr>0.0.0.0/0</cidr> + </rules> + <rules name="https" kind="INGRESS" protocol="tcp" fromPort="443" toPort="443"> + <cidr>0.0.0.0/0</cidr> + </rules> + <rules name="ssh" kind="INGRESS" protocol="tcp" fromPort="22" toPort="22"> + <cidr>0.0.0.0/0</cidr> + </rules> + </groups> + </infrastructure> + <concretizations name="con_infra"> + <providers name="openstack"> + <vms name="concrete_vm" maps="//@infrastructure/@nodes.0/@machineDefinition"> + <annotations xsi:type="commons:SProperty" key="vm_name" value="nginx-host"/> + <annotations xsi:type="commons:SProperty" key="vm_flavor" value="small"/> + <annotations xsi:type="commons:SProperty" key="vm_key_name" value="user1"/> + </vms> + <vmImages name="concrete_vm_image" maps="//@infrastructure/@generators.0"> + <annotations xsi:type="commons:SProperty" key="name" value="ubuntu-20.04.3"/> + </vmImages> + <networks name="concrete_net" maps="//@infrastructure/@networks.0"> + <annotations xsi:type="commons:SProperty" key="name" value="ostack2"/> + </networks> + </providers> + </concretizations> + <configurations name="config"> + <deployments component="//@application/@components.0" node="//@infrastructure/@nodes.0/@machineDefinition"/> + </configurations> +</commons:DOMLModel> diff --git a/doc/posidonia/posidonia.doml b/doc/posidonia/posidonia.doml new file mode 100644 index 0000000..e69de29 diff --git a/doc/posidonia/posidoniav2.2.2.doml b/doc/posidonia/posidoniav2.2.2.doml new file mode 100644 index 0000000..97c6eb3 --- /dev/null +++ b/doc/posidonia/posidoniav2.2.2.doml @@ -0,0 +1,268 @@ +doml posidonia +properties { + entorno="pre"; + proyecto="baleares"; +} + +/* Application Definition +application posidonia { + software_component Gestaut { + provides { http https } + consumes { dbAccess, search } + } + software_component ElasticSearch { + provides { search } + consumes { dbAccess } + } + software_component Edi { + provides { edi } + consumes { dbAccess } + } + dbms Database { + provides { + dbAccess + } + } +}*/ + +infrastructure abstractInfra { + // Networks + net vpc { + cidr "/16" + protocol "tcp/ip" + subnet subnet1 { + cidr "/24" + protocol "tcp/ip" + } + subnet subnet2 { + cidr "/24" + protocol "tcp/ip" + } + subnet subnet3 { + cidr "/24" + protocol "tcp/ip" + } + } + // Credentials + // TODO: key file? + key_pair GestautKeyName { + algorithm "RSA" + bits 4096 + } + key_pair ESKeyName { + algorithm "RSA" + bits 4096 + } + key_pair EdiKeyName { + algorithm "RSA" + bits 4096 + } + user_pass dbCredentials { + user "***" + pass "***" + } + + // Nodes + vm OracleDB { + os "Ubuntu" + size "t2.small" + iface db1 { + belongs_to subnet1 + } + iface db2 { + belongs_to subnet2 + } + iface db3 { + belongs_to subnet3 + } + sto "20" + credentials dbCredentials + } + + vm_image posidonia_image { + generates gestaut_vm, elasticsearch_vm, edi_vm + image "ami-02a6bfdcf8224bd77" + } + + autoscale_group gestaut_asg { + // TODO: Shouldn't vm live outside the group leaving a reference here? vm might also be referenced by other components. + // It also applies to the following vms in other groups. + vm gestaut_vm { + os "Ubuntu" + size "t2.small" + credentials GestautKeyName + } + min 1 + max 1 // Using AutoScaleGroup as a way to automatically reboot a machine in case of error + } + + autoscale_group elasticsearch_asg { + // TODO + vm elasticsearch_vm { + os "Ubuntu" + size "t2.small" + credentials ESKeyName + } + min 1 + max 1 // Using AutoScaleGroup as a way to automatically reboot a machine in case of error + } + + autoscale_group edi_asg { + // TODO + vm edi_vm { + os "Ubuntu" + size "t2.small" + credentials EdiKeyName + } + min 1 + max 1 // Using AutoScaleGroup as a way to automatically reboot a machine in case of error + } + + security_group sg { + // TODO: the following vms should have the associated interfaces (ifaces) in the security group? + //nodes gestaut_vm, elasticsearch_vm, edi_vm + egress salida { + protocol "-1" + from_port 0 + to_port 0 + cidr ["0.0.0.0/0"] + } + ingress lb { + protocol "tcp" + from_port 80 + to_port 80 + cidr ["10.100.1.0/24", "10.100.2.0/24", "10.100.3.0/24"] + } + ingress es { + protocol "tcp" + from_port 9200 + to_port 9200 + cidr ["10.100.1.0/24", "10.100.2.0/24", "10.100.3.0/24"] + } + ingress monitor { + protocol "tcp" + from_port 6556 + to_port 6556 + cidr ["54.217.119.81/32"] + } + + // TODO: ftp (20/21) or ssh (22)? + ingress ftp { + protocol "tcp" + from_port 22 + to_port 22 + cidr ["213.96.27.139/32", "37.187.173.88/32", "51.89.40.59/32", "195.53.242.200/32"] + } + } + + security_group dbsg { + // TODO: the associated vms and ifaces? + egress salida { + protocol "-1" + from_port 0 + to_port 0 + cidr ["0.0.0.0/0"] + } + ingress ora { + protocol "tcp" + from_port 1521 + to_port 1521 + cidr ["10.100.1.0/24", "10.100.2.0/24", "10.100.3.0/24", "84.124.78.66/32"] + } + } + + security_group elbsg { + // TODO: the associated vms and ifaces? + egress salida { + protocol "-1" + from_port 0 + to_port 0 + cidr ["0.0.0.0/0"] + } + ingress http { + protocol "tcp" + from_port 80 + to_port 80 + cidr ["0.0.0.0/0", "::/0"] + } + ingress https { + protocol "tcp" + from_port 443 + to_port 443 + cidr ["0.0.0.0/0", "::/0"] + } + ingress es { + protocol "tcp" + from_port 9200 + to_port 9200 + cidr ["10.100.1.0/24", "10.100.2.0/24", "10.100.3.0/24"] + } + } + + security_group checkmk { + // TODO: the associated vms and ifaces? + egress salida { + protocol "-1" + from_port 0 + to_port 0 + cidr ["0.0.0.0/0"] + } + ingress http { + protocol "tcp" + from_port 80 + to_port 80 + cidr ["84.124.78.66/32"] + } + ingress https { + protocol "tcp" + from_port 443 + to_port 443 + cidr ["84.124.78.66/32"] + } + + // TODO: ftp (20/21) or ssh (22)? + ingress ftp { + protocol "tcp" + from_port 22 + to_port 22 + cidr ["84.124.78.66/32"] + } + } + +} +deployment dep { +// Gestaut -> gestaut_vm, +// ElasticSearch -> elasticsearch_vm, +// Edi -> edi_vm, +// Database -> OracleDB + +} +active deployment dep + +// Concretization to AWS +concretizations { + concrete_infrastructure dev { + provider aws { + autoscale_group asg1 { + maps elasticsearch_asg + } + autoscale_group asg2 { + maps edi_asg + } + autoscale_group asg3 { + maps gestaut_asg + } + vm concrete_OracleDB { + maps OracleDB + } + } + } + +// concrete_infrastructure pro { +// provider aws { +// +// } +// } + + active dev +} \ No newline at end of file diff --git a/doc/posidonia/posidoniav2.2.2.domlx b/doc/posidonia/posidoniav2.2.2.domlx new file mode 100644 index 0000000..1602c52 --- /dev/null +++ b/doc/posidonia/posidoniav2.2.2.domlx @@ -0,0 +1,107 @@ +<?xml version="1.0" encoding="ASCII"?> +<commons:DOMLModel xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:commons="http://www.piacere-project.eu/doml/commons" xmlns:infra="http://www.piacere-project.eu/doml/infrastructure" name="posidonia" activeConfiguration="//@configurations.0" activeInfrastructure="//@concretizations.0"> + <annotations xsi:type="commons:SProperty" key="entorno" value="pre"/> + <annotations xsi:type="commons:SProperty" key="proyecto" value="baleares"/> + <infrastructure name="abstractInfra"> + <nodes xsi:type="infra:VirtualMachine" name="OracleDB" os="Ubuntu" storage="20" credentials="//@infrastructure/@credentials.3" sizeDescription="t2.small"> + <ifaces name="db1" belongsTo="//@infrastructure/@networks.0/@subnets.0"/> + <ifaces name="db2" belongsTo="//@infrastructure/@networks.0/@subnets.1"/> + <ifaces name="db3" belongsTo="//@infrastructure/@networks.0/@subnets.2"/> + </nodes> + <generators xsi:type="infra:VMImage" name="posidonia_image" uri="ami-02a6bfdcf8224bd77" kind="IMAGE" generatedVMs="//@infrastructure/@groups.0/@machineDefinition //@infrastructure/@groups.1/@machineDefinition //@infrastructure/@groups.2/@machineDefinition"/> + <credentials xsi:type="commons:KeyPair" name="GestautKeyName" algorithm="RSA" bits="4096"/> + <credentials xsi:type="commons:KeyPair" name="ESKeyName" algorithm="RSA" bits="4096"/> + <credentials xsi:type="commons:KeyPair" name="EdiKeyName" algorithm="RSA" bits="4096"/> + <credentials xsi:type="commons:UserPass" name="dbCredentials" username="***" password="***"/> + <groups xsi:type="infra:AutoScalingGroup" name="gestaut_asg"> + <machineDefinition name="gestaut_vm" os="Ubuntu" credentials="//@infrastructure/@credentials.0" sizeDescription="t2.small" generatedFrom="//@infrastructure/@generators.0"/> + </groups> + <groups xsi:type="infra:AutoScalingGroup" name="elasticsearch_asg"> + <machineDefinition name="elasticsearch_vm" os="Ubuntu" credentials="//@infrastructure/@credentials.1" sizeDescription="t2.small" generatedFrom="//@infrastructure/@generators.0"/> + </groups> + <groups xsi:type="infra:AutoScalingGroup" name="edi_asg"> + <machineDefinition name="edi_vm" os="Ubuntu" credentials="//@infrastructure/@credentials.2" sizeDescription="t2.small" generatedFrom="//@infrastructure/@generators.0"/> + </groups> + <securityGroups name="sg"> + <rules name="salida" protocol="-1" fromPort="0" toPort="0"> + <cidr>0.0.0.0/0</cidr> + </rules> + <rules name="lb" kind="INGRESS" protocol="tcp" fromPort="80" toPort="80"> + <cidr>10.100.1.0/24</cidr> + <cidr>10.100.2.0/24</cidr> + <cidr>10.100.3.0/24</cidr> + </rules> + <rules name="es" kind="INGRESS" protocol="tcp" fromPort="9200" toPort="9200"> + <cidr>10.100.1.0/24</cidr> + <cidr>10.100.2.0/24</cidr> + <cidr>10.100.3.0/24</cidr> + </rules> + <rules name="monitor" kind="INGRESS" protocol="tcp" fromPort="6556" toPort="6556"> + <cidr>54.217.119.81/32</cidr> + </rules> + <rules name="ftp" kind="INGRESS" protocol="tcp" fromPort="22" toPort="22"> + <cidr>213.96.27.139/32</cidr> + <cidr>37.187.173.88/32</cidr> + <cidr>51.89.40.59/32</cidr> + <cidr>195.53.242.200/32</cidr> + </rules> + </securityGroups> + <securityGroups name="dbsg"> + <rules name="salida" protocol="-1" fromPort="0" toPort="0"> + <cidr>0.0.0.0/0</cidr> + </rules> + <rules name="ora" kind="INGRESS" protocol="tcp" fromPort="1521" toPort="1521"> + <cidr>10.100.1.0/24</cidr> + <cidr>10.100.2.0/24</cidr> + <cidr>10.100.3.0/24</cidr> + <cidr>84.124.78.66/32</cidr> + </rules> + </securityGroups> + <securityGroups name="elbsg"> + <rules name="salida" protocol="-1" fromPort="0" toPort="0"> + <cidr>0.0.0.0/0</cidr> + </rules> + <rules name="http" kind="INGRESS" protocol="tcp" fromPort="80" toPort="80"> + <cidr>0.0.0.0/0</cidr> + <cidr>::/0</cidr> + </rules> + <rules name="https" kind="INGRESS" protocol="tcp" fromPort="443" toPort="443"> + <cidr>0.0.0.0/0</cidr> + <cidr>::/0</cidr> + </rules> + <rules name="es" kind="INGRESS" protocol="tcp" fromPort="9200" toPort="9200"> + <cidr>10.100.1.0/24</cidr> + <cidr>10.100.2.0/24</cidr> + <cidr>10.100.3.0/24</cidr> + </rules> + </securityGroups> + <securityGroups name="checkmk"> + <rules name="salida" protocol="-1" fromPort="0" toPort="0"> + <cidr>0.0.0.0/0</cidr> + </rules> + <rules name="http" kind="INGRESS" protocol="tcp" fromPort="80" toPort="80"> + <cidr>84.124.78.66/32</cidr> + </rules> + <rules name="https" kind="INGRESS" protocol="tcp" fromPort="443" toPort="443"> + <cidr>84.124.78.66/32</cidr> + </rules> + <rules name="ftp" kind="INGRESS" protocol="tcp" fromPort="22" toPort="22"> + <cidr>84.124.78.66/32</cidr> + </rules> + </securityGroups> + <networks name="vpc" protocol="tcp/ip" addressRange="/16"> + <subnets name="subnet1" protocol="tcp/ip" addressRange="/24" connectedIfaces="//@infrastructure/@nodes.0/@ifaces.0"/> + <subnets name="subnet2" protocol="tcp/ip" addressRange="/24" connectedIfaces="//@infrastructure/@nodes.0/@ifaces.1"/> + <subnets name="subnet3" protocol="tcp/ip" addressRange="/24" connectedIfaces="//@infrastructure/@nodes.0/@ifaces.2"/> + </networks> + </infrastructure> + <concretizations name="dev"> + <providers name="aws"> + <vms name="concrete_OracleDB" maps="//@infrastructure/@nodes.0"/> + <group name="asg1" maps="//@infrastructure/@groups.1"/> + <group name="asg2" maps="//@infrastructure/@groups.2"/> + <group name="asg3" maps="//@infrastructure/@groups.0"/> + </providers> + </concretizations> + <configurations name="dep"/> +</commons:DOMLModel> diff --git a/doc/scenario1/scenario1.doml b/doc/scenario1/scenario1.doml new file mode 100644 index 0000000..185563a --- /dev/null +++ b/doc/scenario1/scenario1.doml @@ -0,0 +1,88 @@ +doml scenario1 + +infrastructure infra { + + vm vm1 { + os "CentOS-7-2111" + iface i1 { + belongs_to subnet1 + } + credentials VM1SshKey + } + + vm vm2 { + os "CentOS-7-2111" + iface i1 { + belongs_to subnet2 + } + credentials VM2SshKey + } + + key_pair VM1SshKey { + user "vm1user" + keyfile "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD3F6tyPEFEzV0LX3X8BsXdMsQz1x2cEikKDEY0aIj41qgxMCP/iteneqXSIFZBp5vizPvaoIR3Um9xK7PGoW8giupGn+EPuxIA4cDM4vzOqOkiMPhz5XK0whEjkVzTo4+S0puvDZuwIsdiW9mxhJc7tgBNL0cYlWSYVkz4G/fslNfRPW5mYAM49f4fhtxPb5ok4Q2Lg9dPKVHO/Bgeu5woMc7RY0p1ej6D4CKFE6lymSDJpW0YHX/wqE9+cfEauh7xZcG0q9t2ta6F6fmX0agvpFyZo8aFbXeUBr7osSCJNgvavWbM/06niWrOvYX2xwWdhXmXSrbX8ZbabVohBK41 email@example.com" + } + key_pair VM2SshKey { + user "vm2user" + keyfile "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD3F6tyPEFEzV0LX3X8BsXdMsQz1x2cEikKDEY0aIj41qgxMCP/iteneqXSIFZBp5vizPvaoIR3Um9xK7PGoW8giupGn+EPuxIA4cDM4vzOqOkiMPhz5XK0whEjkVzTo4+S0puvDZuwIsdiW9mxhJc7tgBNL0cYlWSYVkz4G/fslNfRPW5mYAM49f4fhtxPb5ok4Q2Lg9dPKVHO/Bgeu5woMc7RY0p1ej6D4CKFE6lymSDJpW0YHX/wqE9+cfEauh7xZcG0q9t2ta6F6fmX0agvpFyZo8aFbXeUBr7osSCJNgvavWbM/06niWrOvYX2xwWdhXmXSrbX8ZbabVohBK41 email@example.com" + } + + net net1 { + address "10.100.0.0/16" + protocol "tcp/ip" + subnet subnet1 { + address "10.100.1.0/24" + protocol "tcp/ip" + } + subnet subnet2 { + address "10.100.2.0/24" + protocol "tcp/ip" + } + } + + security_group sg { + egress out { + protocol "-1" + from_port 0 + to_port 0 + cidr ["0.0.0.0/0"] + } + + ingress ssh { + protocol "tcp" + from_port 22 + to_port 22 + cidr ["10.100.1.0/24"] + } + ifaces i1 + + } +} + +concretizations { + concrete_infrastructure con_infra { + provider openstack { + properties {} + + vm concrete_vm { + properties { + vm_flavor = "small-centos"; + } + maps vm1 + } + + vm concrete_vm2 { + properties { + vm_flavor = "small-centos"; + } + maps vm2 + } + + net concrete_net { + properties {} + maps net1 + } + } + } + active con_infra +} diff --git a/doc/scenario1/scenario1.domlx b/doc/scenario1/scenario1.domlx new file mode 100644 index 0000000..bd897ca --- /dev/null +++ b/doc/scenario1/scenario1.domlx @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="ASCII"?> +<commons:DOMLModel xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:commons="http://www.piacere-project.eu/doml/commons" xmlns:infra="http://www.piacere-project.eu/doml/infrastructure" name="scenario1" activeInfrastructure="//@concretizations.0"> + <infrastructure name="infra"> + <nodes xsi:type="infra:VirtualMachine" name="vm1" os="CentOS-7-2111" memory_mb="8192.0" cpu_count="2" credentials="//@infrastructure/@credentials.0"> + <ifaces name="i1" belongsTo="//@infrastructure/@networks.0/@subnets.0" associated="//@infrastructure/@securityGroups.0"/> + </nodes> + <nodes xsi:type="infra:VirtualMachine" name="vm2" os="CentOS-7-2111" cpu_count="2" credentials="//@infrastructure/@credentials.1"> + <ifaces name="i1" belongsTo="//@infrastructure/@networks.0/@subnets.1"/> + </nodes> + <credentials xsi:type="infra:KeyPair" name="VM1SshKey" user="vm1user" keyfile="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD3F6tyPEFEzV0LX3X8BsXdMsQz1x2cEikKDEY0aIj41qgxMCP/iteneqXSIFZBp5vizPvaoIR3Um9xK7PGoW8giupGn+EPuxIA4cDM4vzOqOkiMPhz5XK0whEjkVzTo4+S0puvDZuwIsdiW9mxhJc7tgBNL0cYlWSYVkz4G/fslNfRPW5mYAM49f4fhtxPb5ok4Q2Lg9dPKVHO/Bgeu5woMc7RY0p1ej6D4CKFE6lymSDJpW0YHX/wqE9+cfEauh7xZcG0q9t2ta6F6fmX0agvpFyZo8aFbXeUBr7osSCJNgvavWbM/06niWrOvYX2xwWdhXmXSrbX8ZbabVohBK41 email@example.com"/> + <credentials xsi:type="infra:KeyPair" name="VM2SshKey" user="vm2user" keyfile="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD3F6tyPEFEzV0LX3X8BsXdMsQz1x2cEikKDEY0aIj41qgxMCP/iteneqXSIFZBp5vizPvaoIR3Um9xK7PGoW8giupGn+EPuxIA4cDM4vzOqOkiMPhz5XK0whEjkVzTo4+S0puvDZuwIsdiW9mxhJc7tgBNL0cYlWSYVkz4G/fslNfRPW5mYAM49f4fhtxPb5ok4Q2Lg9dPKVHO/Bgeu5woMc7RY0p1ej6D4CKFE6lymSDJpW0YHX/wqE9+cfEauh7xZcG0q9t2ta6F6fmX0agvpFyZo8aFbXeUBr7osSCJNgvavWbM/06niWrOvYX2xwWdhXmXSrbX8ZbabVohBK41 email@example.com"/> + <securityGroups name="sg" ifaces="//@infrastructure/@nodes.0/@ifaces.0"> + <rules name="out" protocol="-1" fromPort="0" toPort="0"> + <cidr>0.0.0.0/0</cidr> + </rules> + <rules name="ssh" kind="INGRESS" protocol="tcp" fromPort="22" toPort="22"> + <cidr>10.100.1.0/24</cidr> + </rules> + </securityGroups> + <networks name="net1" protocol="tcp/ip" addressRange="10.100.0.0/16"> + <subnets name="subnet1" protocol="tcp/ip" addressRange="10.100.1.0/24" connectedIfaces="//@infrastructure/@nodes.0/@ifaces.0"/> + <subnets name="subnet2" protocol="tcp/ip" addressRange="10.100.2.0/24" connectedIfaces="//@infrastructure/@nodes.1/@ifaces.0"/> + </networks> + </infrastructure> + <concretizations name="con_infra"> + <providers name="openstack"> + <vms name="concrete_vm" maps="//@infrastructure/@nodes.0"> + <annotations xsi:type="commons:SProperty" key="vm_flavor" value="small-centos"/> + </vms> + <vms name="concrete_vm2" maps="//@infrastructure/@nodes.1"> + <annotations xsi:type="commons:SProperty" key="vm_flavor" value="small-centos"/> + </vms> + <networks name="concrete_net" maps="//@infrastructure/@networks.0"/> + </providers> + </concretizations> +</commons:DOMLModel> diff --git a/doc/scenario1/scenario1.png b/doc/scenario1/scenario1.png new file mode 100644 index 0000000000000000000000000000000000000000..7dabc63c9104bc1e210830947aed79188dd9101f GIT binary patch literal 77408 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 W_s8OdUB*oyvpik>T-G@yGywqWp4E>4 literal 0 HcmV?d00001 diff --git a/doc/scenario1/scenario1_openstack.png b/doc/scenario1/scenario1_openstack.png new file mode 100644 index 0000000000000000000000000000000000000000..cf015c5d7edb8256708cf16f17197fe9e15ca272 GIT binary patch literal 76292 zcmeAS@N?(olHy`uVBq!ia0y~yU}IolU{2s*VqjoMbuc-|z`(#*9OUlAu<o49O9lo8 zmUKs7M+SzC{oH>NSs54@I14-?iy0XB4ude`@%$Aj3=IF5db&7<RK&fR%kCf?x!5hA z>*~7Oci!zfs{23e`c`e0kS@_B<t!p0UA&^M8x+2EElLu9Bf2r+{ob{yfg*uRt_yGo zc>J5Gk|4pfXi>|Dx#jct)h3@**^p9nZ?3fq6GH`ihs&bs@9&OI+njZE)srVFoe?^E zf<f!J;$N;m(6>VOQIpKfaQ!(G^`{D1Suc6CY>l}8Q}5HA`|Ec<x)!Q^=!y5zgxN+L z&vI?>oq1!KQg7{SSJqAYPhR}}J*a+Kcx!ub{c26Wc{ZB=gY_nU_*wJn{mwtq3=9km z4Q)J30u1@wUq3$h`1ttqb91|Ud#9?)CDwVyseNTk{x|2sho{~tYIpaC&6=q2`P9n8 z;(f9;C6D)<nX+THsQFWyz2O_@7(M*9v}<-(&|}g3MItIkL+^Oy#(z}H30B({sklv3 z_;!fM?J1sreeHJTv!_1!WqM`)>&F*&zkGVrbLmdabbpzp|7NhW`bLY&?Mpu|_sW0o zAAft0%N(4U895rv)eQ{`pY=#y`X9fk-r+#XtK{ZK>RcR(ExdQ9?3mr3H^*LX@9zVB z9<Taqj;s<*{ZmpE5*Wy6umAnY$;nrw8~^Asg3a3M$iTE<TS|I*{O&ScGqY)OkAo)! z&zh~|(Y4r4)Zm}}NdYGg>E<i){}*I7Ui#1OaNyR~Y*Bw1W{~CpM}Y>03f7Y+Pg<9~ z;h1Y1ZFWh$=BLoHbOsK^mfmYieqTQ)b929;Qq9LhKU14>3JVvy=q$TuZBQ%#(jCIf zqQH>l6r<$RwSRxj$wP~Om)YzpoL6{<U$I3X%=F*=*Nlve)@5%h{`}bZ=Pe6J%YuC% z*9ti;{BUWhci_Z{Up(U?&r2TvBH+aFP+L^xXskt9!N)^C|NZ;7aN*{{G6|5L5JnaS zh9`G+m1=8gO|pvMpCz`V_Og}FYi7lk54ygcyDJmipM2AmKGM2#(x#xFzj;A=9BLaF zSQvQi)wLB8J{{}$Af2Av9rRez{R7CN+tJgeSAD(I`r<`~-F*G}-{5eph*w}}Jl5^e zwZp|jc4F}Hr!VXToH*`p4fm6sXIWe2;J{!6_Vhht4u=JXN+Mjz$;s*G<`h1=p?GYe zWs88*4?kV6Ui;#d`iv828X6j^zrQ<qe<sK+4EGE<92OKFeEykV-cCluL^mVq!Npo@ zgNytv0*8KmI<@k!<@rZP4%OV8`WWnzdzv8Sg4Gsj&%V4ozPMcZuLFl-i0I~dQ$K88 zw0Lo=)51S*LHVCy!8%6<rrx$olQ!+Svgk_1nXk->Edf`3cGc%T`|@DHf(72w^-kX3 z26n<L5f+67&p!P8^Ww#eJzJJsI<vfIaW02qOF*fJ%2C-{o6p|AZ?B`HV`v!o&x{SE zBfwpOfwA=2ot?#wj+fK?`2BX(_ZH6KRcsMhwLr7b{P1-BPTP`ze|`wXUsVhZ3r|iu z`tRSr_HQ|dE#pDyZl2}INtcp-?v?_Xc_6x(f#u5N)#B&c^y4Q9l~1m;0U2l}V?0@? z?9GkFlM6xq|L{$LfpM#IOxU#Prz{%UK&C9(baG+(xjCJYjUZz_OjBTB{QB_Y<Kr(i zJ(otFmvBEX;KY%5ICO%DxZu7!_wWCIa&qz~a~qJ_hU@H%94k0CZrr#@vR>l&Spg@G zg;EtZRpkx`o>Z5=KXv~bNEJhzFpI*42M<60Tz%;9%EOPikMkMvO;>CYaN0U?-VHr# z&!8x!`v2e@Qz0wR;BeSYL*(2X%jAD^Ztl2X-6C+vE%90KMuYk1pP#<Z0rCSw7CVzb z#*Gf{;`}+iM_cbHwg{MM9iFClb7OM*pS|F8v_MROp^?pJrb2h20}n`^O+Lm}Siqq2 z?X9g!6FvSd;{drVK$*i~fuoSLbo7LoM=Yekq0!H|U7yvLn}?fQn5*?uxjRT*!z)Jy zCSIP29wMTmq5=j#FZO=!aZcn=Y}pVvv*dqN#)&mI7P~)rKM|ys;c7Dji<FSl!h)-- zJ}k&=TwyQZ#G#ma?sE0g$n%An1|T(igjf_VBnWV|dQa2g<DJ>cUFXQ5*fRBu&y*=* zr|;{7{WztOfklchb&+#*d5X=x!#1y4L2<R$HM4*5wj4h{ziIn3L6s6i2pc1ZiNGnB zrIF_)*>{8Nni4rhN?vfEjfRGXwRQBrYn&h}7cejhWH5Aib$M?tJgX0Kx>|S8<HOVS zIeF)veg66B`vo9X3{Mpp7(>&K_sLF_zGl={cz@-y#bN)=Qb3XQV_HzYM$h&c<!co* z^FAK>={-&7*w%+2w;o_%<XB;H@#4kz|GVB_QLN57Q^Wx(J(k`JkjOWDe{jz7?`8RS zb+iu7xd5_c1q+ivKx|TS^54II<zs9QTWqPluEgNTp}0o)wJO8Ov%V8Mj(#!^0V!o* zQCP6)&hGN__wWBdwes*8<y+<YOad(ehpaZ*F$cIgN!t7Vn+MLV4Gb(H(+<tqBAuQr zd3@7*cNPT>#g@{fzibMpoPU3xV^bOQ?;J>B!!ceafdJPds!qog-~1L~<Z$9hoE!0; z@x_^mfB*ga*Dr5BZU25yz%CGMW?%{F-*oBHn)Dv$8{aiS_L}MF@7%83a)aG)`hJrY zzqP|E%&%25EV?5hXjlBqCq_aJ<Y*2DhpiobeRdnNr5``|YTM1gqS#`pDw^r@@`YXR zKdVD~l)v)2vVZpOdaWUmsS>?7w&2L=)=h82KTOMHV_d<;>RGr@adX((DAoGKpeBVw zGAkp;3W2W{A2%=Ja1e0X`tjAS2Nx!WHGln=aWN!1Dk@{e`rZHU)vZ~3ap#AXJzwLs zw5{Fdoq76cL2z90l0?_GPkVv_J4BcS?q-WLO<W^zvip*BCqu|)&h7fTky{v=8$gx0 z8y}NEfas>po12@K<=)@YS9s&sKUan(fmMy+*IA>#zWj1^xvq^)eP7gp(>q_ObQVwd ztgS7YocZ8HpMA~UWsm*i<zft`6~?c;++;1_w9DOSdiAZ~ZtYLKH6{}*_infqb;WiD zpUG4=hR|)F%;Mu&7TnU%J2QFzV{kwRHZib-FirFj5!+VtZ_Z8El^=VUuYCu_^M-}i z>06HS$8V1mzqDKFSIG0Dxy}E6#^1g;jjho7nW5RDJ*!T1`~S?C`zc?Wm%D1^mNycW zUgn|B3_(J(Px;F%uK4<?D|TwF;?kmBNh}I4w{xBJ+%^Ac$eLweJVQVGOkEwsxWeS~ zN|z9o`upIJe$33sv4W+>&fX>`((8`zvx`}^9SkgrIXl$e#Xd>7b@lEe@rSbEZm(A! zjNCW3{_nfVt6w*1=lb4^OgQr=@M5z>qDPkN_sUR)q&|j8c3zbS7nUvlv+UKh@RIqT zWLE@D5n-P2XN^hg+kpF1?ay{i3n^TG%Kz0@Htm&R42#ZG)xCcg`!iY}l!BxrIUF2R z1;n2j@dv&3XHnotJY2fr-uuKxTglqxW@>Xby!-e(e5&*Woy8h$v$cLcPE}fI)#bj+ zW!EQdCV|bHPaNmn8}m@~^wX=$f>s9V*iGFxHP0o{{^g1g%@=#_ynW>q^i(Be@+z^0 zphtOZEIxMS@8<luD-N=_my<<d0n?ODFXQ<}d_k`RK~^(gP4-yN`tPnp>hf=P7AYUU zpPwIYCEjz<S|W7mC$6c-&V|J7JjM{>YZ<huuJrV38<V9aG5%9+)33=d4fHIWBba=8 zYMa7Ur>WA6QzjN<oZr^az~I9HYDSni2{bs^PM$kA_LzUN%)D#sIUEEIwY``#ahlw; z{RXqny{;;}y`ay&cIAf1OnFUtg~clMAzCh1*Tpz9Oj;MV^KQVY^OqK_s9B#i?Ya@e z$rZb18J(KHEQhsHF({}zD~y#nATX;Y<HVX3D>Q!I1{b?hyc`Y->>hsp>F9V_PsaL$ zh0WS{!3KpE-g8IxcJIr%ZS-_ctm`RHi^RQpOTHiSjLmyzx;i34n*G)Gc=MxslTKf9 zP+qNj>XZ9Yh9w({p8gApWth}Dd-}{}n>IyfYT0L8eY%y$;hSUVF@exm)?T3!3smlN zB)85AImqDh^T8|0`uhLJ`{hsH?*SE=7c3kYn7(?3goI?A@Cn`VX*wutA7Wd6{J}kL zwX(Gx7TkY!*Z#lC9#(6$lmFe{{rR`8cz$jCR_t{DPPW_Kr%#V*3-w?66KKg0TC;jW zZ^l$Mh05cVR~w{`Et{gZzLY^r-%kIy;ohnM|D(s2_D57QTv;7*mcga+*$KwFyuLqY z!7aaDW)_8j{3Az?{`~#>fAvR|$1Aqn1Lb)$3;VpgTatd9za_BuU{$@!yV#Jz*Gcxf z?e6Yd=V7yc%O?eepPjJ>y*{%mGn_nr+9Pm<mKW26FMU(f<HOcnsyGtyPl|EHpVy!6 z21`5$Uc1TT%HPuS{}+YaWN?W*=0ADAJgBB$P}so0@``<;he`dvJ-vbtdmaad3NUgw z-Pmx)_V)7h<N7IY-71c6bv4dC`b+upj@$ax)4s3turc*s>$x_*Fg`MF;mTR|GZ<ey zJ=JB|CRvzrxM0<4`!8#!y1zJaZB5?K*K7+ub+$j*9(Gf*Dtqeul{vLtUi^XFp_UAR zUESPI-=6{{_6!yUhQ?ox8X|f3HyJ+uD0}SS3N3JUkJ!b(?D?;WTW3$6?X&y;y?Gm% zQa9}0*?)MwXy@{@;B&WY@9cVcdiQCe&P|CHq09oxXHTTCEa;kdXhD*-sAy-~#5AoH zoA!KTTycu^=#t+tj;3>8g9G3rL&%hg6`))$Z49#iiY$vlfPF_dOV8netAb$9Kl%Ij z@Iq6!O=Zrtj<u&F8jVVpOl^O9UbOT1)Ve#%PR`X;?CCX$OXYlH3JTGW3}4<Ko&4|L zzwhtvPTp@03Rj1<jtop+oio32A4^>E3gVl(|4-Zb`65po@;w*2y#Kbz+A6LYi#@lC zUfvb{Wp4N@ix=*X*ZsE^x0||g?y;4V1l_g<A7vJJdaNT<<kR;wkV99PayUGwRTkko z>dM-~957V~?AAY@CQsv3U!#5NY{>fkwrt<SVib#Zg`e+>zw*y1!RUJHmwyjmo9)?O zf5Y~+dtr9(`CC3BYh$kRJ8*8-|M~m3y1M%5`%6GsVZlBHhQ{x{B_&@zsdh`SFVIqD z5@^|wc#^|mitwF(fBv23caN6n+iLpmU-j>Kvinc$e6?hU(_7mu$2xZX{=YAG_l@iG zKJMssy~*ORzW$&1(pT&Xr;c(z&%d?B^Pd{1m|^(J%E+-N{6|flgTsND%D*Mu=R>Wy z`S<%bb@Q1<ht4)Tizy~svG)C0{mM80!P&iOea&@$c9dRzDd+sC>`d|-hBfEd1RLaH zY=x^eqW;+-8M|ol;&i|HaZ{wuTl@;q1eYeKug|^zyw}$<|M}Ywx5IOzt9ah^YA(u7 zZsu^<5_w)FY2%-@;0AjZ7n49meTU1UnZ{`{cegiJ-dULhY6nSAn|OKeud?5>1henW zwG5vt=JV!P;cT_AS}WOTvxysx11_=&Hy8;WX|4QJo(+y4O%8_#e?R{C_;^8Pqox0Q zLq4leUr^C~*r@RRBC)eu;>-J<Z!eGacz?g{N5}W?{1Y$lmDtUaYPNtOWY*W1e|cd4 zn1X`I-K#tF&lCNbprW|^?N8s+&(20XvQfHnt+q^m_0B8v=WhSfEnj_R?uDs>8Tb0z zuStA;`|a!Z__`Yv>7DE1Ub!Ex=njkwew24^j$t#Xy}k(?!)2ftc9*p8E!?qkR?zX+ zJ<1iJ*4}*Ch;2#Vl$d8<_;;;wvJTtYO|N#`d(|&7`~3OaTdKUj^JjiM%Kp`WW5pr% zcAb>XF8*b54Uvnqa~v2uj7(ClJv`k0^!-gx00dYIG#vQ7Xw4Rr&zg@Tf=xlC*go#X zV){lKX1-0&-G6_Vtn&RUos4&9-IA_9{KDw{%_*PD{^ix5NbB6UOK-+UE)yr|Fb1nF z>9^0!G=B2_7Q##gYwO=fE*|E6^he(d6ryvjOCQOY-=0`%bYnRi`=1*rZ_mu@(fIst z>RF+`SC{J=c$XaJa**tQ|Ks=X<42F4yuSzR|2UBU9dmNlyo&$3*9m0K-B*T2CCf_d z-PTl!%Nge1Tp2rg#)rBdrQrBeX`N>8z7;>Z=h}09$;2BQm;_RPd|Db982HINALOnT z;-JiEQ2%ew-9Twc_nBJSpzOjo*Q)T`@0IpO{!!cB{PUeW^IpASl+W3DcT%iwy?fpw ze68oH=JU4_-uDhP{(5`ew=jK=PVW&m@dhKg`<j{uY(VYuhN>n8mV1_+ot^*w{o~xO zZ`fxP8U}J}tmM{;iL>8&xGk>axB7SQ%kJ~DWTKLfWEh6}S|r}xe>}auex>y<|GVd- zGWkq@yn3|F@sRbp*G^T^CBIq(oN5p5IQ@E8L6G?OgL@^{`zUWb$f|J4sBYJvzu;k{ zSFB6|70=(?+?@Vz&dr|3-l1ZQ98MAHcGert97y}Nd*1iuR?5D!%d?mNKH6^e?%Dlt zT}OVtNS!%Xme%*5zg53P_R{9eqkEfnnf`oREUU{~_Mgx9*=y6-tIH#g^Q{$mFR``3 z{qE~0YQaZaO{<I7-~7KX_jE;}R41<?!_+jpsU4@E<Ua(3YJjpp!vX8(k9KYr?~_fD zi(j<?lmn~^kG^rOyY@{qcWK-m%MC|&>@VHD^|I>JpU<3iSI;kXt}?UDyg8{*ylwv0 zOFkBfwZ-da?*94xZ0XM0{nusx?e_bA-!36!-mKh<KD!I!r1v?pE$C@kGv&F{!hnBz zd?0H}85ueD6rcL>vPaolR2=N{vaK`9&+O7&yxhbhw*GSFMVG~g)o(ricg;3QZ@ud# zts^UJOmAc?XUtl_nDOk(%XWVGY5TjuC4Z~~1JiqnjXOYj;6%_-a30v}t~@RB*01oC zDQ&MkPfwe;?5rerZ2je#uYRg8{&ZzRi_}{a2VN6~w|%!Hq^zR<*+Crj@Gt9vXyIxj zK6%9{mBlgX#}=ml?P6e2tf^*Z7W3I|ko0F~u(Y!8?E3GK!jX4Y&M=JXK0VKVp||Uo zz%3X3n6Ef6U)V41|Lgbf^z`)8{owFjAhu^egNv;Gsr^j7ZC}28kqwMy@ht37_HMiK zo-NrEYW!UHmUWw6MFjd>Ie*{odZgjn-Fsh}%Wu0d<*bBk=B+=s&(AWt<lov`EVY15 zs$qqBUVZ)l?Ca}J-aiM<WuPv9`H7vy&zqL98u4kb_WGM=FzpJsRk{885xbJ<U$=|y zbotBk^5&QA=S$Vf^MVWw1yr{_d9=;3=Sy+X<jnJZ@g@KF+A;+&7kxbRa|Rn|j3B@m zlrT~*FYA?7J|ua3O6bx$a|!QP;VcRq4{xuQGj`a&?(x5CrAGgED*e54C)+Gwf9?1C zx8Kiud$^)js*~58A<)aK`)U4jP;3WqgUnLV$hZZnhS*Wf^8Ty3@`O_Faph;a(I)vy zdmd|f+yDQ)E%))BAK|UMRt%lopPwKZvs=m1^5*^f_AmdL9%D=mU3s7P*rZ>gj2uos zzL}k4aQXRj`~JVz9y%|PpUG>*F!kf*C+}atov>TU(D37db-6u@J(mXUm+n#h3MxZB zyv^Oh_~Lk7@Ps<8oC}OAG~Rj^6>a+CD+|)cE6<|v;hey9Lq65jlYiY!&{+v?Ip^mu z=5_cXyegxa&%yosTMpj2y7m6-3=9kohuIl9_Glk*W$kf3`DzIy%xbC{1@`UbaG0>k zk$r)?`MKYa6mtJMDEclOaAaV5-(}=z_BddZ71TfT;~q#`>h56P$e*MWQYih%&RjyU zK_Ru7&%t%&$AAC+ZO^+qY5#gq@?X%{%)oMw^W@2s?)>kPWxT@FzwSO55w!o&W$qf~ zt7;4vxjQGOKW*i`=b`N6@%@C>>HBjb4pBIDVM3(3|GX*tw?fQW9LM<L;-Ym|1ou_< z{D}^z4>;a!yxc!s#vNg#ck;AzEcrL=?&UI6b<3_k$R^xy@7>GlTfzCfCL9j$o&Ru# zZdu5_pyR^^S?jVB_iw@cSCwwl75U`e)O+%hv3JFf+Dy8Cg=0aV_;mf1Zv*vV?)6R< zWAresoxR-u{`TgdOBcN3aCq+yHF3`J?<%QFKW#4q#feFQK*K$uo6cpiM>4DpFC8_B z)qE1a>fM7SS@VuY3y5v2DZk=&tT3e%8pU>dSM*$3s8OV4w=0<YHmiF`&&vh->Yn~O zV&`%5SBap;^5&mQy~@|0;Pu)bS-JDjtWPt967Nl%(D&~0sql4KU#pis5fq(%fxrCZ z-nk|1sjn|fSN@F9H+H}J_C#2oxQxsDd0!l-zTfPWyRy!H>1*u;KSWk}<+evHoOXVC zP@%nca4p-U_IX{ZJ&&3hzU(_PKfqPGieZt+bdIO_Z$X~Pc%Z<rSM@5Z!uM0kbt$X& zzMs3=v{b$!?c9vQXD8~8IU;+b?UG)=zs&jna^hEJpWtSi>m)LL`ug<~JhayS-z0e9 zuaoEIfDHL5Uuw14P8e+Jc(l*+x@P>(t6$qU&Dy(aPwLSN!I{TJ%I#KEyDOiXZmk^K zyR-f2`bjzmLrMgm{$BGT;_6%Vy|O=lUEMQj>e?-HU)5z@eZTyXUa0=u3axJ~_llOz zXJWeWTd3YiG_;*T<;iKJ==IK;>-U6ng`Tqa-N56F9x6g>qa?q*I97@3{FXEQuS*11 zT#1x8JyWtNO7Hi!PrvldS5GhyS)*vO=ZxX0$?abn@4TF}XhZeY$g^>wzjY+6zirA3 zs8*F-s9YWNboSbBS6BaVTK1_ntYXcrH|bAHr<{&hUSpoMd;V5#hfD4&&AbieU(fAk zV9~s)z%Z3_)06k_AVIt$jA4@BZ0B?#&Dh<$ruQ%GEL<*baLVs$)<+`-+p3+4t!Gb6 zT<3P*|KrD9yR^^B_rwOtTvNWS$#(VHa&vA6{qn6f=DYM(Rj@qMaSolAksG%?+^$<H zj&WAjw+%Y>lM7iQo1vw-zrI%D*31R7ra06;NMDrl*K79D_u5ZWH51y{j-{nCdQ^HX zkoWL<`fkCUtZ%Dsr60UwxXbP0{h-?m-+SDR`lsZvJ^1bF;~d(jzW=<+=&@_=by>zG zA9g(59eDfinpB1=$(M20c5zMpf92jn4u=Uryi5TXwq#D%i=DLpB{&N!F7{&dn5VR> ztaSRNt=X<iD)us{%r9}@CL1|<#g=J%B2VN`UaK0t_EMC{rngh`yLRgC>Y1WH<$W=) zu<hyjv0EEGET@)Dz12TGTI2rqPwms{!+$bnecW~Rk4{z{YsRXZhd1pBIVPCGi^x|k zSC=)O(p<R6E@ZaiDsKs{OWaStuN9G6V!7<mzO7P>A}X<OOPf88#)ZlU&tPqdo4l)g zd9CZ@NoRRgqL<$a?-vZ4^1fJ?Ie?W*dd<00w^vro+Isi=Ew%-pbi?Z`W}Z1-^<0U; z(aV&>;q-K!uD-r$aBo^Mt|*$8@F9xPW7o6Ot)Zv8H(h;lT14LIq@3-lP0C(jL0xb9 z*2ncb9$jbjF7o4&Rqy6Jx)*vgRx0(p_s`8M&T0pom7A7kn=IpaO#xQ=bZ2S!@;sgY z^X2*fpBQDor2p=UQq&dS<p1^3Q!gpUQch>b);EhjIBa?<`hDuV{zrBiZ)TUwozCx) z|3q}{!d-h;y^9RFXBhQTtmnl$=X&qAS?%w>>2Hs`3yIB{|Dx*dXMLOeYtf~1dxBnm z4*C?l`e|KQ?XRm|Yd!tDLcV@=XQ+yQ<iBE3mOz8j_4Sdv%hvw63#ou4OL-j5<em^_ z-5hBUwt7>*%j8qx)kn9j3NSr;Ws=r!J?EeMLSy4jUtc>bJ#cln!%ewm%a<=dayJFV zbEh=cG8@I!{g|%W*){u#@4`oFtSR%qt@fAT+7sg_RhzLS)M=Jh$`<aj@HKK=zn3kU z>hXQavV_31aZ~RvarpV^l(n1Y?d@Lu5t;=WwO=I?U4Po`w_*sbO3e`2wld^q*_3}* zvNq56npd~=RNW@7)ffGj3w8DX<8x(bnvx~Z(D&~4MlG9|e{B4q5!be!ta(ggpF1uF zXGt1K<SvrAxi@~@iW<|GQD>%Vo$FfVux-jlDdV{zPA5;yiCSC}zPdKx8SmDbyHoN5 zwtWa)-;lHR6#vHL<9%;uUo-4e$esYHls~?7-MV&;N<!@lgBJI!r43839QzUM%+NID zm0*LDbNcD~55RTNgusWM3q+Tj)a_<C5)=3{>lE{Zq8Y0--?1=P{#}{aJ8MN^_-ecJ zU31s%SgOY~p-Agm=(hINhYkxiJg^pKT=8!y^Mpe`)YABal$ZjP!6jic!<3Id{2ooN z_$dvZ8Im$uCED=Brb;B(Zutfq<2|tqlkWb0+I1>?^17ojo`tWyR#uw+{9N%gY?bTJ zbz#33oRNN|z_2&#erk4TT}SA{hwa~T(30ithM=ZHCw-@`*g4gN<K_G&rcLP~hd-}9 zJU^<rGE}fZVW||0f>&W@06ciT*c3FU9&*xUZrvmixGyAe<EN_<j2@LXE1NFaPX2i9 zYrE6Wbz$4{ui9J=-<NmQraNjc)2VL;IypZt)>=!n_nUe^nvXxeFe%nl3pOaIGJ$87 zLfEfzIvBp|dR}Ary7`TDt5cxeM!%(5iu?*+?#|E-i|v&(Tk=|SUD(&YAi1L<yRPld zS*ta5iQX&z)=#mz^&v-}ulzDItd`F!e*3F=t&?~!g;cRld7Jp+%}r2;!%dG#U`fbi z29-0@_aB6pWgUIIUqmj53iE0&YwyZa+#4$vzU$I|cSC=VWR|TFWt&S}Z?<W%cSW%+ zn9d)yFZ|QpRVCp!k7>#Il}$b*yZS)Q9jTRi44QT=CFf6-_eN%CeccuMSw|!8FPFw` z=fchjg>ND@!^>@fL)*izGEexwB0c;u_tW}qs*|25pP%-5#_K&3e+aKyA;q#_$?xl7 zYhx7u+k#S(gR|RIKgQKItR+uBI*4?cmzOR-zQE(P%T)#!-4!!jI+~)ke`;H`QY&<u zTI;=_qsv~DIlUE27JW5OH2Z}7>P53P7M^mx;_{WTqiJXUCGn+PTPyA^38@t`iu=f= z!Rz44+H?5$ENF`x(t6aAZJ4I7?Qj3F^`!pu?`LD;QqNu&2i3*>3{T#_*Z(gHbyewe z1}0wdjRx}zf1vqpEAxekhgP3{^hbNzhk2@}|JBKNu8w%_dvtz)>s1AYRGWRx44}#M z73>@i3*I?0GzmCO<#P!AI=z>(tNz<2uDuI?2(QwBr3Ds+fa59Y>A~(@{dsfJk;@wH zHO2a}vvz&k5qv{%wy1l~{kd1ZDf?N>vllr~Q4*oQFO^}}&VD=N)dlj!ar3-RFTL9K zFUo3R|Cf9N+u8j!8U6oP`25@<=lSqzs=27yA{Ukg<;rkN7gQ}=X4!OVW!fY8sqBm# zPHi9ApZi;8nY>+f@kw!QcG)Y{#Rs(A8mDJ3FY$dT`#U73G0Ng)#{3;KEPHAj-rwKO z<M2so@)-{E#YQjfw>+KcCf2Y{ZsOM2XI8tP>OMa){deDm|K>B}A~d2;+zCmY?tT5Z zW<u#&DHa9CpqQL9j4#)NTKEi7W*iO+`WzXWCOq6aBjJB=+v9%89_v%99kW^!v#n>I z|8-O8%k*0ra-TLk-<bcUHd#A2<x%G0?sjI$-wdyB)PBCSi0P79-^qxlvjo%TTb{Sb zmHup6f15#N{`BRaCdfoC+iB(XZ*9#DzP(Fq-i4fbw%DR-MUh!h+MUp2vG-#BaE6BP zG6^i*q0s*CFQ{?jz|79bamA3sLBME_n6Sye8I4H{Qq?Wy&k9smswVeKN%j5v75;cy zJzq|&biHxr&9e*FzW)~|+_(RZ(N_&&KZzJClirQ|AI|#RHr3YEQmEZ)E?YG%D(#%{ zxj7e&vrox~`bL{iD!jj)&%x^))8y1y)33h@GV}5%TR(lGM`Geu@t>=9KeV5Mo-`B~ z98Y|x^7-&1g85XTd&QRaO5y&O{+273zqk@y73}|7`sdc)*#)!x#N@9X>vO*KrR49K z^!W9h$4_mqX)*uSaqxz6Te<)L(3*$0AANq?y?)Di+jpmK$VXh=lG|AFqElkx##5%n z4<DSVJQjH{R<hIm;+e?3FD15|X^XP2-D_?um2p1jnAv?hhN=7?R=zOW)M&#s(`}p6 z(`(ABEqgZFTs^Dd@>>6P&fOU1d7whvl>syz*YFB7GA}XFL!|!yHU-VT!XFWcR^5iY zy>h}fde<6)%s!;w3A}ggc<lFOrMIU3@o(K7{O{)EYd4clP2bO7vzOt?-u2giB!8Kf z+Gm;cp!&v#gFE|GzIQ&+n`oncXmh>sx8;W3(`T(<sIoY(b^XlGzb`Kc-<T*^E^>Cx z<>^uC%4g<9+2?+?c(rdOd)Dvo3;CD=e*TR4WOCTzfA=OnrU`rgY2-WStNd%xTE)i5 zQOp3!^I6PH0xPPIuv(%P8Kv6F489i2_CI!g?z+9o$9Ao`tJYW72a<aaZ2omDIj)*z z!X<{1|8JI0%62Jg_4udUnr>zM&Hb<Kh6#MOvyWd~n7&llHYD9I-tA_h$9m)Q5n^pA z6E#e$IEy#0&x~bR;B~RR@}KzA+iVIyQxCn`@Gk(`cqtGC%{V#aIxsMm@-qpvbX{V6 zv4ZjGjl+8k+be|oUgg)`v;47UUVP8N9`3#Av2R=@i&lo8czSQy<6D0%UzjpqxEJ?0 z`K9h@nc^D<z6&TblqSCEzcpi9x|wYD5@W-3z0C`i&#k%Fvnlvw=Kl+?b!2?j&fj-y z?!Nq~GiSfPu#3|{-~acdx`R6=U5{p6P##pT@lib_kVOHMQC9Fl%e8<&HwLNtka}s^ zhN||sm!%rJFZYShE#LQIkM_>b>#pdDF~<M3V_;m-W!>~LVsoB~Y?avTi-OB%&76@M zwEfa+9ka#lpVvrx@;|>Rc;?Qt-P`+^0)8HtzuIflxoDOJ<)IjM_JJ0Fgte&N-G9uY zX^rEn^5%_8>W(fKkJxU+d_hpomgz;z`=8gV^J?x~eLvygkHvd9H}2s)&hzWno_m@J zwlQ|nYuy+<?1VpOm0Nu|k(M;?8^7{9ji}i(XS}|+i_78D<^vh4VvhgyWuBm;b<um$ z<C#X=zWoBl28RR4+X0}JC9=({8z33-#$vW9$$X#x@Le?idV=Y~p4Yb5kC)5J<~ryu z(PyhavSIyu^Zhfo{3#B2{pwG0!t48NTPD{2-JN+_PH%qI`}ekfyHaP?we!}5*f6;2 zFZA|(a)jrm&za9JPL@lgwlAzvdp6bYOUJzHb2@j*t=-I!V(eumYV!Gy{wYxLvErA; zdd+_=T3z63hT-Z$Mz#g+{c<Pw2XV?B-BGS;v}@P#6<5|><p-B#<vGi~yn4fy%=ht6 z(hEn2==r7d>ehYVlU>5_sc+fz;6B?N1Ep2FAD`;;dswf1wJ=qEeQ!PMyZ>wd-0piP zz5Bvl_p0)lb?rPgNg>J%Z)dx=%iZ)jllZOV#mY*xX9xN=NvLo;f2n<)r1q?8%R8n3 zW?@iO+RWh6dHMjT`SHrpncKne-K8s!<dpyK+gG{8BdUH^S?Tf@w@L+>1egR`witS= z^~<LDzI>iyS~=<KkHu|YRCkFmUFgwb>?n#^^RPv@c2)74i~EZ6U%Ws0!mrl$pT*vk zKZb|9exIuO&*)($T>Psl-|CLe)+Ne)T4#^M^n-dju!PEM%&^xpW8%_TR(>X%dRO`M z1#t>AC@h)tBJuR&k5yk^U0s~5AFRFiqx;h94b#~aysQ}Jh6^=hT~oi6aIG~idVcA` zebZW(tlD>&L1k{IrEQXn+fSb}kq2XE7P=o?Wo)?o?X07BmPLX3$%3Fo5DW{fOy*fO z-1B6Z^i_jJ!PS6s%cARh>Q?;-Sdta=``C@>h<3J$Ti;(@T^+qWFV?EmxZFUZyv6+4 zhJEop``$a=(y(IqI;(u^@n1jm7W02t@zm(J%%h(&Z%jWqh_7dk-NXNFNweQi-zoRl zD~!*7U-Ztq);HJX<1((=<cfRjyzl1!b}4H2$P+p#tYq`sJo$dsG(A&MW5e_B{oCgq z=<C_Bc*P^1GnvxIFO-KbojC(EHSf_0O+k!|FV;FR1aavx$18rlBH<n49(vE*w4iIb z^HK(v^H&zXS6^#yckx$X=iKyp?>*TUxW~G^e;#-`Gr3~%;_9AWLyp}RX;+ta{%h}b zmwn-uE_-t=``5kSr?V?$SzpwjuKlWdg1Ri@m342jgg>Uv-Bi8n=W-v(-)_|jim_QK z#)j)l7IF8@ItPtAQx1n2EK&>%3<1(oQ4Fi9&P^@hyjv=FR3~UgU!>ue6Y6}ce3s^F z+zG9EermGi{+f<YS9j?}sob4-YD)emL0iL;qi18a-}|tJhOPRtVEql%t89!boL;Ro zoVKEG)t>w;1!acbseMmB_AdK+Z@v7_DSQsa>n|{7T(OgjEne}Iy{gYXrR43)&P7d= z9yMLs@nm-6MrZf8@BPx}X8gVQyE3ktr|G;0bHJ8;=NW%qlen~ehU}wQOB<`lC$^kX z4mRfV^XQ*7V_PAQY!#@GJ!_UQWv<t%yFO>WYA^{bae-Ek3-lOI#D;l4DK@+uuf_9i zVRBau>rE*U{pPNm$yanY%}%)=BBd4bQgOxC74>(LQ*KYun{+?8jCX^?v1d>28%hOK zPG0q8fvErMtGk!nQeX)6ur)m<bTx45cVFH$A`NeNj~%;kxTc2xLf^gR`=4e$kG#aN z<z3+P!0O5?PV<?ol)shTE4KEDQ4xH?{psh=NlGVIw(s8it@P`?_3n4gKcuW*5WIKV zoj3d5R;J5m{r&rCt>G7Ik9eMcMKc1Q^~^i7V{yjfnVmc3Ks{1exp+l|;i5)NbL>8! zLerm{v;<vGK5{=Jyjx3d(W$*t=P-#qJ}0qmaY}kh`Rl04|4FCbht7@-N_0^Q{PXvA z(-I4$fe<EvnrcbL3?H6;yPw^Bw!apC`?SsGZIEDt+q<WM+<U)&m)pAjdGGS&-dx^4 zgIZWsIamJgwY_n@*nj<j$$P&oFTXYS)A@T_`~(YQ_kO?3=&{YTZ6Etm<%VZ}b-8V4 zpgUTX!SGVgrnW^nwyCV`O&>Kj<xhP&J*rgl*tNLQ^-*spaW$`Q(DXutZ3>IR{$?Bb zmnY+=D1ZN8X8%&CXy5ZWr%M<<-8wv3@A~IkxBp&xtgNJHJK4&$FfsE^cl18?ig~-M z{p~Mj-~N0qIr+<<MPF-PM!B#n+`K07%jI)Ta)o$#MWWiXDse`RASG}c#Gy1`I>Qki z<x5?kUNNXdM-;|Iux?uV^mUkg{3>6|ez}w91Sc!qy>)u(ly{!2TW_n1vN3uDA|-BP z23PO)pKlh+?wq}Sr4obfn@3w`t4F`vxcl=e?#bR<-rZa-hnkWKC#F0<yYz5*soc(J z`yW2**Pgcue%*6xv3=G08f&kw3)gENv#w`2T=OcPMZsew8{-NMQ@elva(`Asd*&Pt zR}N)W9A*eH3@%BHTNjdRIw@#(Y*3;=gUHf(QpugKR-F31j4h1Iq0DAKvrW~Rb8%7Y zWB2s-?{1m%-thS%6Na~sPEPB;xA^(i-R@7evg#YnTb82g`hRy{ltq#M`iZ+M4lb|C z^?mbZbAOckzKL0Pk4x`8|KONm@IlGsTgAs_y8o5@&33;ZGIA%Ns>ci(4gvMHww};B zCEv6x>-0q3g=Qz`OnmXw%hW#OZJB3l<_^XcFK*=>T(`3Ixw#_4LGu!K1KX45c1@IA z9mD*VC!S4Vb>6;94=opezfkq=Np8n`qu)&?3`a{p-Z?l~ce%{Y-QPdVjXnO(T&{k> z|Bl$Zd&|B{?QMU)HCV1;&-YjTw{+t-Te-h|SozLuPyMp3PYfs8C`~d6s#wNdZ2z!Y z5Gi&uo-n=;U0nD?qn=}yR(be0jnH>-pPR2`O%IvTCfeZQS<Zbv|I~dGt!lUOn@e_9 zKK#bvup#a8Hr8dXtV`CHosW1r%W}VcZSL31|0n9|wRmK1?Nnlz8Cm*$A^VRDKii6q z@ZJ069-B7*y<pYG<F|I`DR29na{Bbu&v$N4E_#w@dsFDV?4AXa_bw8$4vv=C>0Rin z+IQ#d=R27N@4q0mU#*S5$?uaFL=0H89}{j^#i_k%X(-#BJ$ux|k4Cd9{AAs;YTc1D zE{2Am+}E7dTWPkcUh>Bz_37M6)1Ry@4E%Fsp7HYa3D>VIn$0fIppg4TsLI0s$&sGW zbtNxmru($ZwZ==Ro-<nz9-ZdA*8Xbghtmux=?^wfH?P|MZ$<1D<!zs@*-v3xz<r5f zORsu#+}wG$PcvW0`#aHk=5`~Qo!!rO9)567vz4Jm(BZ2JXvCwLVan$(_g0A}elo8B zwbo2hqyiZxrHkMEaw;}Ht~pjNV}<k+Vg1u#!8ewCYG1HYs3Fd->rdj6`xioXRsW27 z+OPThooSip*1($#AvOP94(@xoliNqse#`T=NG`s!`gIlSl1y0^9DBO2vgE~0n?63< z#BU`}XXY;WAsuw}jAiZ0t43i~f#nO|dEB-A%J8||t&mT_*qZshZOK!~os-LcnBCj8 zW8FgWsx#Br7Celuy!V@@mjBDgo9P<S2dn2Z{(W2bbG!1kkIUvRU>4ZQx?c9z;<8UR z+e(Z3{+w1_wfK2o+?DN{uBKGIebRd)uRxs9gVhBxAPDOuWVkSPC_Q`n<;=UT?Y4{Z zR_tgkS^D~d^3mDx$_#-T=a_WwPASoSc31wWhM$=4+VIMn^V%T4u4GX0s@2%D#nisY z$FC#qDEp5$nR9=<ZWo`|so}ZjWW;<SMh{)z+os)i`Pq=}>?P;Yw6E)aObtj%aIczM zdF;!t)iEzUL_`nVNaHnPzL0ls#oS-Y<rnzwpNjrH--KcAbasW^s`aza|GHWh=)V5# z7Xi87Pv@@4Un~CS>DJi%Cw^6bGmkH2%vf%>_0Wf0+jLn;vC7ngzq%D4f9I3gxOaNa zW~04jAKY@B8G<;G(_@=hgM+h!C{uvvqqB#M1^;|Kym@))svjQng%_#cc&EQ-OX!qe z6E^N!#Nogj{nF>mOF?C$u$^goW{cgwu#~U;Asg^$OZ|%rn+puK+Lh{CeaYcY$@~0u ztw=)-uiN`a7faKu*Sd@7KWa2#nEQI;yF~NtzEyW+ZvD%;vS;&Gce&YBeRod2e)vc7 z>)ZRz_A=Yn{az8vcIDS52AAA}#k2RFp0@wIy^V_IWDhTu(|$j9=lpcv7CCd??aIU3 zrS91NT=~R`DFC#jR}<D`RA6YdT2`mts8?6vSCCQnZjxfx{13(AGsND^j;s04lKk|Q z3qzK`skSF4+Mb_CPvU#C;zx1%49mspwsX=~s@l#;zw7bq-R?BQZIPhX<#mCEYj@sT zJ$EXPWRTjnubhAR`F~P(cCCJ^{!Y58@6N&3FF#p+%}syZAFH(MBvZhHjq?KiwV48z zf9B_(%B8yK(59!$)xvlk<TxBwDI&F5KwbJ7Rh$m4=KLIGzGr7%4*$qpe@|+9ld!h; zyqL5#f4l@6LQXJqzYJ2I-B}6h@;)z3OY*yI(YEjRyqjmv?yFX>y78%EPu$+BuP<($ zuE=;FX#HjHwwIN8OH1#s+5al;`cCm(?Q`C4)LXau^sPACIWHNm9DOIe{j2SsZ7;u< z=<+*c@9MpCu=xHWd(&xr4)bn*X>)&RnfvQ^&Ygpolsr8XUv8TG@7!#wlV;k*N5X2q zd&n_O0IkIXO<-|OdX$vRcj7*H`^JR{ffE@*3||-WYRqnCUm^Eimmzd-leOws)2%I0 zes@nr?EkjPh2fTzi2cjypUw7E9Q#!CSbeM2`;*=GPsPMl-><oUuXWSR8RmTp+du1U zUa0LKd*(<?zg&z><{N`-FMIXXox*GOZ2S1aO4#-FHc$IMdbUg&PjdRV{x2<!xx0FI z2}^+ZZvVEm?;q~`n#xcVclT_z_4(Y*|E?^yRTj3|oqtV!Tj2e&+~vmnPZ%>^a@H^J zotrXys-=zB{akLQ30@tLDY?K$Mjsd$7#gY?1RWOiRbFT8*w^&CbL!RDgL5~2njLwb z(PLg+iu}?n9*0Y%>-J@`RZXs~&Q3KpT)tM?v;W1!vqx+eFMrfO>E-RSN7g9av$QF8 znPT_!gKpK7!)7nm?{V&Dm{qvRz2us`_xe(^g8yIo8*_KvsC~3++wl&MRtAxKXN$GZ zmoBb5@NnMm%hqdm+|B)YHjg|1&#(MXjgOcz8LZyD-?49U&SoRqZ7=w5ANl>Ag(=`K zawiFt;-AT~E_ie+_TXN>HQO0NH2->h-BNcfV)>o)Vy(56rIWtCPE&Xl*P~jRackRw zEh`$nTz&VYV_s|XOF`Sq{{sKqG~|8f)jn_6yv|71J)7(+DwfzSJ)UOkb^Yrv?Y#22 z^(%kBc)NG{-8cI_B&F9zGHm*LM0L^e54!ry6M`$Y3kL2<l>S;%{`Ho~{mS2)&)pWX z&aeAb9P{j-5~FOxG@0+^5!XGpMb3OT`)jPZ62r=t`q{b`sh=Ak)i3XLVNq~J&bV$W zLPziK*Y)U1ma&7(fhh%-v6de!`+qAQ<hOQ)Eu~+W-F|KS`SjbIi&wA8?~Hc8|E=aT zkIUNg5ua|k=}+W6Ig$7LMBb}`D`(CKJ#zYYv+cGQ!MpM=EiQ>%E?>6#^`X;u--LaX zQK~4@t|~2_GLI?kcjxI@a&oid*%Xe8-feULv|{&;+$HZVVrEsyT>D%5?chP9mzoLQ zNq^s7FPoGbw(PcA?R4Hf_5I2WSNF4-E&RRqw&p+aTOKY9L7)N9XW&lB6*d-yfbV;r zrarj;jO$(LqiSA<(98YL^|v2xp0WCB!c^UuxhB6Nb&UkJswI1FJ(ZSp4_XJlSZQ;A zo6zbrN9OFxmn+`fd+hY@rn}}ZgLma#SX|O~A--ts>wi6dvmaTz+rHiOEl&OL!`xKm zhB@J13+}JDzxa<<0$<`?8?(<T-=((2tvkDF|M@%9*%T6AK3e>*EZgd2*o5`l9Pd}k zGEMNxcx_j`_o{r?|6DV+<Q%*AsixCE2AQN?RbY6il>o{Mw?IYIJacY`Pue@$Belv} zuJ3KCp8Mk;(}l3ttGnjUwW;Kf)BdY*>WssFn~G58MPbQ03w^q|JmTN`@;{$=?#LX= z)!QHSFJ3h@EvavgN4s3;EKu6oqkkiJvE9-&4|c4%D!qC2^*!8o#bVxUdr|cLLU8Pt zyLNw*6>OPKXzpph&6dd^<;1S=`{~^~<{z~#_4~{Yt)H*Q@6g@%<Mz6DODa~suBu(W zb&vJ&R~8IQWg`Aq+|kYq*GRTpbFlN@=k#a^jX&WxHSVN+eXOv&YR9{Ir(mNyjEo#t zY}bo6bmhq{s55`{itAlzQZ37Z>oQ-1d(R)L6qT;o%&??p8E9DM^Y6>b4N`A>&U}6G zbnn5#*IozjT6cA^%r?LCmA&&GYt?!!H?`TlVfyk0%XKddbLKiRMBO@@%j{pao@;IR zzuI5(Y#4NH-sB74^ZXb#ee18j?27HLO&DzZxpuwz$NQDL_~gd7k4^1*q7zH=Vs~9R zd(8XS{bXgM3q=Rc9ANF4zR0i}KAOOB#jwV%{%Y5TCBLsrxKHRZTKB;3Z(hLvYbQnT zygu?vit)>_>z^0ZxSePJQWTso(_kb%d*y4Wv)8@;q8Y37MIwk>_HcDs`JXrI6-yY3 zI9@;8^(~cQQ}nIe8&z_T^}@8ZSQTDRohy=G+IOKmB6d3Ci<fTyz58;t>$d9MsQ;0% zQaf38#g!j>Pgh5_nQg0FaCQx|-@5oy6`E3;Vf|Kt1_#;T!=VRO?QXLEx;rpDykNcX z{dk5&Aw^HuU$+)q<MnIh+mH)aX7#9kkgd$j-{9S1xAb$CZQB*6c2Rxi34cGy>1BVZ zw(w@2aP|1M?%g}rJvDrnwDZsV%iohubuDFlF=2mbg!<9wxnZwww|~w%UZ?YOx<{TM zXn5wcd2)T$`^8JwiNCQA`SW(t&xCD}ry7s;Oic{6b!%&jEKS@~m8Em;|0*r$j7|G@ z@diEq-?Ivra4bA?dR@E`pZV+S-=!K-v`#v#x6^zRx#R2XrSdbc`~thR(0{^bsamh) zp;bI9TluuB%C=_zuKq9fKcY5e`<nL8S%2U0%@4X?X7v9+TA=xK4!OsAVyCU<)fY_p zw>nBKQo{IT(arl^+pX4JDvwAlc>TgW_4=%7v3Wsd+cW>IWbFC7viJPrro7&_OMcuw z`r%Z^d!cK+sdu=I_DJo_X~+$DEXndD<?-HX*0%p4LA*?e{@HtdhDNuh8kP%NFMlm! z{cg2Uj%k9As^p1XyR;A5&py24)Aj5n@-wgOmGFLG{pry=1@S6{{(Zqa<^{2{{mV0X zx12G!t~lo6?aZnFCP>!L^!t+<@Zde)m+fb!<{sYF`sVA+(#*HlK5OqapLKifi@40z z&|i9jdy1>~8uziJ%gmcrXklUc?5o`b`@1jd)4BX#9+<!C-mD9{b~zTw+yNIQgP-|s zmO4^#_sS*JYkS=)lV=qyLrNyGLDS~XEH(R7<gkCuf&JRM&i~%iR6RE$pV1?2DbJDa z^gB^`e^h?9uQQ)#`NH=b@4kurd}m6dQ(tUc`hG(xn^#Q!i+wBC`@G7|;{4ir{Mo*D zv!C8Kip_tqVcGJxtJY09m;F`Zbw$p*$3o|Rm%q=BX}5lQ;89Cy@?$aUd-F{L%x->u zU3K$oclBl8f3x$wbDtmG_p|4%dC`2?h-Xg(7k#_ld_VQ?+koXWKUUB5mxqlbfaVPt zN=qUt(sJ)<wRgR6j?@RG@7I<LC#R$@kYc_N?IY#;^Vx5<`1_Z4FVnm8#v*%f=?N?8 zr|Z^ldLkM$J1H;sn$_!{vt|3<&pK1_MQTmnwE5dV{#(=7<DU8C*!x(!3x+A1L!W-E z&3dBuI)CllWq0pw>-l&@5kASyqVVC_6q}t<0>=93`#O`pKg|E4WwU+bzI6}c)K|GM zRDECdY2U%`&rH|P*RnfyeW3*V5rew-MY9t(JFV@N(4Qi;<J79h0n2Buu)n&Jjge!r z12Y2y189n-!uE($-Nm_wo9;H=G_DSFk$;tau&h4IvQ&GopCjMn4;vFZ8CazI^UKa% z*Y>?-*Zk4p!_CH2;p1mt=F6@yTi*P3)4yvckKL|x(Kz>Kl_PAVn8RT~*p#Px1mbQU zy#FFpuvVk^*=&&`XS6GeLiYt(ayW<v&7TMz7yWV1g`w*Fy!%_uT&pdse|=!3*s|Sg z_NVU2<305E+S{~fiHKXjX5VOkG%J3+2qS0(D8njdCV>^4fuHJI-KRfkjnpigzbBNh zHT_x;d+VpETFOG5kKW%GQ#q<BXJ@7(*pTpe<1B_F)zftT>qjYB3N0@Pmf8H>i|hMG zt-Q4Yj4RU4=SP`k-%=9(9q?+sZ}a5?Y9)*Q|5+?(@qBey(0Yl-y2}nihILo)ayU3} z9{uhw@@D=_;j93`+D*;>PWFG%-nDVPj(zaHAk!22%=M3L9&>DB^pst1-1p#q#H+~$ z$>8A|w)?YBF-_R=FGg1Nf7#E8UF(;Ab-&k~_pW=o$D{c_Pro|^+W0sVkwqHjZ2EI% zk@W9R%6~UC|6BRO&T=6e>+f{2`}T@rqNlG{i*h)01<h}fgbXeJxEEr^`DKOA)5ld3 zr&mY!icNNK)(GZh0&TeZ^Y`|r@6bNHK!cR^m5p;h97=m8dPkgJ&t{j)lt1%pmR@HB z4SONE)%?%DH7m@Xo;v@3l~8${GJ~NU>mj$*pamT<Y@jWb0sI0DQkqv|55`ucx<{Vl z{^^y^!Zcygsz06*^Dikred}qfy{dN+SMlFHbC_35Tiy^`_P6PyhJaZVqOE7DlvT|7 zeLFkI=EI0k;!m3$+9Iv`OJ94*|IO1}<&Vx>sJUt`!;7~2^LyMIH(W$i;~sx2*J|&$ z&bfo9ru9mDB4o^#cb~cNwcfQH=BHxoT<%r9kO;~B68Pm{j`6X@4mUDFF^4q-8m>$; z-_cYZ+sF{2_%(6HMwil%r?t)R&x-$k7TzMg^K`$*r0I93)N9Lr4gQyVHTyuip8o#n zP5p=WO7sU=FJ~=R_~zKtDqxm_(sEjox96<ro!49}3%FMDAMR0IwOH%Dbj|#*x$IF^ z`~5BTlLd;er7ykA5WC^O!P5gt$DFVDu_&x)Q-Tg!IxqyWgIgx7OaZM|p2Pj-FRxp= z_Jp~q##vi^^_FE*&j}onU@1xG>fiVmG;4A-0FnK8C0=b<lehjsoO*&#hO55iw$ML0 zYl6VDCO0g6EjRpV`rF<U&#!uEGNa}*xtnt?Or1QzcXdl?vgGacTR9v8cwNB_*@jmQ z3|Br(h&^aq{xRX`yI^%tp(oIwkW+APcHSb1!d0QS8sa{EdSkHj`kuO^oS9}TtXD2* z{<$<u{1nrK<$G%~cR%20?RB_e5ZcV3;?dRJYpL*mJtze+@Jg~QXlslJ`6CA^!4w$w z-s1S&cjb7rvj4Lpj@Kub#4g+_#qvdKs?UF)|J%YnExp9Om;+qDcbWvPa1}6%xT?T_ z=(RB{xwPlM_Ky9w91iQlrZNY#q;CkcuKJ!*@<L9=q<1G@i2MK9`KMP{u7ACj^RC40 zDPH|AUcNZ@LpNyGhEJLaUj!Duo2Z}Py1@}CafvQm|DY}@b#KhUzi%BGs<wU=Xwd3i zyW*O{%f5?SUf)<a_m6i|W~5ZBCc9Yt=U<%{OS0ap9^ZC;ag)Sto$!~Dv7zEh434IV z0&4=-Lay+F`f9{D{ljGDO@izT-pTf!QI21yyjz^ruHBq#^0xC8k26nCo%1I&bl1y} zpQ<%G1sFM2EzA;VFklC*cUoYkz;M-gllBhoZNFEa5WVx7C5G*v=&BrsRjYEs??33< zkT`2Y-WzEjwl4{Ns>#-;me$W*^+#`SOa}wY)c{ithaFs?y@p^@_^${~<%&DFcU{15 zh7!#acjD|!+7`vSE1&lgH%po#8|fT4=iIkchFM}cA$7|_+snT7+$>wN`r_v$9?zd@ z#;@IdN$l_q)#~q`Vkh}7i*CI&@2cwUP?iP9p1k&B3psnm{^_ZIi#6Q;buTYbWD*E* zM`ZoI;URyx-j%k_VpsT?e!Wb#ch~LUvW(+<HkIl!2e99aIlbxaiQj%pGYeB0PpsIT zWBN1MbAQ^C8Q$+cJ>3*LY2E)%f-lWN_66)aKlR+_Re?VzAASDe*wecQ_Vt_!o_l>y zotNc?8HME>iLHp@^qwW}3$AyiO!LpjZ&lI(EzuWDVcv9%A>{GSkoBSsOS1RHM3`>X zi1xi#B_hA*)wkqjC*A&P@O-X39DE?<_R6BIYAc>ClZmLw&QW)Jz4BG+_xwrQ&L`Xv zcUpm1c(kD0)fv=`S@15LarJJ75Y3wbr?#yL$epq-EH!cJHRaH4w@%wuE@o(XXy1Fg z`m(RYQTL0|b@lshn9na1=TLM;<lR!A=6y}oa~J%Z-Z(v7@Z}Zzo_U|57&|sbU43YF zDtvOB&F`nzp4?pV=gvB=rOd4}R`2y==$a6}boR_?3CTuwIXf;{E`0B6p16inF=Q2L znf;3G>khEVj2(uLi`w7mKfheLn?c2T@9VQn6N>KHFr0kN@At3C-~3m6+22J<e`7ZM zHvl#1WH}N`5rtD#@s&?p@ACbVIUI_Vm${x&&)4JE{%XebVruI1txOY^>*O!|=P=WA zx6}Ime_!{MT$+DGm$4^WvBk6l-lKW3Rj|A3>JP4W`Te{6na+lo<+#_)3%Po2amm6@ zo0R2GAD+YfLX>gEEX#%&*X$S;{&RSmv@f0Gz5^nV*7rt)#1z`<f87o8(6#A}-?xO@ zJnz04pOWsUSu*X@%#tZA3S37|SD$p_-@8lLka<GQjfszhR!(oyihaGeY%a%Fb|;S4 zK}gf10uA?A9=4v%s$~7X%jqn$K&*$yalT7Cr!C#N<W+@?lfPiBp>VPFQsoA#Q`07w zU3z=MPj$6SMwVnloL$DxxRd@qtGAf`{QCH`yk90~&<#K9WoPa-2sr)7LL~ggUG8g{ z1#DNS@7S}!!}<K_K%1cDuDi;oPm}!>R$cz;n$}4-v4%U#FFtNg;p|-GT>k85<W@e1 zDbt&Fm9M!WvNimRf$B4jd7mV^HCEY7t6}|l)@#M54gn{Qcugiy(ZCSG4qE=!;j-w* z@9nIh-8?7Qo`80e2slMpBe}wAnP4m{j}wRDnm|N&E�@6L8{4UFFE2<<;B2zdrh( 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 Wd)L2ZOEcm?@#yL5=d#Wzp$Pz+Xgdx7 literal 0 HcmV?d00001 diff --git a/doc/scenario2/scenario2.doml b/doc/scenario2/scenario2.doml new file mode 100644 index 0000000..acc6525 --- /dev/null +++ b/doc/scenario2/scenario2.doml @@ -0,0 +1,112 @@ +doml scenario2 + +infrastructure infra { + + vm vm1 { + os "ami-02a6bfdcf8224bd77" + iface i1 { + belongs_to subnet1 + } + credentials VM1SshKey + } + + vm vm2 { + os "ami-02a6bfdcf8224bd77" + iface i1 { + belongs_to subnet2 + } + credentials VM2SshKey + } + + key_pair VM1SshKey { + user "vm1user" + keyfile "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD3F6tyPEFEzV0LX3X8BsXdMsQz1x2cEikKDEY0aIj41qgxMCP/iteneqXSIFZBp5vizPvaoIR3Um9xK7PGoW8giupGn+EPuxIA4cDM4vzOqOkiMPhz5XK0whEjkVzTo4+S0puvDZuwIsdiW9mxhJc7tgBNL0cYlWSYVkz4G/fslNfRPW5mYAM49f4fhtxPb5ok4Q2Lg9dPKVHO/Bgeu5woMc7RY0p1ej6D4CKFE6lymSDJpW0YHX/wqE9+cfEauh7xZcG0q9t2ta6F6fmX0agvpFyZo8aFbXeUBr7osSCJNgvavWbM/06niWrOvYX2xwWdhXmXSrbX8ZbabVohBK41 email@example.com" + } + key_pair VM2SshKey { + user "vm2user" + keyfile "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD3F6tyPEFEzV0LX3X8BsXdMsQz1x2cEikKDEY0aIj41qgxMCP/iteneqXSIFZBp5vizPvaoIR3Um9xK7PGoW8giupGn+EPuxIA4cDM4vzOqOkiMPhz5XK0whEjkVzTo4+S0puvDZuwIsdiW9mxhJc7tgBNL0cYlWSYVkz4G/fslNfRPW5mYAM49f4fhtxPb5ok4Q2Lg9dPKVHO/Bgeu5woMc7RY0p1ej6D4CKFE6lymSDJpW0YHX/wqE9+cfEauh7xZcG0q9t2ta6F6fmX0agvpFyZo8aFbXeUBr7osSCJNgvavWbM/06niWrOvYX2xwWdhXmXSrbX8ZbabVohBK41 email@example.com" + } + + net net1 { + address "10.100.0.0/16" + protocol "tcp/ip" + subnet subnet1 { + address "10.100.1.0/24" + protocol "tcp/ip" + } + subnet subnet2 { + address "10.100.2.0/24" + protocol "tcp/ip" + } + } + + security_group sg { + egress out { + protocol "-1" + from_port 0 + to_port 0 + cidr ["0.0.0.0/0"] + } + + ingress ssh { + protocol "tcp" + from_port 22 + to_port 22 + cidr ["10.100.1.0/24"] + } + ifaces i1 + + } +} + +concretizations { + concrete_infrastructure con_infra_openstack { + provider openstack { + properties {} + + vm concrete_vm { + properties { + vm_flavor = "small-centos"; + } + maps vm1 + } + + vm concrete_vm2 { + properties { + vm_flavor = "small-centos"; + } + maps vm2 + } + + net concrete_net { + properties {} + maps net1 + } + } + } + concrete_infrastructure con_infra_aws { + provider aws { + properties {} + + vm concrete_vm { + properties { + instance_type = "t2.micro"; + } + maps vm1 + } + + vm concrete_vm2 { + properties { + instance_type = "t2.micro"; + } + maps vm2 + } + + net concrete_net { + properties {} + maps net1 + } + } + } + active con_infra_aws +} diff --git a/doc/scenario2/scenario2.domlx b/doc/scenario2/scenario2.domlx new file mode 100644 index 0000000..2b0310a --- /dev/null +++ b/doc/scenario2/scenario2.domlx @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="ASCII"?> +<commons:DOMLModel xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:commons="http://www.piacere-project.eu/doml/commons" xmlns:infra="http://www.piacere-project.eu/doml/infrastructure" name="scenario2" activeInfrastructure="//@concretizations.1"> + <infrastructure name="infra"> + <nodes xsi:type="infra:VirtualMachine" name="vm1" os="ami-02a6bfdcf8224bd77" credentials="//@infrastructure/@credentials.0"> + <ifaces name="i1" belongsTo="//@infrastructure/@networks.0/@subnets.0" associated="//@infrastructure/@securityGroups.0"/> + </nodes> + <nodes xsi:type="infra:VirtualMachine" name="vm2" os="ami-02a6bfdcf8224bd77" credentials="//@infrastructure/@credentials.1"> + <ifaces name="i1" belongsTo="//@infrastructure/@networks.0/@subnets.1"/> + </nodes> + <credentials xsi:type="infra:KeyPair" name="VM1SshKey" user="vm1user" keyfile="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD3F6tyPEFEzV0LX3X8BsXdMsQz1x2cEikKDEY0aIj41qgxMCP/iteneqXSIFZBp5vizPvaoIR3Um9xK7PGoW8giupGn+EPuxIA4cDM4vzOqOkiMPhz5XK0whEjkVzTo4+S0puvDZuwIsdiW9mxhJc7tgBNL0cYlWSYVkz4G/fslNfRPW5mYAM49f4fhtxPb5ok4Q2Lg9dPKVHO/Bgeu5woMc7RY0p1ej6D4CKFE6lymSDJpW0YHX/wqE9+cfEauh7xZcG0q9t2ta6F6fmX0agvpFyZo8aFbXeUBr7osSCJNgvavWbM/06niWrOvYX2xwWdhXmXSrbX8ZbabVohBK41 email@example.com"/> + <credentials xsi:type="infra:KeyPair" name="VM2SshKey" user="vm2user" keyfile="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD3F6tyPEFEzV0LX3X8BsXdMsQz1x2cEikKDEY0aIj41qgxMCP/iteneqXSIFZBp5vizPvaoIR3Um9xK7PGoW8giupGn+EPuxIA4cDM4vzOqOkiMPhz5XK0whEjkVzTo4+S0puvDZuwIsdiW9mxhJc7tgBNL0cYlWSYVkz4G/fslNfRPW5mYAM49f4fhtxPb5ok4Q2Lg9dPKVHO/Bgeu5woMc7RY0p1ej6D4CKFE6lymSDJpW0YHX/wqE9+cfEauh7xZcG0q9t2ta6F6fmX0agvpFyZo8aFbXeUBr7osSCJNgvavWbM/06niWrOvYX2xwWdhXmXSrbX8ZbabVohBK41 email@example.com"/> + <securityGroups name="sg" ifaces="//@infrastructure/@nodes.0/@ifaces.0"> + <rules name="out" protocol="-1" fromPort="0" toPort="0"> + <cidr>0.0.0.0/0</cidr> + </rules> + <rules name="ssh" kind="INGRESS" protocol="tcp" fromPort="22" toPort="22"> + <cidr>10.100.1.0/24</cidr> + </rules> + </securityGroups> + <networks name="net1" protocol="tcp/ip" addressRange="10.100.0.0/16"> + <subnets name="subnet1" protocol="tcp/ip" addressRange="10.100.1.0/24" connectedIfaces="//@infrastructure/@nodes.0/@ifaces.0"/> + <subnets name="subnet2" protocol="tcp/ip" addressRange="10.100.2.0/24" connectedIfaces="//@infrastructure/@nodes.1/@ifaces.0"/> + </networks> + </infrastructure> + <concretizations name="con_infra_openstack"> + <providers name="openstack"> + <vms name="concrete_vm" maps="//@infrastructure/@nodes.0"> + <annotations xsi:type="commons:SProperty" key="vm_flavor" value="small-centos"/> + </vms> + <vms name="concrete_vm2" maps="//@infrastructure/@nodes.1"> + <annotations xsi:type="commons:SProperty" key="vm_flavor" value="small-centos"/> + </vms> + <networks name="concrete_net" maps="//@infrastructure/@networks.0"/> + </providers> + </concretizations> + <concretizations name="con_infra_aws"> + <providers name="aws"> + <vms name="concrete_vm" maps="//@infrastructure/@nodes.0"> + <annotations xsi:type="commons:SProperty" key="instance_type" value="t2.micro"/> + </vms> + <vms name="concrete_vm2" maps="//@infrastructure/@nodes.1"> + <annotations xsi:type="commons:SProperty" key="instance_type" value="t2.micro"/> + </vms> + <networks name="concrete_net" maps="//@infrastructure/@networks.0"/> + </providers> + </concretizations> +</commons:DOMLModel> diff --git a/doc/scenario3/scenario3.doml b/doc/scenario3/scenario3.doml new file mode 100644 index 0000000..bdf52d6 --- /dev/null +++ b/doc/scenario3/scenario3.doml @@ -0,0 +1,128 @@ +doml scenario3 + +application app { + + software_component nginx { + properties { + // site + source_code="/usr/share/nginx/html/index.html"; + } + } +} + + +infrastructure infra { + + vm vm1 { + os "ami-02a6bfdcf8224bd77" + iface i1 { + belongs_to subnet1 + } + credentials VM1SshKey + } + + vm vm2 { + os "ami-02a6bfdcf8224bd77" + iface i1 { + belongs_to subnet2 + } + credentials VM2SshKey + } + + key_pair VM1SshKey { + user "vm1user" + keyfile "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD3F6tyPEFEzV0LX3X8BsXdMsQz1x2cEikKDEY0aIj41qgxMCP/iteneqXSIFZBp5vizPvaoIR3Um9xK7PGoW8giupGn+EPuxIA4cDM4vzOqOkiMPhz5XK0whEjkVzTo4+S0puvDZuwIsdiW9mxhJc7tgBNL0cYlWSYVkz4G/fslNfRPW5mYAM49f4fhtxPb5ok4Q2Lg9dPKVHO/Bgeu5woMc7RY0p1ej6D4CKFE6lymSDJpW0YHX/wqE9+cfEauh7xZcG0q9t2ta6F6fmX0agvpFyZo8aFbXeUBr7osSCJNgvavWbM/06niWrOvYX2xwWdhXmXSrbX8ZbabVohBK41 email@example.com" + } + key_pair VM2SshKey { + user "vm2user" + keyfile "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD3F6tyPEFEzV0LX3X8BsXdMsQz1x2cEikKDEY0aIj41qgxMCP/iteneqXSIFZBp5vizPvaoIR3Um9xK7PGoW8giupGn+EPuxIA4cDM4vzOqOkiMPhz5XK0whEjkVzTo4+S0puvDZuwIsdiW9mxhJc7tgBNL0cYlWSYVkz4G/fslNfRPW5mYAM49f4fhtxPb5ok4Q2Lg9dPKVHO/Bgeu5woMc7RY0p1ej6D4CKFE6lymSDJpW0YHX/wqE9+cfEauh7xZcG0q9t2ta6F6fmX0agvpFyZo8aFbXeUBr7osSCJNgvavWbM/06niWrOvYX2xwWdhXmXSrbX8ZbabVohBK41 email@example.com" + } + + net net1 { + address "10.100.0.0/16" + protocol "tcp/ip" + subnet subnet1 { + address "10.100.1.0/24" + protocol "tcp/ip" + } + subnet subnet2 { + address "10.100.2.0/24" + protocol "tcp/ip" + } + } + + security_group sg { + egress out { + protocol "-1" + from_port 0 + to_port 0 + cidr ["0.0.0.0/0"] + } + + ingress ssh { + protocol "tcp" + from_port 22 + to_port 22 + cidr ["10.100.1.0/24"] + } + ifaces i1 + + } +} + +deployment config { + nginx -> vm1 +} + + +concretizations { + concrete_infrastructure con_infra_openstack { + provider openstack { + properties {} + + vm concrete_vm { + properties { + vm_flavor = "small-centos"; + } + maps vm1 + } + + vm concrete_vm2 { + properties { + vm_flavor = "small-centos"; + } + maps vm2 + } + + net concrete_net { + properties {} + maps net1 + } + } + } + concrete_infrastructure con_infra_aws { + provider aws { + properties {} + + vm concrete_vm { + properties { + instance_type = "t2.micro"; + } + maps vm1 + } + + vm concrete_vm2 { + properties { + instance_type = "t2.micro"; + } + maps vm2 + } + + net concrete_net { + properties {} + maps net1 + } + } + } + active con_infra_aws +} diff --git a/doc/scenario3/scenario3.domlx b/doc/scenario3/scenario3.domlx new file mode 100644 index 0000000..2361dda --- /dev/null +++ b/doc/scenario3/scenario3.domlx @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="ASCII"?> +<commons:DOMLModel xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:app="http://www.piacere-project.eu/doml/application" xmlns:commons="http://www.piacere-project.eu/doml/commons" xmlns:infra="http://www.piacere-project.eu/doml/infrastructure" name="scenario3" activeInfrastructure="//@concretizations.1"> + <application name="app"> + <components xsi:type="app:SoftwareComponent" name="nginx"> + <annotations xsi:type="commons:SProperty" key="source_code" value="/usr/share/nginx/html/index.html"/> + </components> + </application> + <infrastructure name="infra"> + <nodes xsi:type="infra:VirtualMachine" name="vm1" os="ami-02a6bfdcf8224bd77" credentials="//@infrastructure/@credentials.0"> + <ifaces name="i1" belongsTo="//@infrastructure/@networks.0/@subnets.0" associated="//@infrastructure/@securityGroups.0"/> + </nodes> + <nodes xsi:type="infra:VirtualMachine" name="vm2" os="ami-02a6bfdcf8224bd77" credentials="//@infrastructure/@credentials.1"> + <ifaces name="i1" belongsTo="//@infrastructure/@networks.0/@subnets.1"/> + </nodes> + <credentials xsi:type="infra:KeyPair" name="VM1SshKey" user="vm1user" keyfile="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD3F6tyPEFEzV0LX3X8BsXdMsQz1x2cEikKDEY0aIj41qgxMCP/iteneqXSIFZBp5vizPvaoIR3Um9xK7PGoW8giupGn+EPuxIA4cDM4vzOqOkiMPhz5XK0whEjkVzTo4+S0puvDZuwIsdiW9mxhJc7tgBNL0cYlWSYVkz4G/fslNfRPW5mYAM49f4fhtxPb5ok4Q2Lg9dPKVHO/Bgeu5woMc7RY0p1ej6D4CKFE6lymSDJpW0YHX/wqE9+cfEauh7xZcG0q9t2ta6F6fmX0agvpFyZo8aFbXeUBr7osSCJNgvavWbM/06niWrOvYX2xwWdhXmXSrbX8ZbabVohBK41 email@example.com"/> + <credentials xsi:type="infra:KeyPair" name="VM2SshKey" user="vm2user" keyfile="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD3F6tyPEFEzV0LX3X8BsXdMsQz1x2cEikKDEY0aIj41qgxMCP/iteneqXSIFZBp5vizPvaoIR3Um9xK7PGoW8giupGn+EPuxIA4cDM4vzOqOkiMPhz5XK0whEjkVzTo4+S0puvDZuwIsdiW9mxhJc7tgBNL0cYlWSYVkz4G/fslNfRPW5mYAM49f4fhtxPb5ok4Q2Lg9dPKVHO/Bgeu5woMc7RY0p1ej6D4CKFE6lymSDJpW0YHX/wqE9+cfEauh7xZcG0q9t2ta6F6fmX0agvpFyZo8aFbXeUBr7osSCJNgvavWbM/06niWrOvYX2xwWdhXmXSrbX8ZbabVohBK41 email@example.com"/> + <securityGroups name="sg" ifaces="//@infrastructure/@nodes.0/@ifaces.0"> + <rules name="out" protocol="-1" fromPort="0" toPort="0"> + <cidr>0.0.0.0/0</cidr> + </rules> + <rules name="ssh" kind="INGRESS" protocol="tcp" fromPort="22" toPort="22"> + <cidr>10.100.1.0/24</cidr> + </rules> + </securityGroups> + <networks name="net1" protocol="tcp/ip" addressRange="10.100.0.0/16"> + <subnets name="subnet1" protocol="tcp/ip" addressRange="10.100.1.0/24" connectedIfaces="//@infrastructure/@nodes.0/@ifaces.0"/> + <subnets name="subnet2" protocol="tcp/ip" addressRange="10.100.2.0/24" connectedIfaces="//@infrastructure/@nodes.1/@ifaces.0"/> + </networks> + </infrastructure> + <concretizations name="con_infra_openstack"> + <providers name="openstack"> + <vms name="concrete_vm" maps="//@infrastructure/@nodes.0"> + <annotations xsi:type="commons:SProperty" key="vm_flavor" value="small-centos"/> + </vms> + <vms name="concrete_vm2" maps="//@infrastructure/@nodes.1"> + <annotations xsi:type="commons:SProperty" key="vm_flavor" value="small-centos"/> + </vms> + <networks name="concrete_net" maps="//@infrastructure/@networks.0"/> + </providers> + </concretizations> + <concretizations name="con_infra_aws"> + <providers name="aws"> + <vms name="concrete_vm" maps="//@infrastructure/@nodes.0"> + <annotations xsi:type="commons:SProperty" key="instance_type" value="t2.micro"/> + </vms> + <vms name="concrete_vm2" maps="//@infrastructure/@nodes.1"> + <annotations xsi:type="commons:SProperty" key="instance_type" value="t2.micro"/> + </vms> + <networks name="concrete_net" maps="//@infrastructure/@networks.0"/> + </providers> + </concretizations> + <configurations name="config"> + <deployments component="//@application/@components.0" node="//@infrastructure/@nodes.0"/> + </configurations> +</commons:DOMLModel> diff --git a/doc/wordpress_example/wordpress.doml b/doc/wordpress_example/wordpress.doml new file mode 100644 index 0000000..642d3d3 --- /dev/null +++ b/doc/wordpress_example/wordpress.doml @@ -0,0 +1,121 @@ +doml wordpress + +application app { + software_component mysql { + properties { + db_user = "app1user"; + db_password = "app1user"; + db_name = "app1"; + } + provides { + DB_interface + } + } + + software_component wordpress { + properties { + wordpress_db_host = "db_host"; + wordpress_db_user = "app1user"; + wordpress_db_password = "app1user"; + wordpress_db_name = "app1"; + wordpress_table_prefix = "wp"; + } + consumes { + DB_interface + } + } + +} + +infrastructure infra { + key_pair ssh_key { + keyfile "local path to ssh key" + user "myuser" + } + + vm vm1 { + os "ubuntu-20.04.3" + credentials ssh_key + iface i1 { + belongs_to subnet1 + } + } + + vm vm2 { + os "ubuntu-20.04.3" + credentials ssh_key + iface i2 { + belongs_to subnet1 + } + } + + net net1 { + address "10.10.10.0/24" + protocol "tcp/ip" + subnet subnet1 { + address "10.100.1.0/24" + protocol "tcp/ip" + } + } +} + +deployment config { + mysql -> vm1, + wordpress -> vm2 +} + + +active deployment config + + +concretizations { + concrete_infrastructure con_os_infra { + provider openstack { + properties {} + + vm concrete_vm1 { + properties { + vm_flavor = "small-centos"; + } + maps vm1 + } + + vm concrete_vm2 { + properties { + vm_flavor = "small-centos"; + } + maps vm2 + } + + net concrete_net { + properties {} + maps net1 + } + } + } + concrete_infrastructure con_aws_infra { + provider aws { + properties {} + + vm concrete_vm1 { + properties { + vm_flavor = "t2.micro"; + } + maps vm1 + } + + vm concrete_vm2 { + properties { + vm_flavor = "t2.micro"; + } + maps vm2 + } + + net concrete_net { + properties {} + maps net1 + } + } + } + active con_os_infra +} \ No newline at end of file diff --git a/doc/wordpress_example/wordpress.domlx b/doc/wordpress_example/wordpress.domlx new file mode 100644 index 0000000..6a8861b --- /dev/null +++ b/doc/wordpress_example/wordpress.domlx @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="ASCII"?> +<commons:DOMLModel xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:app="http://www.piacere-project.eu/doml/application" xmlns:commons="http://www.piacere-project.eu/doml/commons" xmlns:infra="http://www.piacere-project.eu/doml/infrastructure" name="wordpress" activeConfiguration="//@configurations.0" activeInfrastructure="//@concretizations.0"> + <application name="app"> + <components xsi:type="app:SoftwareComponent" name="mysql"> + <annotations xsi:type="commons:SProperty" key="db_user" value="app1user"/> + <annotations xsi:type="commons:SProperty" key="db_password" value="app1user"/> + <annotations xsi:type="commons:SProperty" key="db_name" value="app1"/> + <exposedInterfaces name="DB_interface"/> + </components> + <components xsi:type="app:SoftwareComponent" name="wordpress" consumedInterfaces="//@application/@components.0/@exposedInterfaces.0"> + <annotations xsi:type="commons:SProperty" key="wordpress_db_host" value="db_host"/> + <annotations xsi:type="commons:SProperty" key="wordpress_db_user" value="app1user"/> + <annotations xsi:type="commons:SProperty" key="wordpress_db_password" value="app1user"/> + <annotations xsi:type="commons:SProperty" key="wordpress_db_name" value="app1"/> + <annotations xsi:type="commons:SProperty" key="wordpress_table_prefix" value="wp"/> + </components> + </application> + <infrastructure name="infra"> + <nodes xsi:type="infra:VirtualMachine" name="vm1" os="ubuntu-20.04.3" credentials="//@infrastructure/@credentials.0"> + <ifaces name="i1" belongsTo="//@infrastructure/@networks.0/@subnets.0"/> + </nodes> + <nodes xsi:type="infra:VirtualMachine" name="vm2" os="ubuntu-20.04.3" credentials="//@infrastructure/@credentials.0"> + <ifaces name="i2" belongsTo="//@infrastructure/@networks.0/@subnets.0"/> + </nodes> + <credentials xsi:type="infra:KeyPair" name="ssh_key" user="myuser" keyfile="local path to ssh key"/> + <networks name="net1" protocol="tcp/ip" addressRange="10.10.10.0/24"> + <subnets name="subnet1" protocol="tcp/ip" addressRange="10.100.1.0/24" connectedIfaces="//@infrastructure/@nodes.0/@ifaces.0 //@infrastructure/@nodes.1/@ifaces.0"/> + </networks> + </infrastructure> + <concretizations name="con_os_infra"> + <providers name="openstack"> + <vms name="concrete_vm1" maps="//@infrastructure/@nodes.0"> + <annotations xsi:type="commons:SProperty" key="vm_flavor" value="small-centos"/> + </vms> + <vms name="concrete_vm2" maps="//@infrastructure/@nodes.1"> + <annotations xsi:type="commons:SProperty" key="vm_flavor" value="small-centos"/> + </vms> + <networks name="concrete_net" maps="//@infrastructure/@networks.0"/> + </providers> + </concretizations> + <concretizations name="con_aws_infra"> + <providers name="aws"> + <vms name="concrete_vm1" maps="//@infrastructure/@nodes.0"> + <annotations xsi:type="commons:SProperty" key="vm_flavor" value="t2.micro"/> + </vms> + <vms name="concrete_vm2" maps="//@infrastructure/@nodes.1"> + <annotations xsi:type="commons:SProperty" key="vm_flavor" value="t2.micro"/> + </vms> + <networks name="concrete_net" maps="//@infrastructure/@networks.0"/> + </providers> + </concretizations> + <configurations name="config"> + <deployments component="//@application/@components.0" node="//@infrastructure/@nodes.0"/> + <deployments component="//@application/@components.1" node="//@infrastructure/@nodes.1"/> + </configurations> +</commons:DOMLModel> diff --git a/doc/wordpress_example/wordpress/config.yaml b/doc/wordpress_example/wordpress/config.yaml new file mode 100644 index 0000000..1933bb2 --- /dev/null +++ b/doc/wordpress_example/wordpress/config.yaml @@ -0,0 +1,7 @@ +--- +iac: +- terraform +- piacere_monitoring +- mysql +- wordpress +... \ No newline at end of file diff --git a/doc/wordpress_example/wordpress/mysql/config.yaml b/doc/wordpress_example/wordpress/mysql/config.yaml new file mode 100644 index 0000000..dff8ba5 --- /dev/null +++ b/doc/wordpress_example/wordpress/mysql/config.yaml @@ -0,0 +1,8 @@ + +--- +input: + - instance_ip_vm1 + - instance_server_private_key_ssh_key +output: [] +engine: ansible +... diff --git a/doc/wordpress_example/wordpress/mysql/inventory.j2 b/doc/wordpress_example/wordpress/mysql/inventory.j2 new file mode 100644 index 0000000..f08ec2c --- /dev/null +++ b/doc/wordpress_example/wordpress/mysql/inventory.j2 @@ -0,0 +1,9 @@ + + +[servers_for_mysql] +{{ instance_ip_vm1 }} + +[servers_for_mysql:vars] +ansible_connection=ssh +ansible_user=ubuntu +ansible_ssh_private_key_file=ssh_key diff --git a/output_files_generated/wordpress_azure/ansible/mysql.play b/doc/wordpress_example/wordpress/mysql/main.yml similarity index 99% rename from output_files_generated/wordpress_azure/ansible/mysql.play rename to doc/wordpress_example/wordpress/mysql/main.yml index d273e15..73257f6 100644 --- a/output_files_generated/wordpress_azure/ansible/mysql.play +++ b/doc/wordpress_example/wordpress/mysql/main.yml @@ -1,3 +1,5 @@ + + --- - hosts: DB become: yes diff --git a/doc/wordpress_example/wordpress/mysql/ssh_key.j2 b/doc/wordpress_example/wordpress/mysql/ssh_key.j2 new file mode 100644 index 0000000..4d512f8 --- /dev/null +++ b/doc/wordpress_example/wordpress/mysql/ssh_key.j2 @@ -0,0 +1 @@ +{{ instance_server_private_key_ssh_key }} diff --git a/templates/ansible/ubuntu/monitoring/ansible.cfg b/doc/wordpress_example/wordpress/piacere_monitoring/ansible.cfg similarity index 100% rename from templates/ansible/ubuntu/monitoring/ansible.cfg rename to doc/wordpress_example/wordpress/piacere_monitoring/ansible.cfg diff --git a/doc/wordpress_example/wordpress/piacere_monitoring/ansible_requirements.yml b/doc/wordpress_example/wordpress/piacere_monitoring/ansible_requirements.yml new file mode 100644 index 0000000..47808cf --- /dev/null +++ b/doc/wordpress_example/wordpress/piacere_monitoring/ansible_requirements.yml @@ -0,0 +1,8 @@ +roles: +# - name: dj-wasabi.telegraf +# version: 0.13.3 +# source: https://galaxy.ansible.com + - name: dj-wasabi.telegraf + src: https://github.com/dj-wasabi/ansible-telegraf.git + scm: git + version: 0.13.3 diff --git a/doc/wordpress_example/wordpress/piacere_monitoring/config.yaml b/doc/wordpress_example/wordpress/piacere_monitoring/config.yaml new file mode 100644 index 0000000..dff8ba5 --- /dev/null +++ b/doc/wordpress_example/wordpress/piacere_monitoring/config.yaml @@ -0,0 +1,8 @@ + +--- +input: + - instance_ip_vm1 + - instance_server_private_key_ssh_key +output: [] +engine: ansible +... diff --git a/templates/ansible/ubuntu/monitoring/hosts.yaml b/doc/wordpress_example/wordpress/piacere_monitoring/hosts.yaml similarity index 100% rename from templates/ansible/ubuntu/monitoring/hosts.yaml rename to doc/wordpress_example/wordpress/piacere_monitoring/hosts.yaml diff --git a/templates/ansible/ubuntu/monitoring/install_playbook_requirements.sh b/doc/wordpress_example/wordpress/piacere_monitoring/install_playbook_requirements.sh similarity index 100% rename from templates/ansible/ubuntu/monitoring/install_playbook_requirements.sh rename to doc/wordpress_example/wordpress/piacere_monitoring/install_playbook_requirements.sh diff --git a/doc/wordpress_example/wordpress/piacere_monitoring/inventory.j2 b/doc/wordpress_example/wordpress/piacere_monitoring/inventory.j2 new file mode 100644 index 0000000..fda17b3 --- /dev/null +++ b/doc/wordpress_example/wordpress/piacere_monitoring/inventory.j2 @@ -0,0 +1,9 @@ + + +[servers_for_piacere_monitoring] +{{ instance_ip_vm1 }} + +[servers_for_piacere_monitoring:vars] +ansible_connection=ssh +ansible_user=ubuntu +ansible_ssh_private_key_file=ssh_key diff --git a/doc/wordpress_example/wordpress/piacere_monitoring/main.yml b/doc/wordpress_example/wordpress/piacere_monitoring/main.yml new file mode 100644 index 0000000..a0edc4f --- /dev/null +++ b/doc/wordpress_example/wordpress/piacere_monitoring/main.yml @@ -0,0 +1,22 @@ +--- +- hosts: localhost + tasks: + - name: print disclamer + debug: + msg: this can also be done with "ansible-galaxy install -r requirements" + - name: install telegraf from galaxy + community.general.ansible_galaxy_install: + type: role + requirements_file: ansible_requirements.yml + +- hosts: all + pre_tasks: + - name: Ensure gnupg package + package: + name: gnupg + state: present + become: true + vars_files: + - vars/main.yaml + roles: + - dj-wasabi.telegraf diff --git a/templates/ansible/ubuntu/monitoring/run-playbook.sh b/doc/wordpress_example/wordpress/piacere_monitoring/run-playbook.sh similarity index 100% rename from templates/ansible/ubuntu/monitoring/run-playbook.sh rename to doc/wordpress_example/wordpress/piacere_monitoring/run-playbook.sh diff --git a/templates/ansible/ubuntu/monitoring/site.yaml b/doc/wordpress_example/wordpress/piacere_monitoring/site.yaml similarity index 100% rename from templates/ansible/ubuntu/monitoring/site.yaml rename to doc/wordpress_example/wordpress/piacere_monitoring/site.yaml diff --git a/templates/ansible/ubuntu/monitoring/site_requirements.yaml b/doc/wordpress_example/wordpress/piacere_monitoring/site_requirements.yaml similarity index 100% rename from templates/ansible/ubuntu/monitoring/site_requirements.yaml rename to doc/wordpress_example/wordpress/piacere_monitoring/site_requirements.yaml diff --git a/doc/wordpress_example/wordpress/piacere_monitoring/ssh_key.j2 b/doc/wordpress_example/wordpress/piacere_monitoring/ssh_key.j2 new file mode 100644 index 0000000..4d512f8 --- /dev/null +++ b/doc/wordpress_example/wordpress/piacere_monitoring/ssh_key.j2 @@ -0,0 +1 @@ +{{ instance_server_private_key_ssh_key }} diff --git a/templates/ansible/ubuntu/monitoring/vars/main.yaml b/doc/wordpress_example/wordpress/piacere_monitoring/vars/main.yaml similarity index 100% rename from templates/ansible/ubuntu/monitoring/vars/main.yaml rename to doc/wordpress_example/wordpress/piacere_monitoring/vars/main.yaml diff --git a/doc/wordpress_example/wordpress/terraform/config.yaml b/doc/wordpress_example/wordpress/terraform/config.yaml new file mode 100644 index 0000000..8a80315 --- /dev/null +++ b/doc/wordpress_example/wordpress/terraform/config.yaml @@ -0,0 +1,20 @@ + + +--- +engine: terraform +input: + - OS_USERNAME + - OS_PASSWORD + - OS_AUTH_URL + - OS_PROJECT_NAME +output: + + - instance_server_public_key_ssh_key + - instance_server_private_key_ssh_key + - instance_ip_vm1 + + - instance_server_public_key_ssh_key + - instance_server_private_key_ssh_key + - instance_ip_vm2 + +... diff --git a/doc/wordpress_example/wordpress/terraform/main.tf b/doc/wordpress_example/wordpress/terraform/main.tf new file mode 100644 index 0000000..1f0ce8d --- /dev/null +++ b/doc/wordpress_example/wordpress/terraform/main.tf @@ -0,0 +1,131 @@ + + +terraform { +required_version = ">= 0.14.0" + required_providers { + openstack = { + source = "terraform-provider-openstack/openstack" + version = "~> 1.35.0" + } + } +} + +# Configure the OpenStack Provider +provider "openstack" { + insecure = true +} + +# Retrieve data +data "openstack_networking_network_v2" "external" { + name = "external" +} + + +# Create virtual machine +resource "openstack_compute_instance_v2" "vm1" { + name = "concrete_vm1" + image_name = "ubuntu-20.04.3" + flavor_name = "small-centos" + key_pair = openstack_compute_keypair_v2.ssh_key.name + network { + port = openstack_networking_port_v2.subnet1_networking_port.id + + } +} + +# Create floating ip +resource "openstack_networking_floatingip_v2" "vm1_floating_ip" { + pool = "external" + # fixed_ip = "" +} + +# Attach floating ip to instance +resource "openstack_compute_floatingip_associate_v2" "vm1_floating_ip_association" { + floating_ip = openstack_networking_floatingip_v2.vm1_floating_ip.address + instance_id = openstack_compute_instance_v2.vm1.id +} + +# Router interface configuration + +resource "openstack_networking_router_interface_v2" "subnet1_router_interface" { + router_id = openstack_networking_router_v2.router.id + subnet_id = openstack_networking_subnet_v2.subnet1_subnet.id +} + + + +# Create virtual machine +resource "openstack_compute_instance_v2" "vm2" { + name = "concrete_vm2" + image_name = "ubuntu-20.04.3" + flavor_name = "small-centos" + key_pair = openstack_compute_keypair_v2.ssh_key.name + network { + port = openstack_networking_port_v2.subnet1_networking_port.id + + } +} + +# Create floating ip +resource "openstack_networking_floatingip_v2" "vm2_floating_ip" { + pool = "external" + # fixed_ip = "" +} + +# Attach floating ip to instance +resource "openstack_compute_floatingip_associate_v2" "vm2_floating_ip_association" { + floating_ip = openstack_networking_floatingip_v2.vm2_floating_ip.address + instance_id = openstack_compute_instance_v2.vm2.id +} + +# Router interface configuration + +resource "openstack_networking_router_interface_v2" "subnet1_router_interface" { + router_id = openstack_networking_router_v2.router.id + subnet_id = openstack_networking_subnet_v2.subnet1_subnet.id +} + + +## Network + +# Create Network +resource "openstack_networking_network_v2" "net1" { + name = "concrete_net" +} + +# Subnet +resource "openstack_networking_subnet_v2" "subnet1_subnet" { + name = "subnet1_subnet" + network_id = openstack_networking_network_v2.net1.id + cidr = "10.100.1.0/24" + dns_nameservers = ["8.8.8.8", "8.8.8.4"] +} +# Attach networking port +resource "openstack_networking_port_v2" "subnet1_networking_port" { + name = "concrete_net" + network_id = openstack_networking_network_v2.net1.id + admin_state_up = true + security_group_ids = [ + + ] + fixed_ip { + subnet_id = openstack_networking_subnet_v2.subnet1_subnet.id + } +} + + + +# Create router +resource "openstack_networking_router_v2" "router" { ## 1router, not parametric + name = "router" + external_network_id = data.openstack_networking_network_v2.external.id #External network id +} + + + +# Create ssh keys +resource "openstack_compute_keypair_v2" "ssh_key" { + name = "myuser" + public_key = "local path to ssh key" +} + diff --git a/doc/wordpress_example/wordpress/terraform/output.tf b/doc/wordpress_example/wordpress/terraform/output.tf new file mode 100644 index 0000000..9bfb60c --- /dev/null +++ b/doc/wordpress_example/wordpress/terraform/output.tf @@ -0,0 +1,28 @@ + + +output "instance_server_public_key_ssh_key" { + value = openstack_compute_keypair_v2.ssh_key.public_key +} + +output "instance_server_private_key_ssh_key" { + value = openstack_compute_keypair_v2.ssh_key.private_key +} + +output "instance_ip_vm1" { + value = openstack_compute_floatingip_associate_v2.vm1_floating_ip_association.floating_ip +} + + + +output "instance_server_public_key_ssh_key" { + value = openstack_compute_keypair_v2.ssh_key.public_key +} + +output "instance_server_private_key_ssh_key" { + value = openstack_compute_keypair_v2.ssh_key.private_key +} + +output "instance_ip_vm2" { + value = openstack_compute_floatingip_associate_v2.vm2_floating_ip_association.floating_ip +} + diff --git a/doc/wordpress_example/wordpress/wordpress/config.yaml b/doc/wordpress_example/wordpress/wordpress/config.yaml new file mode 100644 index 0000000..bc56b5d --- /dev/null +++ b/doc/wordpress_example/wordpress/wordpress/config.yaml @@ -0,0 +1,8 @@ + +--- +input: + - instance_ip_vm2 + - instance_server_private_key_ssh_key +output: [] +engine: ansible +... diff --git a/doc/wordpress_example/wordpress/wordpress/inventory.j2 b/doc/wordpress_example/wordpress/wordpress/inventory.j2 new file mode 100644 index 0000000..270ad8c --- /dev/null +++ b/doc/wordpress_example/wordpress/wordpress/inventory.j2 @@ -0,0 +1,9 @@ + + +[servers_for_wordpress] +{{ instance_ip_vm2 }} + +[servers_for_wordpress:vars] +ansible_connection=ssh +ansible_user=ubuntu +ansible_ssh_private_key_file=ssh_key diff --git a/output_files_generated/wordpress_azure/ansible/wordpress.play b/doc/wordpress_example/wordpress/wordpress/main.yml similarity index 97% rename from output_files_generated/wordpress_azure/ansible/wordpress.play rename to doc/wordpress_example/wordpress/wordpress/main.yml index f8e98e0..f3ff910 100644 --- a/output_files_generated/wordpress_azure/ansible/wordpress.play +++ b/doc/wordpress_example/wordpress/wordpress/main.yml @@ -1,3 +1,5 @@ + + --- - hosts: APP1 become: yes @@ -58,7 +60,7 @@ image: wordpress:5.8.0 state: started env: - WORDPRESS_DB_HOST: "10.10.10.10" + WORDPRESS_DB_HOST: "db_host" WORDPRESS_DB_USER: "app1user" WORDPRESS_DB_PASSWORD: "app1user" WORDPRESS_DB_NAME: "app1" diff --git a/doc/wordpress_example/wordpress/wordpress/ssh_key.j2 b/doc/wordpress_example/wordpress/wordpress/ssh_key.j2 new file mode 100644 index 0000000..4d512f8 --- /dev/null +++ b/doc/wordpress_example/wordpress/wordpress/ssh_key.j2 @@ -0,0 +1 @@ +{{ instance_server_private_key_ssh_key }} diff --git a/external-plugins.properties b/external-plugins.properties new file mode 100644 index 0000000..788b9ff --- /dev/null +++ b/external-plugins.properties @@ -0,0 +1,2 @@ +[plugins] +docker-compose = containerImages \ No newline at end of file diff --git a/icgparser/DomlParserUtilities.py b/icgparser/DomlParserUtilities.py index 8a93a95..e78b2fd 100644 --- a/icgparser/DomlParserUtilities.py +++ b/icgparser/DomlParserUtilities.py @@ -23,6 +23,10 @@ TO_BE_PARSED_RESOURCES = {} METAMODEL_SECTIONS = ["doml", "commons", "application", "infrastructure", "concrete", "optimization"] METAMODEL_DIRECTORY = "icgparser/doml" +doml_layers = { + "active_infrastructure_layer": "activeInfrastructure", +} + def extract_value_from(ecore_object_value): if isinstance(ecore_object_value, EOrderedSet): @@ -33,6 +37,13 @@ def extract_value_from(ecore_object_value): value = ecore_object_value return value +def get_infrastructure_element_from(concrete_element): + try: + return concrete_element.maps + except Exception: + logging.warning(f"No infrastructure link found for element {concrete_element.name}") + return None + def get_reference_list_if_exists(from_object, reference): reference_from_object = from_object.eGet(reference.name) @@ -41,6 +52,36 @@ def get_reference_list_if_exists(from_object, reference): else: return None +def get_references(from_object): + refs = from_object.eClass.eAllReferences() + return list(refs) + +def get_external_references(from_object): + try: + return list(from_object.eClass.eReferences) + except Exception: + logging.warning(f"Error searching for references for object {from_object.name}") + return None + + +def get_resources_from_concrete_layer(doml_model, resource_name): + concretization_layer = get_concrete_layer(doml_model) + providers = concretization_layer.providers + for provider in providers: + logging.info(f'Searching object {resource_name} in concrete layer "{concretization_layer.name}"') + try: + resources = provider.eGet(resource_name+"") + logging.info(f"Found {len(list(resources))} {resource_name}") + return resources + except Exception: + logging.warning(f"No resources found for {resource_name}") + return [] + + +def get_concrete_layer(doml_model): + concretization_layer = doml_model.eGet(doml_layers["active_infrastructure_layer"]) + return concretization_layer + def save_annotations(from_object, to_object): print(f'Saving annotation from {from_object.name}') @@ -56,7 +97,7 @@ def save_attributes(from_object, to_object, skip_component_name=False): if not to_object: to_object = {} for attribute in from_object.eClass.eAllAttributes(): - if from_object.eGet(attribute.name): + if from_object.eGet(attribute.name) is not None: key = attribute.name if skip_component_name and attribute.name == "name": key = "infra_element_name" @@ -87,7 +128,36 @@ def update_missing_parsed_resources(resource, reference, is_to_be_parsed): print(f'update_missing_parsed_resources: skipping {resource_name}') -def save_references_info(from_object, to_object): ## TODO refactoring +def save_references_info(from_object, to_object): + logging.info(f"Searching references from {from_object}") + refs = from_object.eClass.eReferences + for ref in refs: + if get_reference_list_if_exists(from_object, ref): + logging.info(f'{ref.name} is a list') + object_representation_list = [] + for reference_object in get_reference_list_if_exists(from_object, ref): + logging.info(f'Adding info for ref_link "{reference_object.name}"') + object_representation = {} + object_representation = save_annotations(reference_object, object_representation) + object_representation = save_attributes(reference_object, object_representation) + object_representation = save_references_link(reference_object, object_representation) + save_references_info(reference_object, object_representation) + object_representation_list.append(object_representation) + to_object[ref.name] = object_representation_list + logging.info(f"References added: {to_object}") + # save_references_info(reference_object, to_object) + elif from_object.eGet(ref.name): + logging.info(f'Adding object info "{ref.name}"') + reference_object = from_object.eGet(ref.name) + object_representation = {} + object_representation = save_annotations(reference_object, object_representation) + object_representation = save_attributes(reference_object, object_representation) + object_representation = save_references_link(reference_object, object_representation) + to_object[ref.name] = object_representation + return to_object + + +def save_references_link(from_object, to_object): ## TODO refactoring refs = from_object.eClass.eAllReferences() for ref in refs: if get_reference_list_if_exists(from_object, ref): @@ -100,23 +170,37 @@ def save_references_info(from_object, to_object): ## TODO refactoring update_missing_parsed_resources(reference_object, reference=ref, is_to_be_parsed=True) return to_object -def get_references(from_object): - refs = from_object.eClass.eAllReferences() - return list(refs) +def save_concrete_references_info(from_object, to_object): + if "refs" in dir(from_object): + logging.info(f"Adding concrete references for object {from_object.name}") + refs = from_object.refs + for ref_elem in refs: + logging.info(f"Found reference {ref_elem} for object {from_object.name}") + inner_component = save_attributes(ref_elem, {}) + save_references_link(ref_elem, inner_component) + to_object[ref_elem.name] = inner_component + else: + logging.info(f"No concrete references found for object {from_object.name}") + return to_object def save_inner_components(from_object, to_object): inner_components = from_object.eAllContents() for obj in inner_components: - if not isinstance(obj, EOrderedSet): # TODO espandere info - if obj.name is not None: - object_name = obj.name - else: - logging.warning(f'Object name not available, changing it using class name: {obj.eClass.name}') - object_name = obj.eClass.name - print(f'Saving information from object {object_name}') - inner_component = save_attributes(obj, {}) - save_references_info(obj, inner_component) - to_object[object_name] = inner_component + to_object = save_inner_component(obj, to_object) + return to_object + +def save_inner_component(component, to_object): + if not isinstance(component, EOrderedSet): # TODO espandere info + logging.info("Saving inner component") + if component.name is not None: + object_name = component.eClass.name + "_" + component.name + else: + logging.warning(f'Object name not available, changing it using class name: {component.eClass.name}') + object_name = component.eClass.name + print(f'Saving information from object {object_name}') + inner_component = save_attributes(component, {}) + save_references_link(component, inner_component) + to_object[object_name] = inner_component return to_object @@ -124,7 +208,7 @@ def add_infrastructure_information(infrastructure_element, to_object): print(f'Saving infrastructure information from {infrastructure_element.name}') update_missing_parsed_resources(infrastructure_element, is_to_be_parsed=False, reference=None) save_attributes(infrastructure_element, to_object, skip_component_name=True) - save_references_info(infrastructure_element, to_object) + save_references_link(infrastructure_element, to_object) save_inner_components(infrastructure_element, to_object) return to_object @@ -154,4 +238,16 @@ def load_metamodel(metamodel_directory=METAMODEL_DIRECTORY, is_multiecore=False) def load_model(model_path, rset): doml_model_resource = rset.get_resource(URI(model_path)) - return doml_model_resource.contents[0] + DOML_MODEL = doml_model_resource.contents[0] + return DOML_MODEL + + +def hasMaps(object): + try: + object.maps + return True + except: + logging.info("No maps found") + return False + + diff --git a/icgparser/IntermediateRepresentationUtility.py b/icgparser/IntermediateRepresentationUtility.py index 3d021bc..1a74e0c 100644 --- a/icgparser/IntermediateRepresentationUtility.py +++ b/icgparser/IntermediateRepresentationUtility.py @@ -1,35 +1,24 @@ import logging -from enum import Enum +from icgparser.ModelResourcesUtilities import ModelResources, ModelResourcesUtilities, get_ir_key_name -class NoValue(Enum): - def __repr__(self): - return '<%s.%s>' % (self.__class__.__name__, self.name) - -class IntermediateRepresentationResources(NoValue): - STEP_NAME = 'step_name' - STEPS = 'steps' - DATA = 'data' - LANGUAGE = "programming_language" - VIRTUAL_MACHINES = 'vms' - NETWORKS = "networks" - SECURITY_GROUPS = "computingGroup" - - -def find_objects(object_name, intermediate_representation): - logging.info(f"Searching for {object_name.value} in intermediate representation") - steps = intermediate_representation[IntermediateRepresentationResources.STEPS.value] +def find_objects(object_name: ModelResources, intermediate_representation): + logging.info(f"Searching for {object_name.name} in intermediate representation") + ir_step_name = get_ir_key_name(ModelResources.STEPS) + steps = intermediate_representation[ir_step_name] + object_ir_name = get_ir_key_name(object_name) for step in steps: - data = step[IntermediateRepresentationResources.DATA.value] - if object_name.value in data.keys(): - return data[object_name.value] + data = step[get_ir_key_name(ModelResources.DATA)] + if object_ir_name in data.keys(): + return data[object_ir_name] return [] def add_step(step, intermediate_representation, step_number): logging.info("Adding step into intermediate representation") - steps = intermediate_representation[IntermediateRepresentationResources.STEPS.value] + model_resource_class = ModelResourcesUtilities() + steps = intermediate_representation[model_resource_class.get_ir_key_name_from_model_resource(ModelResources.STEPS)] if step_number: steps.insert(step_number, step) else: @@ -38,11 +27,14 @@ def add_step(step, intermediate_representation, step_number): def force_add_resources_name(to_resource, from_resource, intermediate_representation): + logging.info(f"force_add_resources_name of resource {from_resource} into {to_resource} ") sec_groups = find_objects(from_resource, intermediate_representation) sec_groups_names = [] - for key, sg in sec_groups[0].items(): - if isinstance(sg, dict) and sg["name"]: - sec_groups_names.append(sg["name"]) - for resource in find_objects(to_resource, intermediate_representation): - resource["infra_sgs"] = sec_groups_names + logging.info(f"Found sec_groups {sec_groups} to be added into {from_resource}") + if sec_groups and len(sec_groups): + for key, sg in sec_groups[0].items(): + if isinstance(sg, dict) and sg["name"]: + sec_groups_names.append(sg["name"]) + for resource in find_objects(to_resource, intermediate_representation): + resource["infra_sgs"] = sec_groups_names return intermediate_representation diff --git a/icgparser/ModelParser.py b/icgparser/ModelParser.py index 88c5aaf..b970499 100644 --- a/icgparser/ModelParser.py +++ b/icgparser/ModelParser.py @@ -26,8 +26,12 @@ # model the input model to be translated into the ICG intermediate representation # ------------------------------------------------------------------------- import logging +import re from icgparser import DomlParserUtilities -from icgparser.DomlParserUtilities import get_reference_list_if_exists +from icgparser.DomlParserUtilities import get_reference_list_if_exists, get_resources_from_concrete_layer, \ + get_infrastructure_element_from, get_external_references, save_references_info +from icgparser.ModelResourcesUtilities import ModelResourcesUtilities +from plugin.PluginUtility import find_external_plugins_name, find_resources_names_for_plugin OUTPUT_BASE_DIR_PATH = "output_files_generated/" doml_layers = { @@ -51,9 +55,12 @@ def include_missing_objects_from_infrastructure_layer(to_step): infra_object_representation) infra_object_representation = DomlParserUtilities.add_infrastructure_information(obj["resource"], infra_object_representation) - ## TODO fix attenzione che sovrascrive + ir_key_name = to_camel_case(obj["reference"].eType.name) - to_step["data"][ir_key_name] = [infra_object_representation] + if ir_key_name in to_step["data"].keys(): + to_step["data"][ir_key_name].append(infra_object_representation) + else: + to_step["data"][ir_key_name] = [infra_object_representation] return to_step @@ -66,15 +73,31 @@ def include_infra_object_from_concrete_layer(provider, infra_object_step): f'Found list of object {len(provider_object_list)} "{provider_object_list}" in "{provider.name}"') object_list_representation = [] for object in provider_object_list: - object_representation = {} - object_representation = DomlParserUtilities.save_annotations(object, object_representation) - object_representation = DomlParserUtilities.save_attributes(object, object_representation) - object_representation = DomlParserUtilities.add_infrastructure_information(object.maps, - object_representation) + object_representation = save_object_from_concrete_layer(object) object_list_representation.append(object_representation) infra_object_step["data"][ref.name] = object_list_representation return infra_object_step +def save_object_from_concrete_layer(object): + logging.info(f"Parsing object {object.name}") + object_representation = {} + object_representation = DomlParserUtilities.save_annotations(object, object_representation) + object_representation = DomlParserUtilities.save_attributes(object, object_representation) + object_representation = DomlParserUtilities.save_references_link(object, object_representation) + object_representation = DomlParserUtilities.save_concrete_references_info(object, object_representation) + if DomlParserUtilities.hasMaps(object): + object_representation = DomlParserUtilities.add_infrastructure_information(object.maps, + object_representation) + return object_representation + + +def include_provide_info_from_concrete_layer(provider, infra_object_step): + logging.info(f'Adding provider info from concrete layer for provider {provider.name}') + provider_info = DomlParserUtilities.save_annotations(provider, {}) + provider_info["provider_name"] = provider.name + infra_object_step["data"]["provider_info"] = [provider_info] + return infra_object_step + def parse_infrastructural_objects(doml_model): infra_object_step = {"programming_language": "terraform"} ## TODO refactoring: generalize @@ -83,58 +106,110 @@ def parse_infrastructural_objects(doml_model): for provider in providers: logging.info(f'Searching objects to be generates for provider "{provider.name}"') infra_object_step["data"] = {} ## TODO refactoring, fix (maybe list?): generalize - infra_object_step["data"]["provider"] = provider.name ## TODO refactoring: generalize + ## infra_object_step["data"]["provider"] = provider.name + + infra_object_step = include_provide_info_from_concrete_layer(provider, infra_object_step) infra_object_step = include_infra_object_from_concrete_layer(provider, infra_object_step) infra_object_step = include_missing_objects_from_infrastructure_layer(infra_object_step) return infra_object_step -def parse_application_layer(doml_model, infra_object_step): - logging.info("DOML parsing: getting active configuration") +def parse_application_layer(deployment, infra_object_step): + ## TODO moved to create_intermediate_representation; to be checked + # logging.info("DOML parsing: getting active configuration") + # active_configuration = doml_model.activeConfiguration + # if not active_configuration: + # logging.info("No application layer found") + # else: + # application_object_step = {"programming_language": "ansible", "data": {}} + application_object_step = {"programming_language": "ansible", "data": {}} - active_configuration = doml_model.activeConfiguration - logging.info(f"Found active configuration '{active_configuration.name}'") - for deployment in list(active_configuration.deployments): - deployment_component_name = deployment.component.name - logging.info(f'Parsing deployment for component {deployment_component_name}') - object_representation = {} - - application_resource = deployment.eGet("component") - ## TODO refactoring -> far diventare lista nodi? nel monitoring sono più nodi - vm = deployment.eGet("node") - try: - for infra_vm in infra_object_step.get("data").get("vms"): - if infra_vm.get("infra_element_name") == vm.name: - object_representation["node"] = infra_vm - except Exception: - logging.error(f"parsing error: no vm {vm.name} found for deployment {deployment_component_name}") - - object_representation = DomlParserUtilities.save_annotations(application_resource, object_representation) - object_representation = DomlParserUtilities.save_attributes(application_resource, object_representation) - - application_object_step["data"][deployment_component_name] = object_representation - application_object_step["step_name"] = deployment_component_name + deployment_component_name = deployment.component.name + logging.info(f'Parsing deployment for component {deployment_component_name}') + object_representation = {} + + application_resource = deployment.eGet("component") + vm = deployment.eGet("node") + try: + for infra_vm in infra_object_step.get("data").get("vms"): + if infra_vm.get("infra_element_name") == vm.name: + ## TODO fake list, refactoring -> far diventare lista nodi? nel monitoring sono più nodi + object_representation["nodes"] = [infra_vm] + except Exception: + logging.error(f"parsing error: no vm {vm.name} found for deployment {deployment_component_name}") + + object_representation = DomlParserUtilities.save_annotations(application_resource, object_representation) + object_representation = DomlParserUtilities.save_attributes(application_resource, object_representation) + + application_object_step["data"][deployment_component_name] = object_representation + application_object_step["step_name"] = deployment_component_name return application_object_step +def add_external_plugin_steps(model_loaded): + logging.info("Adding external plugin resource in intermediate representation") + plugins_name = find_external_plugins_name() + plugin_steps = [] + for plugin_name in plugins_name: + object_list_representation = [] + resources_names = find_resources_names_for_plugin(plugin_name) + for res_name in resources_names: + resources = get_resources_from_concrete_layer(model_loaded, res_name) + if len(list(resources)) > 0: + plugin_object_step = {"programming_language": plugin_name, "data": {}} + for res in resources: + object_representation = save_object_from_concrete_layer(res) + logging.info(f"Searching link to infra element for concrete resource {res_name}") + infra_elem = get_infrastructure_element_from(res) + logging.info(f"Infra element found: {infra_elem}") + logging.info(f"Searching references from infra {infra_elem.name}") + object_representation = save_references_info(infra_elem, object_representation) + object_list_representation.append(object_representation) + plugin_object_step["data"][res_name] = object_list_representation + plugin_steps.append(plugin_object_step) + return plugin_steps + + def create_intermediate_representation(model_loaded): model_name = model_loaded.name output_path = OUTPUT_BASE_DIR_PATH + model_name + "/" intermediate_representation_steps = [] infra_object_step = parse_infrastructural_objects(model_loaded) - application_step = parse_application_layer(model_loaded, infra_object_step) - intermediate_representation_steps.append(infra_object_step) - intermediate_representation_steps.append(application_step) + (intermediate_representation_steps.append(infra_object_step) if infra_object_step is not None else None) + new_plugin_steps = add_external_plugin_steps(model_loaded) + intermediate_representation_steps.extend(new_plugin_steps) + active_configuration = model_loaded.activeConfiguration + # TODO Refactoring + if not active_configuration: + logging.info("No application layer found") + else: + for deployment in list(active_configuration.deployments): + application_step = parse_application_layer(deployment, infra_object_step) + (intermediate_representation_steps.append(application_step) if application_step is not None else None) intermediate_representation = { "output_path": output_path, "steps": intermediate_representation_steps } return intermediate_representation +def get_doml_version(doml_model): + logging.info("Searching for DOML version") + doml_version = doml_model.version + if not doml_version.isnumeric(): + logging.info(f"Cleaning doml version {doml_version} from letters") + doml_version = re.sub("[^0-9.]", "", doml_version) + logging.info(f"Found DOML version {doml_version}") + return doml_version def parse_model(model_path, is_multiecore_metamodel, metamodel_directory): + rset = DomlParserUtilities.load_metamodel(metamodel_directory=metamodel_directory, is_multiecore=is_multiecore_metamodel) doml_model = DomlParserUtilities.load_model(model_path, rset) + doml_version = get_doml_version(doml_model) + logging.info(f"Setup Singleton ModelResourcesUtilities with doml version {doml_version}") + ModelResourcesUtilities(doml_version) intermediate_representation = create_intermediate_representation(doml_model) return intermediate_representation + + diff --git a/icgparser/ModelResourcesUtilities.py b/icgparser/ModelResourcesUtilities.py new file mode 100644 index 0000000..d07cea9 --- /dev/null +++ b/icgparser/ModelResourcesUtilities.py @@ -0,0 +1,84 @@ +import logging +from enum import Enum + +class ModelResources(Enum): + STEP_NAME = 1, + STEPS = 2, + DATA = 3, + LANGUAGE = 4, + VIRTUAL_MACHINES = 5, + NETWORKS = 6, + SECURITY_GROUPS = 7, + AUTOSCALING_GROUPS = 8, + +def from_model_resources_to_ir_names_version1(model_resource: ModelResources): + switcher = { + 1: "step_name", + 2: "steps", + 3: "data", + 4: "programming_language", + 5: "vms", + 6: "networks", + 7: "computingGroup", + 8: "group", + } + if model_resource.value[0]: + resource_number = model_resource.value[0] + else: + resource_number = model_resource.value + return switcher.get(resource_number, None) + + +def from_model_resources_to_ir_names_version2(model_resource: ModelResources): + switcher = { + 1: "step_name", + 2: "steps", + 3: "data", + 4: "programming_language", + 5: "vms", + 6: "networks", + 7: "securityGroup", + 8: "group", + } + if model_resource.value[0]: + resource_number = model_resource.value[0] + else: + resource_number = model_resource.value + return switcher.get(resource_number, None) + +def singleton(class_): + instances = {} + def getinstance(*args, **kwargs): + if class_ not in instances: + instances[class_] = class_(*args, **kwargs) + return instances[class_] + return getinstance + +@singleton +class ModelResourcesUtilities: + doml_version = "2" + + def __init__(self, doml_version): + logging.info(f"ModelResourcesUtilities setting doml version to {doml_version}") + self.doml_version = doml_version + + def convert_doml_version_into_integer(self): + return float(self.doml_version) + + def get_ir_key_name_from_model_resource(self, model_resource: ModelResources): + #doml_version_converted = self.convert_doml_version_into_integer() + logging.info(f"Found doml version {self.doml_version}") + switcher = { + "1": from_model_resources_to_ir_names_version1(model_resource), + "2": from_model_resources_to_ir_names_version2(model_resource), + "2.2": from_model_resources_to_ir_names_version2(model_resource), + "2.2.2": from_model_resources_to_ir_names_version2(model_resource), + } + return switcher.get(self.doml_version, from_model_resources_to_ir_names_version1(model_resource)) + + def set_doml_version(self, doml_version): + self.doml_version = doml_version + +def get_ir_key_name(model_resource: ModelResources): + model_resource_utilities = ModelResourcesUtilities() + return model_resource_utilities.get_ir_key_name_from_model_resource(model_resource) diff --git a/icgparser/PiacereInternalToolsIntegrator.py b/icgparser/PiacereInternalToolsIntegrator.py index ab65804..3064ea2 100644 --- a/icgparser/PiacereInternalToolsIntegrator.py +++ b/icgparser/PiacereInternalToolsIntegrator.py @@ -1,40 +1,83 @@ import logging +import os from distutils.dir_util import copy_tree - from icgparser import IntermediateRepresentationUtility -from icgparser.IntermediateRepresentationUtility import IntermediateRepresentationResources +from icgparser.ModelResourcesUtilities import ModelResources -def extract_info_for_monitoring_agents(intermediate_representation): - logging.info("Adding info for monitoring step") - monitoring_object_step = {"programming_language": "ansible", - "step_name": "piacere_monitoring", - "data": {"piacere_monitoring": {"name": "piacere_monitoring"}}} - vms = IntermediateRepresentationUtility.find_objects(IntermediateRepresentationResources.VIRTUAL_MACHINES, +def create_piacere_agents_ansible_step(piacere_component_name, intermediate_representation): + logging.info(f"Adding info for {piacere_component_name} step") + step_name = piacere_component_name + "_monitoring" + vms = IntermediateRepresentationUtility.find_objects(ModelResources.VIRTUAL_MACHINES, intermediate_representation) - # TODO restore these 2 commented lines: monitoring could be installed on multiple nodes! - # monitoring_object_step["data"]["monitoring"]["nodes"] = [] - # monitoring_object_step["data"]["monitoring"]["nodes"] += vms - # TODO remove this line: monitoring could be installed on multiple nodes! - if vms: - monitoring_object_step["data"]["piacere_monitoring"]["node"] = vms[0] - logging.info(f"Monitoring step: {monitoring_object_step}") + autoscaling_group_vms = IntermediateRepresentationUtility.find_objects(ModelResources.AUTOSCALING_GROUPS, + intermediate_representation) + intermediate_repr_step = None + if vms or autoscaling_group_vms: + intermediate_repr_step = {"programming_language": "ansible", + "step_name": step_name, + "data": {step_name: {"name": step_name}}} + intermediate_repr_step["data"][step_name]["nodes"] = [] + if vms: + intermediate_repr_step["data"][step_name]["nodes"] += vms + if autoscaling_group_vms: + for ag in autoscaling_group_vms: + vms = [] + vm = next(v for k, v in ag.items() if k.lower().startswith('virtualmachine')) + vms.append(vm) + intermediate_repr_step["data"][step_name]["nodes"] += vms + + logging.info(f"{step_name} step: {intermediate_repr_step}") + return intermediate_repr_step + + +def extract_info_for_monitoring_agents(intermediate_representation): + logging.info("Adding info for performance step") + monitoring_object_step = create_piacere_agents_ansible_step("performance", intermediate_representation) return monitoring_object_step +def extract_infor_for_security_agents(intermediate_representation): + logging.info("Adding info for security step") + security_object_step = create_piacere_agents_ansible_step("security", intermediate_representation) + return security_object_step + + def add_internal_tool_information(intermediate_representation): + performance_monitoring_directory_path = "templates/ansible/cross-platform/performance_monitoring" + security_monitoring_directory_path = "templates/ansible/cross-platform/security_monitoring" + if not os.listdir(performance_monitoring_directory_path) or not os.listdir(security_monitoring_directory_path): + logging.warning(f"add_internal_tool_information: {performance_monitoring_directory_path} " + f"or {security_monitoring_directory_path} is empty.") + return intermediate_representation monitoring_step = extract_info_for_monitoring_agents(intermediate_representation) - intermediate_representation = IntermediateRepresentationUtility.add_step(monitoring_step, - intermediate_representation, - 1) - return intermediate_representation + security_step = extract_infor_for_security_agents(intermediate_representation) + intermediate_representation_with_monitoring = IntermediateRepresentationUtility.add_step(monitoring_step, + intermediate_representation, + 1) + intermediate_representation_with_security_monitoring = IntermediateRepresentationUtility \ + .add_step(security_step, intermediate_representation_with_monitoring, 2) + return intermediate_representation_with_security_monitoring def add_files_for_monitoring_agents(template_generated_folder_path): - monitoring_folder_path = template_generated_folder_path + "/piacere_monitoring" + monitoring_folder_path = template_generated_folder_path + "performance_monitoring" logging.info(f"Adding monitoring agents folder in {monitoring_folder_path}") - copy_tree("templates/ansible/ubuntu/monitoring", monitoring_folder_path) + monitoring_folder = "templates/ansible/cross-platform/performance_monitoring" + if not monitoring_folder: + os.makedirs(monitoring_folder) + copy_tree("templates/ansible/cross-platform/performance_monitoring", monitoring_folder_path) + + +def add_files_for_security_agents(template_generated_folder_path): + security_folder_path = template_generated_folder_path + "security_monitoring" + logging.info(f"Adding monitoring agents folder in {security_folder_path}") + security_folder = "templates/ansible/cross-platform/security_monitoring" + if not security_folder: + os.makedirs(security_folder) + copy_tree("templates/ansible/cross-platform/security_monitoring", security_folder_path) def add_files_for_piacere_internal_tools(template_generated_folder_path): add_files_for_monitoring_agents(template_generated_folder_path) + add_files_for_security_agents(template_generated_folder_path) diff --git a/icgparser/doml/v1/nginx-openstack_v1.domlx b/icgparser/doml/v1/nginx-openstack_v1.domlx index 4195ffc..e760e78 100644 --- a/icgparser/doml/v1/nginx-openstack_v1.domlx +++ b/icgparser/doml/v1/nginx-openstack_v1.domlx @@ -1,52 +1,51 @@ <?xml version="1.0" encoding="ASCII"?> -<commons:DOMLModel xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:app="http://www.piacere-project.eu/doml/application" xmlns:commons="http://www.piacere-project.eu/doml/commons" xmlns:infra="http://www.piacere-project.eu/doml/infrastructure" xmlns:optimization="http://www.piacere-project.eu/doml/optimization" name="nginx_openstack" activeConfiguration="//@configurations.0" activeInfrastructure="//@concretizations.0"> - <application name="app"> - <components xsi:type="app:SoftwareComponent" name="nginx"> - <annotations xsi:type="commons:SProperty" key="source_code" value="/usr/share/nginx/html/index.html"/> - </components> - </application> +<commons:DOMLModel xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:commons="http://www.piacere-project.eu/doml/commons" xmlns:infra="http://www.piacere-project.eu/doml/infrastructure" name="nio3_test_exec_env" activeInfrastructure="//@concretizations.0"> <infrastructure name="infra"> - <nodes xsi:type="infra:AutoScalingGroup" name="ag" deploymentNetwork="//@infrastructure/@networks.0"> - <machineDefinition name="vm1" os="Ubuntu-Focal-20.04-Daily-2022-04-19" credentials="//@infrastructure/@credentials.0" group="//@infrastructure/@groups.0"> - <ifaces name="i1" endPoint="16.0.0.1" belongsTo="//@infrastructure/@networks.0" associated="//@infrastructure/@groups.0"/> - </machineDefinition> + <nodes xsi:type="infra:VirtualMachine" name="vm1" os="centos7_64Guest" memory_mb="1024.0" cpu_count="2" credentials="//@infrastructure/@credentials.0" generatedFrom="//@infrastructure/@generators.0"> + <ifaces name="i1" endPoint="10.83.18.92" belongsTo="//@infrastructure/@networks.0"/> </nodes> - <networks name="net1" protocol="tcp/ip" addressRange="16.0.0.0/24" connectedIfaces="//@infrastructure/@nodes.0/@machineDefinition/@ifaces.0"/> - <credentials xsi:type="infra:KeyPair" name="user1" user="user1"/> - <groups xsi:type="infra:SecurityGroup" name="sg" groupedNodes="//@infrastructure/@nodes.0/@machineDefinition" ifaces="//@infrastructure/@nodes.0/@machineDefinition/@ifaces.0"> - <rules name="icmp" protocol="icmp" fromPort="-1" toPort="-1"> - <cidr>0.0.0.0/0</cidr> - </rules> - <rules name="http" kind="INGRESS" protocol="tcp" fromPort="80" toPort="80"> - <cidr>0.0.0.0/0</cidr> - </rules> - <rules name="https" kind="INGRESS" protocol="tcp" fromPort="443" toPort="443"> - <cidr>0.0.0.0/0</cidr> - </rules> - <rules name="ssh" kind="INGRESS" protocol="tcp" fromPort="22" toPort="22"> - <cidr>0.0.0.0/0</cidr> - </rules> - </groups> + <nodes xsi:type="infra:VirtualMachine" name="vm2" os="centos7_64Guest" memory_mb="1024.0" cpu_count="2" credentials="//@infrastructure/@credentials.0" generatedFrom="//@infrastructure/@generators.0"> + <ifaces name="i1" endPoint="10.83.18.88" belongsTo="//@infrastructure/@networks.0"/> + </nodes> + <generators xsi:type="infra:VMImage" name="img" generatedVMs="//@infrastructure/@nodes.0 //@infrastructure/@nodes.1"/> + <storages name="disk0" label="disk0" size_gb="100"/> + <credentials xsi:type="commons:KeyPair" name="ssh_key" algorithm="RSA" bits="4096"/> + <networks name="net1" protocol="tcp/ip" addressRange="/24" connectedIfaces="//@infrastructure/@nodes.0/@ifaces.0 //@infrastructure/@nodes.1/@ifaces.0"> + <gateways name="g1" address="10.83.18.65"/> + </networks> </infrastructure> <concretizations name="con_infra"> - <providers name="openstack"> - <vms name="concrete_vm" maps="//@infrastructure/@nodes.0/@machineDefinition"> - <annotations xsi:type="commons:SProperty" key="vm_name" value="nginx-host"/> - <annotations xsi:type="commons:SProperty" key="vm_flavor" value="small"/> + <providers name="vsphere"> + <annotations xsi:type="commons:SProperty" key="username" value="vc_username"/> + <annotations xsi:type="commons:SProperty" key="password" value="vc_password"/> + <annotations xsi:type="commons:SProperty" key="vsphere_server" value="psvc10000002.cd.sigov.si"/> + <annotations xsi:type="commons:BProperty" key="allow_unverified_ssl" value="true"/> + <resources name="dc" preexisting="true" type="vsphere_datacenter" gname="PIACDC"/> + <resources name="cl" preexisting="true" refs="//@concretizations.0/@providers.0/@resources.0" type="vsphere_compute_cluster" gname="PIACC"/> + <resources name="pool" preexisting="true" refs="//@concretizations.0/@providers.0/@resources.0" type="vsphere_resource_pool" gname="Piacere"/> + <vms name="con_vm1" refs="//@concretizations.0/@providers.0/@resources.2 //@concretizations.0/@providers.0/@storages.0 //@concretizations.0/@providers.0/@vmImages.0" maps="//@infrastructure/@nodes.0"> + <annotations xsi:type="commons:SProperty" key="host_name" value="simpa-test00-piacere"/> + <annotations xsi:type="commons:SProperty" key="domain" value="tri.lan"/> + <annotations xsi:type="commons:SProperty" key="guest_id" value="centos7_64Guest"/> + <annotations xsi:type="commons:SProperty" key="disk" value="disk0"/> + <annotations xsi:type="commons:IProperty" key="disk_size" value="100"/> + </vms> + <vms name="con_vm2" refs="//@concretizations.0/@providers.0/@resources.2 //@concretizations.0/@providers.0/@storages.0 //@concretizations.0/@providers.0/@vmImages.0" maps="//@infrastructure/@nodes.1"> + <annotations xsi:type="commons:SProperty" key="host_name" value="simpa-test00-piacere"/> + <annotations xsi:type="commons:SProperty" key="domain" value="tri.lan"/> + <annotations xsi:type="commons:SProperty" key="guest_id" value="centos7_64Guest"/> + <annotations xsi:type="commons:SProperty" key="disk" value="disk1"/> + <annotations xsi:type="commons:IProperty" key="disk_size" value="100"/> </vms> - <networks name="concrete_net" maps="//@infrastructure/@networks.0"> - <annotations xsi:type="commons:SProperty" key="name" value="ostack2"/> + <vmImages name="template" preexisting="true" refs="//@concretizations.0/@providers.0/@resources.0" maps="//@infrastructure/@generators.0"> + <annotations xsi:type="commons:SProperty" key="vsphere_virtual_machine_name" value="c7tmp"/> + </vmImages> + <networks name="network" preexisting="true" refs="//@concretizations.0/@providers.0/@resources.0" maps="//@infrastructure/@networks.0"> + <annotations xsi:type="commons:SProperty" key="vsphere_network_name" value="Nested-ESXi"/> </networks> + <storages name="datastore" preexisting="true" refs="//@concretizations.0/@providers.0/@resources.0" maps="//@infrastructure/@storages.0"> + <annotations xsi:type="commons:SProperty" key="vsphere_datastore_name" value="NFSShare01"/> + </storages> </providers> </concretizations> - <optimization name="opt"> - <objectives xsi:type="optimization:MeasurableObjective" kind="min" property="cost"/> - <nonfunctionalRequirements xsi:type="commons:RangedRequirement" name="req1" description="Cost <= 200" property="cost" max="200.0"/> - <nonfunctionalRequirements xsi:type="commons:EnumeratedRequirement" name="req2" description="Provider" property="provider"> - <values>OPEN</values> - </nonfunctionalRequirements> - </optimization> - <configurations name="config"> - <deployments component="//@application/@components.0" node="//@infrastructure/@nodes.0/@machineDefinition"/> - </configurations> </commons:DOMLModel> diff --git a/icgparser/doml/v1/posidonia_example.domlx b/icgparser/doml/v1/posidonia_example.domlx new file mode 100644 index 0000000..17587d4 --- /dev/null +++ b/icgparser/doml/v1/posidonia_example.domlx @@ -0,0 +1,133 @@ +<?xml version="1.0" encoding="ASCII"?> +<commons:DOMLModel xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:commons="http://www.piacere-project.eu/doml/commons" xmlns:infra="http://www.piacere-project.eu/doml/infrastructure" xmlns:optimization="http://www.piacere-project.eu/doml/optimization" name="posidonia" activeInfrastructure="//@concretizations.0"> + <annotations xsi:type="commons:SProperty" key="entorno" value="pre"/> + <annotations xsi:type="commons:SProperty" key="proyecto" value="baleares"/> + <infrastructure name="abstractInfra"> + <nodes xsi:type="infra:VirtualMachine" name="OracleDB" os="ami-02a6bfdcf8224bd77" storage="20" credentials="//@infrastructure/@credentials.3"> + <ifaces name="db1" belongsTo="//@infrastructure/@networks.0/@subnets.0"/> + <ifaces name="db2" belongsTo="//@infrastructure/@networks.0/@subnets.1"/> + <ifaces name="db3" belongsTo="//@infrastructure/@networks.0/@subnets.2"/> + </nodes> + <nodes xsi:type="infra:AutoScalingGroup" name="gestaut_asg" deploymentNetwork="//@infrastructure/@networks.0"> + <machineDefinition name="gestaut_vm" os="ami-02a6bfdcf8224bd77" credentials="//@infrastructure/@credentials.0" group="//@infrastructure/@groups.0"> + <ifaces name="db1" belongsTo="//@infrastructure/@networks.0/@subnets.0"/> + <ifaces name="db2" belongsTo="//@infrastructure/@networks.0/@subnets.1"/> + <ifaces name="db3" belongsTo="//@infrastructure/@networks.0/@subnets.2"/> + </machineDefinition> + </nodes> + <nodes xsi:type="infra:AutoScalingGroup" name="elasticsearch_asg" deploymentNetwork="//@infrastructure/@networks.0"> + <machineDefinition name="elasticsearch_vm" os="ami-02a6bfdcf8224bd77" credentials="//@infrastructure/@credentials.1" group="//@infrastructure/@groups.0"> + <ifaces name="db1" belongsTo="//@infrastructure/@networks.0/@subnets.0"/> + <ifaces name="db2" belongsTo="//@infrastructure/@networks.0/@subnets.1"/> + <ifaces name="db3" belongsTo="//@infrastructure/@networks.0/@subnets.2"/> + </machineDefinition> + </nodes> + <nodes xsi:type="infra:AutoScalingGroup" name="edi_asg" deploymentNetwork="//@infrastructure/@networks.0"> + <machineDefinition name="edi_vm" os="ami-02a6bfdcf8224bd77" credentials="//@infrastructure/@credentials.2" group="//@infrastructure/@groups.0"> + <ifaces name="db1" belongsTo="//@infrastructure/@networks.0/@subnets.0"/> + <ifaces name="db2" belongsTo="//@infrastructure/@networks.0/@subnets.1"/> + <ifaces name="db3" belongsTo="//@infrastructure/@networks.0/@subnets.2"/> + </machineDefinition> + </nodes> + <networks name="vpc" protocol="tcp/ip" addressRange="10.100.0.0/16"> + <igws name="internet"/> + <subnets name="subnet1" protocol="tcp/ip" addressRange="10.100.1.0/24" connectedIfaces="//@infrastructure/@nodes.0/@ifaces.0 //@infrastructure/@nodes.1/@machineDefinition/@ifaces.0 //@infrastructure/@nodes.2/@machineDefinition/@ifaces.0 //@infrastructure/@nodes.3/@machineDefinition/@ifaces.0"/> + <subnets name="subnet2" protocol="tcp/ip" addressRange="10.100.2.0/24" connectedIfaces="//@infrastructure/@nodes.0/@ifaces.1 //@infrastructure/@nodes.1/@machineDefinition/@ifaces.1 //@infrastructure/@nodes.2/@machineDefinition/@ifaces.1 //@infrastructure/@nodes.3/@machineDefinition/@ifaces.1"/> + <subnets name="subnet3" protocol="tcp/ip" addressRange="10.100.3.0/24" connectedIfaces="//@infrastructure/@nodes.0/@ifaces.2 //@infrastructure/@nodes.1/@machineDefinition/@ifaces.2 //@infrastructure/@nodes.2/@machineDefinition/@ifaces.2 //@infrastructure/@nodes.3/@machineDefinition/@ifaces.2"/> + </networks> + <credentials xsi:type="infra:KeyPair" name="GestautKeyName" keyfile="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD3F6tyPEFEzV0LX3X8BsXdMsQz1x2cEikKDEY0aIj41qgxMCP/iteneqXSIFZBp5vizPvaoIR3Um9xK7PGoW8giupGn+EPuxIA4cDM4vzOqOkiMPhz5XK0whEjkVzTo4+S0puvDZuwIsdiW9mxhJc7tgBNL0cYlWSYVkz4G/fslNfRPW5mYAM49f4fhtxPb5ok4Q2Lg9dPKVHO/Bgeu5woMc7RY0p1ej6D4CKFE6lymSDJpW0YHX/wqE9+cfEauh7xZcG0q9t2ta6F6fmX0agvpFyZo8aFbXeUBr7osSCJNgvavWbM/06niWrOvYX2xwWdhXmXSrbX8ZbabVohBK41 email@example.com" algorithm="RSA" bits="4096"/> + <credentials xsi:type="infra:KeyPair" name="ESKeyName" keyfile="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD3F6tyPEFEzV0LX3X8BsXdMsQz1x2cEikKDEY0aIj41qgxMCP/iteneqXSIFZBp5vizPvaoIR3Um9xK7PGoW8giupGn+EPuxIA4cDM4vzOqOkiMPhz5XK0whEjkVzTo4+S0puvDZuwIsdiW9mxhJc7tgBNL0cYlWSYVkz4G/fslNfRPW5mYAM49f4fhtxPb5ok4Q2Lg9dPKVHO/Bgeu5woMc7RY0p1ej6D4CKFE6lymSDJpW0YHX/wqE9+cfEauh7xZcG0q9t2ta6F6fmX0agvpFyZo8aFbXeUBr7osSCJNgvavWbM/06niWrOvYX2xwWdhXmXSrbX8ZbabVohBK41 email@example.com" algorithm="RSA" bits="4096"/> + <credentials xsi:type="infra:KeyPair" name="EdiKeyName" keyfile="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD3F6tyPEFEzV0LX3X8BsXdMsQz1x2cEikKDEY0aIj41qgxMCP/iteneqXSIFZBp5vizPvaoIR3Um9xK7PGoW8giupGn+EPuxIA4cDM4vzOqOkiMPhz5XK0whEjkVzTo4+S0puvDZuwIsdiW9mxhJc7tgBNL0cYlWSYVkz4G/fslNfRPW5mYAM49f4fhtxPb5ok4Q2Lg9dPKVHO/Bgeu5woMc7RY0p1ej6D4CKFE6lymSDJpW0YHX/wqE9+cfEauh7xZcG0q9t2ta6F6fmX0agvpFyZo8aFbXeUBr7osSCJNgvavWbM/06niWrOvYX2xwWdhXmXSrbX8ZbabVohBK41 email@example.com" algorithm="RSA" bits="4096"/> + <credentials xsi:type="infra:UserPass" name="dbCredentials" username="balearesadm" password="balearesadm"/> + <groups xsi:type="infra:SecurityGroup" name="sg" groupedNodes="//@infrastructure/@nodes.1/@machineDefinition //@infrastructure/@nodes.2/@machineDefinition //@infrastructure/@nodes.3/@machineDefinition"> + <rules name="salida" protocol="-1" fromPort="0" toPort="0"> + <cidr>0.0.0.0/0</cidr> + </rules> + <rules name="lb" kind="INGRESS" protocol="tcp" fromPort="80" toPort="80"> + <cidr>10.100.1.0/24</cidr> + <cidr>10.100.2.0/24</cidr> + <cidr>10.100.3.0/24</cidr> + </rules> + <rules name="es" kind="INGRESS" protocol="tcp" fromPort="9200" toPort="9200"> + <cidr>10.100.1.0/24</cidr> + <cidr>10.100.2.0/24</cidr> + <cidr>10.100.3.0/24</cidr> + </rules> + <rules name="monitor" kind="INGRESS" protocol="tcp" fromPort="6556" toPort="6556"> + <cidr>54.217.119.81/32</cidr> + </rules> + <rules name="ftp" kind="INGRESS" protocol="tcp" fromPort="22" toPort="22"> + <cidr>213.96.27.139/32</cidr> + <cidr>37.187.173.88/32</cidr> + <cidr>51.89.40.59/32</cidr> + <cidr>195.53.242.200/32</cidr> + </rules> + </groups> + <groups xsi:type="infra:SecurityGroup" name="dbsg"> + <rules name="salida" protocol="-1" fromPort="0" toPort="0"> + <cidr>0.0.0.0/0</cidr> + </rules> + <rules name="ora" kind="INGRESS" protocol="tcp" fromPort="1521" toPort="1521"> + <cidr>10.100.1.0/24</cidr> + <cidr>10.100.2.0/24</cidr> + <cidr>10.100.3.0/24</cidr> + <cidr>84.124.78.66/32</cidr> + </rules> + </groups> + <groups xsi:type="infra:SecurityGroup" name="elbsg"> + <rules name="salida" protocol="-1" fromPort="0" toPort="0"> + <cidr>0.0.0.0/0</cidr> + </rules> + <rules name="http" kind="INGRESS" protocol="tcp" fromPort="80" toPort="80"> + <cidr>0.0.0.0/0</cidr> + <cidr>::/0</cidr> + </rules> + <rules name="https" kind="INGRESS" protocol="tcp" fromPort="443" toPort="443"> + <cidr>0.0.0.0/0</cidr> + <cidr>::/0</cidr> + </rules> + <rules name="es" kind="INGRESS" protocol="tcp" fromPort="9200" toPort="9200"> + <cidr>10.100.1.0/24</cidr> + <cidr>10.100.2.0/24</cidr> + <cidr>10.100.3.0/24</cidr> + </rules> + </groups> + <groups xsi:type="infra:SecurityGroup" name="checkmk"> + <rules name="salida" protocol="-1" fromPort="0" toPort="0"> + <cidr>0.0.0.0/0</cidr> + </rules> + <rules name="http" kind="INGRESS" protocol="tcp" fromPort="80" toPort="80"> + <cidr>84.124.78.66/32</cidr> + </rules> + <rules name="https" kind="INGRESS" protocol="tcp" fromPort="443" toPort="443"> + <cidr>84.124.78.66/32</cidr> + </rules> + <rules name="ftp" kind="INGRESS" protocol="tcp" fromPort="22" toPort="22"> + <cidr>84.124.78.66/32</cidr> + </rules> + </groups> + </infrastructure> + <concretizations name="dev"> + <providers name="aws"> + <vms name="concrete_oracle_db" maps="//@infrastructure/@nodes.0"> + <annotations xsi:type="commons:SProperty" key="instance_type" value="t2.micro"/> + </vms> + <vms name="concrete_gestaut_vm" maps="//@infrastructure/@nodes.1/@machineDefinition"> + <annotations xsi:type="commons:SProperty" key="instance_type" value="t2.micro"/> + </vms> + <vms name="elasticsearch_vm" maps="//@infrastructure/@nodes.2/@machineDefinition"> + <annotations xsi:type="commons:SProperty" key="instance_type" value="t2.micro"/> + </vms> + <vms name="edi_vm" maps="//@infrastructure/@nodes.3/@machineDefinition"/> + <networks name="concrete_vpc" maps="//@infrastructure/@networks.0"> + <annotations xsi:type="commons:SProperty" key="instance_type" value="t2.micro"/> + </networks> + </providers> + </concretizations> + <optimization name="opt"> + <objectives xsi:type="optimization:MeasurableObjective" kind="min" property="cost"/> + <objectives xsi:type="optimization:MeasurableObjective" kind="max" property="availability"/> + <nonfunctionalRequirements xsi:type="commons:RangedRequirement" name="req1" description="Cost <= 70.0" property="cost" max="70.0"/> + <nonfunctionalRequirements xsi:type="commons:RangedRequirement" name="req2" description="Availability >= 66.5%" property="availability" min="66.5"/> + </optimization> +</commons:DOMLModel> diff --git a/icgparser/doml/v2/doml.ecore b/icgparser/doml/v2/doml.ecore new file mode 100644 index 0000000..cbff3c6 --- /dev/null +++ b/icgparser/doml/v2/doml.ecore @@ -0,0 +1,423 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="doml" nsURI="http://www.piacere-project.eu/doml" nsPrefix="doml"> + <eAnnotations source="emf.gen"> + <details key="basePackage" value="eu.piacere.doml"/> + <details key="fileExtensions" value="domlx"/> + <details key="complianceLevel" value="JDK80"/> + </eAnnotations> + <eSubpackages name="commons" nsURI="http://www.piacere-project.eu/doml/commons" + nsPrefix="commons"> + <eClassifiers xsi:type="ecore:EClass" name="DOMLModel" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="version" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString" + defaultValueLiteral="2.2"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="application" eType="#//application/ApplicationLayer" + containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="infrastructure" eType="#//infrastructure/InfrastructureLayer" + containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="concretizations" upperBound="-1" + eType="#//concrete/ConcreteInfrastructure" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="optimization" eType="#//optimization/OptimizationLayer" + containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="configurations" upperBound="-1" + eType="#//commons/Configuration" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="activeConfiguration" + eType="#//commons/Configuration"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="activeInfrastructure" + eType="#//concrete/ConcreteInfrastructure"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="functionalRequirements" + upperBound="-1" eType="#//commons/Requirement" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Property" abstract="true"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="key" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="IProperty" eSuperTypes="#//commons/Property"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="value" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="SProperty" eSuperTypes="#//commons/Property"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="value" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="FProperty" eSuperTypes="#//commons/Property"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="value" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="BProperty" eSuperTypes="#//commons/Property"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="value" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBooleanObject"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ExtensionElement" abstract="true"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="metaclassName" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="DOMLElement" abstract="true"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="description" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="annotations" upperBound="-1" + eType="#//commons/Property" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="contributesTo" upperBound="-1" + eType="#//commons/Requirement" eOpposite="#//commons/Requirement/predicatesOn"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Configuration" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="deployments" upperBound="-1" + eType="#//commons/Deployment" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="DeployableElement" abstract="true"/> + <eClassifiers xsi:type="ecore:EClass" name="Deployment"> + <eStructuralFeatures xsi:type="ecore:EReference" name="component" lowerBound="1" + eType="#//commons/DeployableElement"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="node" lowerBound="1" + eType="#//infrastructure/InfrastructureElement"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Requirement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="description" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="property" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="predicatesOn" upperBound="-1" + eType="#//commons/DOMLElement" eOpposite="#//commons/DOMLElement/contributesTo"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="RangedRequirement" eSuperTypes="#//commons/Requirement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="min" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="max" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="EnumeratedRequirement" eSuperTypes="#//commons/Requirement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="values" upperBound="-1" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="DeploymentRequirement" abstract="true" + eSuperTypes="#//commons/Requirement"/> + <eClassifiers xsi:type="ecore:EClass" name="DeploymentToNodeTypeRequirement" eSuperTypes="#//commons/DeploymentRequirement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="validTypes" upperBound="-1" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="DeploymentToNodeWithPropertyRequirement" + eSuperTypes="#//commons/DeploymentRequirement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="min" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="max" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="values" upperBound="-1" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="DeploymentToSpecificNodeRequirement" + eSuperTypes="#//commons/DeploymentRequirement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="validElements" upperBound="-1" + eType="#//infrastructure/InfrastructureElement"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Credentials" abstract="true" eSuperTypes="#//commons/DOMLElement"/> + <eClassifiers xsi:type="ecore:EClass" name="KeyPair" eSuperTypes="#//commons/Credentials"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="user" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="keyfile" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="algorithm" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="bits" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="UserPass" eSuperTypes="#//commons/Credentials"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="username" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="password" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Source" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="entry" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="backend" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + </eSubpackages> + <eSubpackages name="application" nsURI="http://www.piacere-project.eu/doml/application" + nsPrefix="app"> + <eClassifiers xsi:type="ecore:EClass" name="ApplicationLayer" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="components" upperBound="-1" + eType="#//application/ApplicationComponent" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ApplicationComponent" abstract="true" + eSuperTypes="#//commons/DOMLElement #//commons/DeployableElement"/> + <eClassifiers xsi:type="ecore:EClass" name="SoftwareComponent" eSuperTypes="#//application/ApplicationComponent"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="isPersistent" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBooleanObject" + defaultValueLiteral="false"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="licenseCost" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="exposedInterfaces" upperBound="-1" + eType="#//application/SoftwareInterface" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="consumedInterfaces" upperBound="-1" + eType="#//application/SoftwareInterface"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="src" eType="#//commons/Source" + containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="DBMS" eSuperTypes="#//application/SoftwareComponent"/> + <eClassifiers xsi:type="ecore:EClass" name="SaaS" eSuperTypes="#//application/ApplicationComponent"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="licenseCost" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="exposedInterfaces" upperBound="-1" + eType="#//application/SoftwareInterface" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="SaaSDBMS" eSuperTypes="#//application/SaaS"/> + <eClassifiers xsi:type="ecore:EClass" name="SoftwareInterface" eSuperTypes="#//application/ApplicationComponent"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="endPoint" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ExtApplicationComponent" eSuperTypes="#//application/ApplicationComponent #//commons/ExtensionElement"/> + </eSubpackages> + <eSubpackages name="infrastructure" nsURI="http://www.piacere-project.eu/doml/infrastructure" + nsPrefix="infra"> + <eClassifiers xsi:type="ecore:EClass" name="InfrastructureLayer" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="nodes" upperBound="-1" + eType="#//infrastructure/ComputingNode" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="generators" upperBound="-1" + eType="#//infrastructure/ComputingNodeGenerator" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="storages" upperBound="-1" + eType="#//infrastructure/Storage" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="faas" upperBound="-1" + eType="#//infrastructure/FunctionAsAService" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="credentials" upperBound="-1" + eType="#//commons/Credentials" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="groups" upperBound="-1" + eType="#//infrastructure/ComputingGroup" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="securityGroups" upperBound="-1" + eType="#//infrastructure/SecurityGroup" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="networks" upperBound="-1" + eType="#//infrastructure/Network" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="rules" upperBound="-1" + eType="#//infrastructure/MonitoringRule" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="MonitoringRule" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="condition" lowerBound="1" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="strategy" lowerBound="1" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="strategyConfigurationString" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ComputingGroup" abstract="true" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="groupedNodes" upperBound="-1" + eType="#//infrastructure/ComputingNode" eOpposite="#//infrastructure/ComputingNode/group"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="AutoScalingGroup" eSuperTypes="#//infrastructure/ComputingGroup"> + <eStructuralFeatures xsi:type="ecore:EReference" name="machineDefinition" lowerBound="1" + eType="#//infrastructure/VirtualMachine" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="min" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject" + defaultValueLiteral="1"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="max" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject" + defaultValueLiteral="1"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="securityGroup" eType="#//infrastructure/SecurityGroup" + containment="true"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="loadBalancer" eType="#//infrastructure/LoadBalancerKind" + defaultValueLiteral="DEFAULT"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EEnum" name="LoadBalancerKind"> + <eLiterals name="DEFAULT"/> + <eLiterals name="INTERNAL" value="1"/> + <eLiterals name="EXTERNAL" value="2"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Rule" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="kind" eType="#//infrastructure/RuleKind"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="protocol" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="fromPort" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="toPort" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="cidr" upperBound="-1" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EEnum" name="RuleKind"> + <eLiterals name="EGRESS"/> + <eLiterals name="INGRESS" value="1"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="SecurityGroup" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="rules" upperBound="-1" + eType="#//infrastructure/Rule" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="ifaces" upperBound="-1" + eType="#//infrastructure/NetworkInterface" eOpposite="#//infrastructure/NetworkInterface/associated"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="AvailabilityGroup" eSuperTypes="#//infrastructure/ComputingGroup"/> + <eClassifiers xsi:type="ecore:EClass" name="InfrastructureElement" abstract="true" + eSuperTypes="#//commons/DOMLElement #//commons/DeployableElement"/> + <eClassifiers xsi:type="ecore:EClass" name="ComputingNode" abstract="true" eSuperTypes="#//infrastructure/InfrastructureElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="architecture" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="os" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="memory_mb" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="storage" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="cpu_count" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="cost" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="ifaces" upperBound="-1" + eType="#//infrastructure/NetworkInterface" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="location" eType="#//infrastructure/Location" + containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="credentials" eType="#//commons/Credentials"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="group" eType="#//infrastructure/ComputingGroup" + eOpposite="#//infrastructure/ComputingGroup/groupedNodes"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="disabledMonitorings" + upperBound="-1" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ComputingNodeGenerator" abstract="true" + eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="uri" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="kind" eType="#//infrastructure/GeneratorKind"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EEnum" name="GeneratorKind"> + <eLiterals name="SCRIPT"/> + <eLiterals name="IMAGE" value="1"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="VMImage" eSuperTypes="#//infrastructure/ComputingNodeGenerator"> + <eStructuralFeatures xsi:type="ecore:EReference" name="generatedVMs" upperBound="-1" + eType="#//infrastructure/VirtualMachine" eOpposite="#//infrastructure/VirtualMachine/generatedFrom"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ContainerImage" eSuperTypes="#//infrastructure/ComputingNodeGenerator"> + <eStructuralFeatures xsi:type="ecore:EReference" name="generatedContainers" + upperBound="-1" eType="#//infrastructure/Container" eOpposite="#//infrastructure/Container/generatedFrom"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="PhysicalComputingNode" eSuperTypes="#//infrastructure/ComputingNode"/> + <eClassifiers xsi:type="ecore:EClass" name="VirtualMachine" eSuperTypes="#//infrastructure/ComputingNode"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="sizeDescription" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="generatedFrom" eType="#//infrastructure/VMImage" + eOpposite="#//infrastructure/VMImage/generatedVMs"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Location" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="region" lowerBound="1" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="zone" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ContainerConfig" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="container_port" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="vm_port" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="host" eType="#//infrastructure/ComputingNode"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="iface" eType="#//infrastructure/NetworkInterface"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Container" eSuperTypes="#//infrastructure/ComputingNode"> + <eStructuralFeatures xsi:type="ecore:EReference" name="generatedFrom" eType="#//infrastructure/ContainerImage" + eOpposite="#//infrastructure/ContainerImage/generatedContainers"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="configs" upperBound="-1" + eType="#//infrastructure/ContainerConfig" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Network" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="protocol" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="addressRange" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="connectedIfaces" upperBound="-1" + eType="#//infrastructure/NetworkInterface" eOpposite="#//infrastructure/NetworkInterface/belongsTo"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="gateways" upperBound="-1" + eType="#//infrastructure/InternetGateway" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="subnets" upperBound="-1" + eType="#//infrastructure/Subnet" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Subnet" eSuperTypes="#//infrastructure/Network"> + <eStructuralFeatures xsi:type="ecore:EReference" name="connectedTo" upperBound="-1" + eType="#//infrastructure/Subnet"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="NetworkInterface" eSuperTypes="#//infrastructure/InfrastructureElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="speed" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="endPoint" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="belongsTo" eType="#//infrastructure/Network" + eOpposite="#//infrastructure/Network/connectedIfaces"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="associated" eType="#//infrastructure/SecurityGroup" + eOpposite="#//infrastructure/SecurityGroup/ifaces"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="InternetGateway" eSuperTypes="#//infrastructure/NetworkInterface"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="address" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Storage" eSuperTypes="#//infrastructure/InfrastructureElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="label" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="size_gb" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="cost" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="ifaces" upperBound="-1" + eType="#//infrastructure/NetworkInterface" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="FunctionAsAService" eSuperTypes="#//infrastructure/InfrastructureElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="cost" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="ifaces" upperBound="-1" + eType="#//infrastructure/NetworkInterface" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EEnum" name="RoleKind"> + <eLiterals name="NONE"/> + <eLiterals name="MANAGER" value="1"/> + <eLiterals name="WORKER" value="2"/> + <eLiterals name="MASTER" value="3"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="SwarmRole" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="kind" eType="#//infrastructure/RoleKind"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="nodes" upperBound="-1" + eType="#//infrastructure/ComputingNode" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Swarm" eSuperTypes="#//infrastructure/ComputingGroup"> + <eStructuralFeatures xsi:type="ecore:EReference" name="roles" upperBound="-1" + eType="#//infrastructure/SwarmRole" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ExtInfrastructureElement" eSuperTypes="#//infrastructure/InfrastructureElement #//commons/ExtensionElement"/> + </eSubpackages> + <eSubpackages name="concrete" nsURI="http://www.piacere-project.eu/doml/concrete" + nsPrefix="concrete"> + <eClassifiers xsi:type="ecore:EClass" name="ConcreteInfrastructure" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="providers" upperBound="-1" + eType="#//concrete/RuntimeProvider" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="RuntimeProvider" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="resources" upperBound="-1" + eType="#//concrete/GenericResource" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="vms" upperBound="-1" + eType="#//concrete/VirtualMachine" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="vmImages" upperBound="-1" + eType="#//concrete/VMImage" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="containerImages" upperBound="-1" + eType="#//concrete/ContainerImage" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="networks" upperBound="-1" + eType="#//concrete/Network" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="storages" upperBound="-1" + eType="#//concrete/Storage" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="faas" upperBound="-1" + eType="#//concrete/FunctionAsAService" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="group" upperBound="-1" + eType="#//concrete/ComputingGroup" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ConcreteElement" abstract="true" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="configurationScript" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="preexisting" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBooleanObject" + defaultValueLiteral="false"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="refs" upperBound="-1" + eType="#//concrete/ConcreteElement"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="GenericResource" eSuperTypes="#//concrete/ConcreteElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="type" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="gname" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="VirtualMachine" eSuperTypes="#//concrete/ConcreteElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="maps" eType="#//infrastructure/VirtualMachine"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="VMImage" eSuperTypes="#//concrete/ConcreteElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="maps" eType="#//infrastructure/VMImage"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ContainerImage" eSuperTypes="#//concrete/ConcreteElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="maps" eType="#//infrastructure/ContainerImage"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Network" eSuperTypes="#//concrete/ConcreteElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="maps" eType="#//infrastructure/Network"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Storage" eSuperTypes="#//concrete/ConcreteElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="maps" eType="#//infrastructure/Storage"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="FunctionAsAService" eSuperTypes="#//concrete/ConcreteElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="maps" eType="#//infrastructure/FunctionAsAService"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ComputingGroup" eSuperTypes="#//concrete/ConcreteElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="maps" eType="#//infrastructure/ComputingGroup"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ExtConcreteElement" eSuperTypes="#//concrete/ConcreteElement #//commons/ExtensionElement"/> + </eSubpackages> + <eSubpackages name="optimization" nsURI="http://www.piacere-project.eu/doml/optimization" + nsPrefix="optimization"> + <eClassifiers xsi:type="ecore:EClass" name="OptimizationLayer" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="startingHint" eType="#//commons/Configuration"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="solutions" upperBound="-1" + eType="#//optimization/OptimizationSolution" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="objectives" upperBound="-1" + eType="#//optimization/OptimizationObjective" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="nonfunctionalRequirements" + upperBound="-1" eType="#//commons/Requirement" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ObjectiveValue"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="cost" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="availability" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="performance" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="OptimizationSolution" eSuperTypes="#//commons/Configuration"> + <eStructuralFeatures xsi:type="ecore:EReference" name="objectives" eType="#//optimization/ObjectiveValue" + containment="true"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="decisions" upperBound="-1" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="OptimizationObjective" abstract="true" + eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="kind" lowerBound="1" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString" + defaultValueLiteral="Max"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="property" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="CountObjective" eSuperTypes="#//optimization/OptimizationObjective"/> + <eClassifiers xsi:type="ecore:EClass" name="MeasurableObjective" eSuperTypes="#//optimization/OptimizationObjective"/> + <eClassifiers xsi:type="ecore:EClass" name="ExtOptimizationObjective" eSuperTypes="#//optimization/OptimizationObjective #//commons/ExtensionElement"/> + </eSubpackages> +</ecore:EPackage> diff --git a/icgparser/doml/v2/doml_v2.0.ecore b/icgparser/doml/v2/doml_v2.0.ecore new file mode 100644 index 0000000..9c23605 --- /dev/null +++ b/icgparser/doml/v2/doml_v2.0.ecore @@ -0,0 +1,390 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="doml" nsURI="http://www.piacere-project.eu/doml" nsPrefix="doml"> + <eAnnotations source="emf.gen"> + <details key="basePackage" value="eu.piacere.doml"/> + <details key="fileExtensions" value="domlx"/> + <details key="complianceLevel" value="JDK80"/> + </eAnnotations> + <eSubpackages name="commons" nsURI="http://www.piacere-project.eu/doml/commons" + nsPrefix="commons"> + <eClassifiers xsi:type="ecore:EClass" name="DOMLModel" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="version" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString" + changeable="false" defaultValueLiteral="v2"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="application" eType="#//application/ApplicationLayer" + containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="infrastructure" eType="#//infrastructure/InfrastructureLayer" + containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="concretizations" upperBound="-1" + eType="#//concrete/ConcreteInfrastructure" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="optimization" eType="#//optimization/OptimizationLayer" + containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="configurations" upperBound="-1" + eType="#//commons/Configuration" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="activeConfiguration" + eType="#//commons/Configuration"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="activeInfrastructure" + eType="#//concrete/ConcreteInfrastructure"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="functionalRequirements" + upperBound="-1" eType="#//commons/Requirement" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Property" abstract="true"> + <eOperations name="getValue" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EJavaObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="key" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="reference" eType="#//commons/DOMLElement"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="IProperty" eSuperTypes="#//commons/Property"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="value" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="SProperty" eSuperTypes="#//commons/Property"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="value" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="FProperty" eSuperTypes="#//commons/Property"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="value" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="BProperty" eSuperTypes="#//commons/Property"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="value" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBooleanObject"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ExtensionElement" abstract="true"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="metaclassName" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="DOMLElement" abstract="true"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="description" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="annotations" upperBound="-1" + eType="#//commons/Property" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="contributesTo" upperBound="-1" + eType="#//commons/Requirement" eOpposite="#//commons/Requirement/predicatesOn"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Configuration" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="deployments" upperBound="-1" + eType="#//commons/Deployment" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Deployment"> + <eStructuralFeatures xsi:type="ecore:EReference" name="component" lowerBound="1" + eType="#//application/ApplicationComponent"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="node" lowerBound="1" + eType="#//infrastructure/InfrastructureElement"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Requirement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="description" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="property" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="predicatesOn" upperBound="-1" + eType="#//commons/DOMLElement" eOpposite="#//commons/DOMLElement/contributesTo"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="RangedRequirement" eSuperTypes="#//commons/Requirement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="min" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="max" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="EnumeratedRequirement" eSuperTypes="#//commons/Requirement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="values" upperBound="-1" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="DeploymentRequirement" abstract="true" + eSuperTypes="#//commons/Requirement"/> + <eClassifiers xsi:type="ecore:EClass" name="DeploymentToNodeTypeRequirement" eSuperTypes="#//commons/DeploymentRequirement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="validTypes" upperBound="-1" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="DeploymentToNodeWithPropertyRequirement" + eSuperTypes="#//commons/DeploymentRequirement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="min" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="max" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="values" upperBound="-1" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="DeploymentToSpecificNodeRequirement" + eSuperTypes="#//commons/DeploymentRequirement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="validElements" upperBound="-1" + eType="#//infrastructure/InfrastructureElement"/> + </eClassifiers> + </eSubpackages> + <eSubpackages name="application" nsURI="http://www.piacere-project.eu/doml/application" + nsPrefix="app"> + <eClassifiers xsi:type="ecore:EClass" name="ApplicationLayer" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="components" upperBound="-1" + eType="#//application/ApplicationComponent" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ApplicationComponent" abstract="true" + eSuperTypes="#//commons/DOMLElement"/> + <eClassifiers xsi:type="ecore:EClass" name="SoftwareComponent" eSuperTypes="#//application/ApplicationComponent"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="isPersistent" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBooleanObject" + defaultValueLiteral="false"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="licenseCost" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="configFile" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="exposedInterfaces" upperBound="-1" + eType="#//application/SoftwareInterface" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="consumedInterfaces" upperBound="-1" + eType="#//application/SoftwareInterface"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="DBMS" eSuperTypes="#//application/SoftwareComponent"/> + <eClassifiers xsi:type="ecore:EClass" name="SaaS" eSuperTypes="#//application/ApplicationComponent"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="licenseCost" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="exposedInterfaces" upperBound="-1" + eType="#//application/SoftwareInterface" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="SaaSDBMS" eSuperTypes="#//application/SaaS"/> + <eClassifiers xsi:type="ecore:EClass" name="SoftwareInterface" eSuperTypes="#//application/ApplicationComponent"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="endPoint" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ExtApplicationComponent" eSuperTypes="#//application/ApplicationComponent #//commons/ExtensionElement"/> + </eSubpackages> + <eSubpackages name="infrastructure" nsURI="http://www.piacere-project.eu/doml/infrastructure" + nsPrefix="infra"> + <eClassifiers xsi:type="ecore:EClass" name="InfrastructureLayer" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="nodes" upperBound="-1" + eType="#//infrastructure/ComputingNode" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="generators" upperBound="-1" + eType="#//infrastructure/ComputingNodeGenerator" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="storages" upperBound="-1" + eType="#//infrastructure/Storage" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="faas" upperBound="-1" + eType="#//infrastructure/FunctionAsAService" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="credentials" upperBound="-1" + eType="#//infrastructure/Credentials" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="groups" upperBound="-1" + eType="#//infrastructure/ComputingGroup" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="securityGroups" upperBound="-1" + eType="#//infrastructure/SecurityGroup" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="networks" upperBound="-1" + eType="#//infrastructure/Network" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ComputingGroup" abstract="true" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="groupedNodes" upperBound="-1" + eType="#//infrastructure/ComputingNode" eOpposite="#//infrastructure/ComputingNode/group"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="AutoScalingGroup" eSuperTypes="#//infrastructure/ComputingGroup"> + <eStructuralFeatures xsi:type="ecore:EReference" name="machineDefinition" lowerBound="1" + eType="#//infrastructure/VirtualMachine" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="min" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject" + defaultValueLiteral="1"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="max" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject" + defaultValueLiteral="1"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="deploymentNetwork" eType="#//infrastructure/Network"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="securityGroup" eType="#//infrastructure/SecurityGroup" + containment="true"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="loadBalancer" eType="#//infrastructure/LoadBalancerKind" + defaultValueLiteral="DEFAULT"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EEnum" name="LoadBalancerKind"> + <eLiterals name="DEFAULT"/> + <eLiterals name="INTERNAL" value="1"/> + <eLiterals name="EXTERNAL" value="2"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Rule" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="kind" eType="#//infrastructure/RuleKind"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="protocol" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="fromPort" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="toPort" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="cidr" upperBound="-1" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EEnum" name="RuleKind"> + <eLiterals name="EGRESS"/> + <eLiterals name="INGRESS" value="1"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="SecurityGroup" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="rules" upperBound="-1" + eType="#//infrastructure/Rule" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="ifaces" upperBound="-1" + eType="#//infrastructure/NetworkInterface" eOpposite="#//infrastructure/NetworkInterface/associated"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="AvailabilityGroup" eSuperTypes="#//infrastructure/ComputingGroup"/> + <eClassifiers xsi:type="ecore:EClass" name="InfrastructureElement" abstract="true" + eSuperTypes="#//commons/DOMLElement"/> + <eClassifiers xsi:type="ecore:EClass" name="ComputingNode" abstract="true" eSuperTypes="#//infrastructure/InfrastructureElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="architecture" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="os" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="memory_mb" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="storage" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="cpu_count" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="cost" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="ifaces" upperBound="-1" + eType="#//infrastructure/NetworkInterface" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="location" eType="#//infrastructure/Location" + containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="credentials" eType="#//infrastructure/Credentials"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="group" eType="#//infrastructure/ComputingGroup" + eOpposite="#//infrastructure/ComputingGroup/groupedNodes"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ComputingNodeGenerator" abstract="true" + eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="uri" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="kind" eType="#//infrastructure/GeneratorKind"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EEnum" name="GeneratorKind"> + <eLiterals name="SCRIPT"/> + <eLiterals name="IMAGE" value="1"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="VMImage" eSuperTypes="#//infrastructure/ComputingNodeGenerator"> + <eStructuralFeatures xsi:type="ecore:EReference" name="generatedVMs" upperBound="-1" + eType="#//infrastructure/VirtualMachine" eOpposite="#//infrastructure/VirtualMachine/generatedFrom"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ContainerImage" eSuperTypes="#//infrastructure/ComputingNodeGenerator"> + <eStructuralFeatures xsi:type="ecore:EReference" name="generatedContainers" + upperBound="-1" eType="#//infrastructure/Container" eOpposite="#//infrastructure/Container/generatedFrom"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="PhysicalComputingNode" eSuperTypes="#//infrastructure/ComputingNode"/> + <eClassifiers xsi:type="ecore:EClass" name="VirtualMachine" eSuperTypes="#//infrastructure/ComputingNode"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="sizeDescription" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="generatedFrom" eType="#//infrastructure/VMImage" + eOpposite="#//infrastructure/VMImage/generatedVMs"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Location" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="region" lowerBound="1" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="zone" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Container" eSuperTypes="#//infrastructure/ComputingNode"> + <eStructuralFeatures xsi:type="ecore:EReference" name="generatedFrom" eType="#//infrastructure/ContainerImage" + eOpposite="#//infrastructure/ContainerImage/generatedContainers"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="hosts" upperBound="-1" + eType="#//infrastructure/ComputingNode"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Network" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="protocol" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="addressRange" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="connectedIfaces" upperBound="-1" + eType="#//infrastructure/NetworkInterface" eOpposite="#//infrastructure/NetworkInterface/belongsTo"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="igws" upperBound="-1" + eType="#//infrastructure/InternetGateway" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="subnets" upperBound="-1" + eType="#//infrastructure/Subnet" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Subnet" eSuperTypes="#//infrastructure/Network"> + <eStructuralFeatures xsi:type="ecore:EReference" name="connectedTo" upperBound="-1" + eType="#//infrastructure/Subnet"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="NetworkInterface" eSuperTypes="#//infrastructure/InfrastructureElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="speed" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="endPoint" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="belongsTo" eType="#//infrastructure/Network" + eOpposite="#//infrastructure/Network/connectedIfaces"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="associated" eType="#//infrastructure/SecurityGroup" + eOpposite="#//infrastructure/SecurityGroup/ifaces"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="InternetGateway" eSuperTypes="#//infrastructure/NetworkInterface"/> + <eClassifiers xsi:type="ecore:EClass" name="Storage" eSuperTypes="#//infrastructure/InfrastructureElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="label" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="size_gb" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="cost" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="ifaces" upperBound="-1" + eType="#//infrastructure/NetworkInterface" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="FunctionAsAService" eSuperTypes="#//infrastructure/InfrastructureElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="cost" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="ifaces" upperBound="-1" + eType="#//infrastructure/NetworkInterface" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Credentials" abstract="true" eSuperTypes="#//commons/DOMLElement"/> + <eClassifiers xsi:type="ecore:EClass" name="KeyPair" eSuperTypes="#//infrastructure/Credentials"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="user" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="keyfile" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="algorithm" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="bits" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="UserPass" eSuperTypes="#//infrastructure/Credentials"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="username" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="password" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EEnum" name="RoleKind"> + <eLiterals name="NONE"/> + <eLiterals name="MANAGER" value="1"/> + <eLiterals name="WORKER" value="2"/> + <eLiterals name="MASTER" value="3"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="SwarmRole" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="kind" eType="#//infrastructure/RoleKind"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="nodes" upperBound="-1" + eType="#//infrastructure/ComputingNode" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Swarm" eSuperTypes="#//infrastructure/ComputingGroup"> + <eStructuralFeatures xsi:type="ecore:EReference" name="roles" upperBound="-1" + eType="#//infrastructure/SwarmRole" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ExtInfrastructureElement" eSuperTypes="#//infrastructure/InfrastructureElement #//commons/ExtensionElement"/> + </eSubpackages> + <eSubpackages name="concrete" nsURI="http://www.piacere-project.eu/doml/concrete" + nsPrefix="concrete"> + <eClassifiers xsi:type="ecore:EClass" name="ConcreteInfrastructure" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="providers" upperBound="-1" + eType="#//concrete/RuntimeProvider" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="RuntimeProvider" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="vms" upperBound="-1" + eType="#//concrete/VirtualMachine" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="vmImages" upperBound="-1" + eType="#//concrete/VMImage" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="containerImages" upperBound="-1" + eType="#//concrete/ContainerImage" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="networks" upperBound="-1" + eType="#//concrete/Network" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="storages" upperBound="-1" + eType="#//concrete/Storage" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="faas" upperBound="-1" + eType="#//concrete/FunctionAsAService" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="group" upperBound="-1" + eType="#//concrete/ComputingGroup" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ConcreteElement" abstract="true" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="configurationScript" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="VirtualMachine" eSuperTypes="#//concrete/ConcreteElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="maps" eType="#//infrastructure/VirtualMachine"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="VMImage" eSuperTypes="#//concrete/ConcreteElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="maps" eType="#//infrastructure/VMImage"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ContainerImage" eSuperTypes="#//concrete/ConcreteElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="maps" eType="#//infrastructure/ContainerImage"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Network" eSuperTypes="#//concrete/ConcreteElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="maps" eType="#//infrastructure/Network"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Storage" eSuperTypes="#//concrete/ConcreteElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="maps" eType="#//infrastructure/Storage"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="FunctionAsAService" eSuperTypes="#//concrete/ConcreteElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="maps" eType="#//infrastructure/FunctionAsAService"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ComputingGroup" eSuperTypes="#//concrete/ConcreteElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="maps" eType="#//infrastructure/ComputingGroup"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ExtConcreteElement" eSuperTypes="#//concrete/ConcreteElement #//commons/ExtensionElement"/> + </eSubpackages> + <eSubpackages name="optimization" nsURI="http://www.piacere-project.eu/doml/optimization" + nsPrefix="optimization"> + <eClassifiers xsi:type="ecore:EClass" name="OptimizationLayer" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="startingHint" eType="#//commons/Configuration"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="solutions" upperBound="-1" + eType="#//optimization/OptimizationSolution" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="objectives" upperBound="-1" + eType="#//optimization/OptimizationObjective" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="nonfunctionalRequirements" + upperBound="-1" eType="#//commons/Requirement" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ObjectiveValue"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="cost" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="availability" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="performance" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="OptimizationSolution" eSuperTypes="#//commons/Configuration"> + <eStructuralFeatures xsi:type="ecore:EReference" name="objectives" eType="#//optimization/ObjectiveValue" + containment="true"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="decisions" upperBound="-1" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="OptimizationObjective" abstract="true" + eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="kind" lowerBound="1" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString" + defaultValueLiteral="Max"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="property" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="CountObjective" eSuperTypes="#//optimization/OptimizationObjective"/> + <eClassifiers xsi:type="ecore:EClass" name="MeasurableObjective" eSuperTypes="#//optimization/OptimizationObjective"/> + <eClassifiers xsi:type="ecore:EClass" name="ExtOptimizationObjective" eSuperTypes="#//optimization/OptimizationObjective #//commons/ExtensionElement"/> + </eSubpackages> +</ecore:EPackage> diff --git a/icgparser/doml/v2/doml_v2.1-2.ecore b/icgparser/doml/v2/doml_v2.1-2.ecore new file mode 100644 index 0000000..3b499b4 --- /dev/null +++ b/icgparser/doml/v2/doml_v2.1-2.ecore @@ -0,0 +1,423 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="doml" nsURI="http://www.piacere-project.eu/doml" nsPrefix="doml"> + <eAnnotations source="emf.gen"> + <details key="basePackage" value="eu.piacere.doml"/> + <details key="fileExtensions" value="domlx"/> + <details key="complianceLevel" value="JDK80"/> + </eAnnotations> + <eSubpackages name="commons" nsURI="http://www.piacere-project.eu/doml/commons" + nsPrefix="commons"> + <eClassifiers xsi:type="ecore:EClass" name="DOMLModel" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="version" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString" + defaultValueLiteral="2.2"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="application" eType="#//application/ApplicationLayer" + containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="infrastructure" eType="#//infrastructure/InfrastructureLayer" + containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="concretizations" upperBound="-1" + eType="#//concrete/ConcreteInfrastructure" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="optimization" eType="#//optimization/OptimizationLayer" + containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="configurations" upperBound="-1" + eType="#//commons/Configuration" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="activeConfiguration" + eType="#//commons/Configuration"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="activeInfrastructure" + eType="#//concrete/ConcreteInfrastructure"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="functionalRequirements" + upperBound="-1" eType="#//commons/Requirement" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Property" abstract="true"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="key" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="IProperty" eSuperTypes="#//commons/Property"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="value" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="SProperty" eSuperTypes="#//commons/Property"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="value" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="FProperty" eSuperTypes="#//commons/Property"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="value" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="BProperty" eSuperTypes="#//commons/Property"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="value" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBooleanObject"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ExtensionElement" abstract="true"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="metaclassName" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="DOMLElement" abstract="true"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="description" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="annotations" upperBound="-1" + eType="#//commons/Property" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="contributesTo" upperBound="-1" + eType="#//commons/Requirement" eOpposite="#//commons/Requirement/predicatesOn"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Configuration" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="deployments" upperBound="-1" + eType="#//commons/Deployment" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="DeployableElement" abstract="true"/> + <eClassifiers xsi:type="ecore:EClass" name="Deployment"> + <eStructuralFeatures xsi:type="ecore:EReference" name="component" lowerBound="1" + eType="#//commons/DeployableElement"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="node" lowerBound="1" + eType="#//infrastructure/InfrastructureElement"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Requirement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="description" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="property" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="predicatesOn" upperBound="-1" + eType="#//commons/DOMLElement" eOpposite="#//commons/DOMLElement/contributesTo"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="RangedRequirement" eSuperTypes="#//commons/Requirement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="min" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="max" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="EnumeratedRequirement" eSuperTypes="#//commons/Requirement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="values" upperBound="-1" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="DeploymentRequirement" abstract="true" + eSuperTypes="#//commons/Requirement"/> + <eClassifiers xsi:type="ecore:EClass" name="DeploymentToNodeTypeRequirement" eSuperTypes="#//commons/DeploymentRequirement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="validTypes" upperBound="-1" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="DeploymentToNodeWithPropertyRequirement" + eSuperTypes="#//commons/DeploymentRequirement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="min" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="max" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="values" upperBound="-1" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="DeploymentToSpecificNodeRequirement" + eSuperTypes="#//commons/DeploymentRequirement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="validElements" upperBound="-1" + eType="#//infrastructure/InfrastructureElement"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Credentials" abstract="true" eSuperTypes="#//commons/DOMLElement"/> + <eClassifiers xsi:type="ecore:EClass" name="KeyPair" eSuperTypes="#//commons/Credentials"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="user" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="keyfile" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="algorithm" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="bits" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="UserPass" eSuperTypes="#//commons/Credentials"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="username" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="password" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Source" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="entry" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="backend" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + </eSubpackages> + <eSubpackages name="application" nsURI="http://www.piacere-project.eu/doml/application" + nsPrefix="app"> + <eClassifiers xsi:type="ecore:EClass" name="ApplicationLayer" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="components" upperBound="-1" + eType="#//application/ApplicationComponent" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ApplicationComponent" abstract="true" + eSuperTypes="#//commons/DOMLElement #//commons/DeployableElement"/> + <eClassifiers xsi:type="ecore:EClass" name="SoftwareComponent" eSuperTypes="#//application/ApplicationComponent"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="isPersistent" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBooleanObject" + defaultValueLiteral="false"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="licenseCost" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="exposedInterfaces" upperBound="-1" + eType="#//application/SoftwareInterface" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="consumedInterfaces" upperBound="-1" + eType="#//application/SoftwareInterface"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="src" eType="#//commons/Source" + containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="DBMS" eSuperTypes="#//application/SoftwareComponent"/> + <eClassifiers xsi:type="ecore:EClass" name="SaaS" eSuperTypes="#//application/ApplicationComponent"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="licenseCost" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="exposedInterfaces" upperBound="-1" + eType="#//application/SoftwareInterface" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="SaaSDBMS" eSuperTypes="#//application/SaaS"/> + <eClassifiers xsi:type="ecore:EClass" name="SoftwareInterface" eSuperTypes="#//application/ApplicationComponent"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="endPoint" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ExtApplicationComponent" eSuperTypes="#//application/ApplicationComponent #//commons/ExtensionElement"/> + </eSubpackages> + <eSubpackages name="infrastructure" nsURI="http://www.piacere-project.eu/doml/infrastructure" + nsPrefix="infra"> + <eClassifiers xsi:type="ecore:EClass" name="InfrastructureLayer" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="nodes" upperBound="-1" + eType="#//infrastructure/ComputingNode" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="generators" upperBound="-1" + eType="#//infrastructure/ComputingNodeGenerator" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="storages" upperBound="-1" + eType="#//infrastructure/Storage" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="faas" upperBound="-1" + eType="#//infrastructure/FunctionAsAService" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="credentials" upperBound="-1" + eType="#//commons/Credentials" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="groups" upperBound="-1" + eType="#//infrastructure/ComputingGroup" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="securityGroups" upperBound="-1" + eType="#//infrastructure/SecurityGroup" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="networks" upperBound="-1" + eType="#//infrastructure/Network" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="rules" upperBound="-1" + eType="#//infrastructure/MonitoringRule" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="MonitoringRule" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="condition" lowerBound="1" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="strategy" lowerBound="1" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="strategyConfigurationString" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ComputingGroup" abstract="true" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="groupedNodes" upperBound="-1" + eType="#//infrastructure/ComputingNode" eOpposite="#//infrastructure/ComputingNode/group"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="AutoScalingGroup" eSuperTypes="#//infrastructure/ComputingGroup"> + <eStructuralFeatures xsi:type="ecore:EReference" name="machineDefinition" lowerBound="1" + eType="#//infrastructure/VirtualMachine" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="min" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject" + defaultValueLiteral="1"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="max" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject" + defaultValueLiteral="1"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="securityGroup" eType="#//infrastructure/SecurityGroup" + containment="true"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="loadBalancer" eType="#//infrastructure/LoadBalancerKind" + defaultValueLiteral="DEFAULT"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EEnum" name="LoadBalancerKind"> + <eLiterals name="DEFAULT"/> + <eLiterals name="INTERNAL" value="1"/> + <eLiterals name="EXTERNAL" value="2"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Rule" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="kind" eType="#//infrastructure/RuleKind"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="protocol" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="fromPort" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="toPort" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="cidr" upperBound="-1" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EEnum" name="RuleKind"> + <eLiterals name="EGRESS"/> + <eLiterals name="INGRESS" value="1"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="SecurityGroup" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="rules" upperBound="-1" + eType="#//infrastructure/Rule" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="ifaces" upperBound="-1" + eType="#//infrastructure/NetworkInterface" eOpposite="#//infrastructure/NetworkInterface/associated"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="AvailabilityGroup" eSuperTypes="#//infrastructure/ComputingGroup"/> + <eClassifiers xsi:type="ecore:EClass" name="InfrastructureElement" abstract="true" + eSuperTypes="#//commons/DOMLElement #//commons/DeployableElement"/> + <eClassifiers xsi:type="ecore:EClass" name="ComputingNode" abstract="true" eSuperTypes="#//infrastructure/InfrastructureElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="architecture" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="os" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="memory_mb" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="storage" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="cpu_count" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="cost" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="ifaces" upperBound="-1" + eType="#//infrastructure/NetworkInterface" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="location" eType="#//infrastructure/Location" + containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="credentials" eType="#//commons/Credentials"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="group" eType="#//infrastructure/ComputingGroup" + eOpposite="#//infrastructure/ComputingGroup/groupedNodes"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="disabledMonitorings" + upperBound="-1" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ComputingNodeGenerator" abstract="true" + eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="uri" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="kind" eType="#//infrastructure/GeneratorKind"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EEnum" name="GeneratorKind"> + <eLiterals name="SCRIPT"/> + <eLiterals name="IMAGE" value="1"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="VMImage" eSuperTypes="#//infrastructure/ComputingNodeGenerator"> + <eStructuralFeatures xsi:type="ecore:EReference" name="generatedVMs" upperBound="-1" + eType="#//infrastructure/VirtualMachine" eOpposite="#//infrastructure/VirtualMachine/generatedFrom"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ContainerImage" eSuperTypes="#//infrastructure/ComputingNodeGenerator"> + <eStructuralFeatures xsi:type="ecore:EReference" name="generatedContainers" + upperBound="-1" eType="#//infrastructure/Container" eOpposite="#//infrastructure/Container/generatedFrom"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="PhysicalComputingNode" eSuperTypes="#//infrastructure/ComputingNode"/> + <eClassifiers xsi:type="ecore:EClass" name="VirtualMachine" eSuperTypes="#//infrastructure/ComputingNode"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="sizeDescription" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="generatedFrom" eType="#//infrastructure/VMImage" + eOpposite="#//infrastructure/VMImage/generatedVMs"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Location" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="region" lowerBound="1" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="zone" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ContainerConfig" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="container_port" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="vm_port" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="host" eType="#//infrastructure/ComputingNode"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="iface" eType="#//infrastructure/NetworkInterface"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Container" eSuperTypes="#//infrastructure/ComputingNode"> + <eStructuralFeatures xsi:type="ecore:EReference" name="generatedFrom" eType="#//infrastructure/ContainerImage" + eOpposite="#//infrastructure/ContainerImage/generatedContainers"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="configs" upperBound="-1" + eType="#//infrastructure/ContainerConfig" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Network" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="protocol" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="addressRange" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="connectedIfaces" upperBound="-1" + eType="#//infrastructure/NetworkInterface" eOpposite="#//infrastructure/NetworkInterface/belongsTo"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="gateways" upperBound="-1" + eType="#//infrastructure/InternetGateway" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="subnets" upperBound="-1" + eType="#//infrastructure/Subnet" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Subnet" eSuperTypes="#//infrastructure/Network"> + <eStructuralFeatures xsi:type="ecore:EReference" name="connectedTo" upperBound="-1" + eType="#//infrastructure/Subnet"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="NetworkInterface" eSuperTypes="#//infrastructure/InfrastructureElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="speed" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="endPoint" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="belongsTo" eType="#//infrastructure/Network" + eOpposite="#//infrastructure/Network/connectedIfaces"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="associated" eType="#//infrastructure/SecurityGroup" + eOpposite="#//infrastructure/SecurityGroup/ifaces"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="InternetGateway" eSuperTypes="#//infrastructure/NetworkInterface"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="address" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Storage" eSuperTypes="#//infrastructure/InfrastructureElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="label" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="size_gb" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="cost" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="ifaces" upperBound="-1" + eType="#//infrastructure/NetworkInterface" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="FunctionAsAService" eSuperTypes="#//infrastructure/InfrastructureElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="cost" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="ifaces" upperBound="-1" + eType="#//infrastructure/NetworkInterface" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EEnum" name="RoleKind"> + <eLiterals name="NONE"/> + <eLiterals name="MANAGER" value="1"/> + <eLiterals name="WORKER" value="2"/> + <eLiterals name="MASTER" value="3"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="SwarmRole" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="kind" eType="#//infrastructure/RoleKind"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="nodes" upperBound="-1" + eType="#//infrastructure/ComputingNode" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Swarm" eSuperTypes="#//infrastructure/ComputingGroup"> + <eStructuralFeatures xsi:type="ecore:EReference" name="roles" upperBound="-1" + eType="#//infrastructure/SwarmRole" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ExtInfrastructureElement" eSuperTypes="#//infrastructure/InfrastructureElement #//commons/ExtensionElement"/> + </eSubpackages> + <eSubpackages name="concrete" nsURI="http://www.piacere-project.eu/doml/concrete" + nsPrefix="concrete"> + <eClassifiers xsi:type="ecore:EClass" name="ConcreteInfrastructure" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="providers" upperBound="-1" + eType="#//concrete/RuntimeProvider" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="RuntimeProvider" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="resources" upperBound="-1" + eType="#//concrete/GenericResource" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="vms" upperBound="-1" + eType="#//concrete/VirtualMachine" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="vmImages" upperBound="-1" + eType="#//concrete/VMImage" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="containerImages" upperBound="-1" + eType="#//concrete/ContainerImage" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="networks" upperBound="-1" + eType="#//concrete/Network" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="storages" upperBound="-1" + eType="#//concrete/Storage" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="faas" upperBound="-1" + eType="#//concrete/FunctionAsAService" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="group" upperBound="-1" + eType="#//concrete/ComputingGroup" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ConcreteElement" abstract="true" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="configurationScript" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="preexisting" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBooleanObject" + defaultValueLiteral="false"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="refs" upperBound="-1" + eType="#//concrete/ConcreteElement"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="GenericResource" eSuperTypes="#//concrete/ConcreteElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="type" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="gname" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="VirtualMachine" eSuperTypes="#//concrete/ConcreteElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="maps" eType="#//infrastructure/VirtualMachine"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="VMImage" eSuperTypes="#//concrete/ConcreteElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="maps" eType="#//infrastructure/VMImage"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ContainerImage" eSuperTypes="#//concrete/ConcreteElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="maps" eType="#//infrastructure/ContainerImage"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Network" eSuperTypes="#//concrete/ConcreteElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="maps" eType="#//infrastructure/Network"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Storage" eSuperTypes="#//concrete/ConcreteElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="maps" eType="#//infrastructure/Storage"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="FunctionAsAService" eSuperTypes="#//concrete/ConcreteElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="maps" eType="#//infrastructure/FunctionAsAService"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ComputingGroup" eSuperTypes="#//concrete/ConcreteElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="maps" eType="#//infrastructure/ComputingGroup"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ExtConcreteElement" eSuperTypes="#//concrete/ConcreteElement #//commons/ExtensionElement"/> + </eSubpackages> + <eSubpackages name="optimization" nsURI="http://www.piacere-project.eu/doml/optimization" + nsPrefix="optimization"> + <eClassifiers xsi:type="ecore:EClass" name="OptimizationLayer" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="startingHint" eType="#//commons/Configuration"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="solutions" upperBound="-1" + eType="#//optimization/OptimizationSolution" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="objectives" upperBound="-1" + eType="#//optimization/OptimizationObjective" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="nonfunctionalRequirements" + upperBound="-1" eType="#//commons/Requirement" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ObjectiveValue"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="cost" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="availability" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="performance" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="OptimizationSolution" eSuperTypes="#//commons/Configuration"> + <eStructuralFeatures xsi:type="ecore:EReference" name="objectives" eType="#//optimization/ObjectiveValue" + containment="true"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="decisions" upperBound="-1" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="OptimizationObjective" abstract="true" + eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="kind" lowerBound="1" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString" + defaultValueLiteral="Max"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="property" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="CountObjective" eSuperTypes="#//optimization/OptimizationObjective"/> + <eClassifiers xsi:type="ecore:EClass" name="MeasurableObjective" eSuperTypes="#//optimization/OptimizationObjective"/> + <eClassifiers xsi:type="ecore:EClass" name="ExtOptimizationObjective" eSuperTypes="#//optimization/OptimizationObjective #//commons/ExtensionElement"/> + </eSubpackages> +</ecore:EPackage> diff --git a/icgparser/doml/v2/doml_v2.1.ecore b/icgparser/doml/v2/doml_v2.1.ecore new file mode 100644 index 0000000..02f3904 --- /dev/null +++ b/icgparser/doml/v2/doml_v2.1.ecore @@ -0,0 +1,396 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="doml" nsURI="http://www.piacere-project.eu/doml" nsPrefix="doml"> + <eAnnotations source="emf.gen"> + <details key="basePackage" value="eu.piacere.doml"/> + <details key="fileExtensions" value="domlx"/> + <details key="complianceLevel" value="JDK80"/> + </eAnnotations> + <eSubpackages name="commons" nsURI="http://www.piacere-project.eu/doml/commons" + nsPrefix="commons"> + <eClassifiers xsi:type="ecore:EClass" name="DOMLModel" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="version" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString" + changeable="false" defaultValueLiteral="v2"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="application" eType="#//application/ApplicationLayer" + containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="infrastructure" eType="#//infrastructure/InfrastructureLayer" + containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="concretizations" upperBound="-1" + eType="#//concrete/ConcreteInfrastructure" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="optimization" eType="#//optimization/OptimizationLayer" + containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="configurations" upperBound="-1" + eType="#//commons/Configuration" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="activeConfiguration" + eType="#//commons/Configuration"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="activeInfrastructure" + eType="#//concrete/ConcreteInfrastructure"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="functionalRequirements" + upperBound="-1" eType="#//commons/Requirement" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Property" abstract="true"> + <eOperations name="getValue" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EJavaObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="key" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="reference" eType="#//commons/DOMLElement"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="IProperty" eSuperTypes="#//commons/Property"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="value" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="SProperty" eSuperTypes="#//commons/Property"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="value" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="FProperty" eSuperTypes="#//commons/Property"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="value" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="BProperty" eSuperTypes="#//commons/Property"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="value" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBooleanObject"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ExtensionElement" abstract="true"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="metaclassName" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="DOMLElement" abstract="true"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="description" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="annotations" upperBound="-1" + eType="#//commons/Property" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="contributesTo" upperBound="-1" + eType="#//commons/Requirement" eOpposite="#//commons/Requirement/predicatesOn"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Configuration" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="deployments" upperBound="-1" + eType="#//commons/Deployment" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Deployment"> + <eStructuralFeatures xsi:type="ecore:EReference" name="component" lowerBound="1" + eType="#//application/ApplicationComponent"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="node" lowerBound="1" + eType="#//infrastructure/InfrastructureElement"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Requirement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="description" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="property" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="predicatesOn" upperBound="-1" + eType="#//commons/DOMLElement" eOpposite="#//commons/DOMLElement/contributesTo"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="RangedRequirement" eSuperTypes="#//commons/Requirement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="min" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="max" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="EnumeratedRequirement" eSuperTypes="#//commons/Requirement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="values" upperBound="-1" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="DeploymentRequirement" abstract="true" + eSuperTypes="#//commons/Requirement"/> + <eClassifiers xsi:type="ecore:EClass" name="DeploymentToNodeTypeRequirement" eSuperTypes="#//commons/DeploymentRequirement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="validTypes" upperBound="-1" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="DeploymentToNodeWithPropertyRequirement" + eSuperTypes="#//commons/DeploymentRequirement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="min" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="max" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="values" upperBound="-1" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="DeploymentToSpecificNodeRequirement" + eSuperTypes="#//commons/DeploymentRequirement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="validElements" upperBound="-1" + eType="#//infrastructure/InfrastructureElement"/> + </eClassifiers> + </eSubpackages> + <eSubpackages name="application" nsURI="http://www.piacere-project.eu/doml/application" + nsPrefix="app"> + <eClassifiers xsi:type="ecore:EClass" name="ApplicationLayer" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="components" upperBound="-1" + eType="#//application/ApplicationComponent" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ApplicationComponent" abstract="true" + eSuperTypes="#//commons/DOMLElement"/> + <eClassifiers xsi:type="ecore:EClass" name="SoftwareComponent" eSuperTypes="#//application/ApplicationComponent"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="isPersistent" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBooleanObject" + defaultValueLiteral="false"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="licenseCost" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="configFile" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="exposedInterfaces" upperBound="-1" + eType="#//application/SoftwareInterface" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="consumedInterfaces" upperBound="-1" + eType="#//application/SoftwareInterface"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="DBMS" eSuperTypes="#//application/SoftwareComponent"/> + <eClassifiers xsi:type="ecore:EClass" name="SaaS" eSuperTypes="#//application/ApplicationComponent"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="licenseCost" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="exposedInterfaces" upperBound="-1" + eType="#//application/SoftwareInterface" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="SaaSDBMS" eSuperTypes="#//application/SaaS"/> + <eClassifiers xsi:type="ecore:EClass" name="SoftwareInterface" eSuperTypes="#//application/ApplicationComponent"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="endPoint" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ExtApplicationComponent" eSuperTypes="#//application/ApplicationComponent #//commons/ExtensionElement"/> + </eSubpackages> + <eSubpackages name="infrastructure" nsURI="http://www.piacere-project.eu/doml/infrastructure" + nsPrefix="infra"> + <eClassifiers xsi:type="ecore:EClass" name="InfrastructureLayer" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="nodes" upperBound="-1" + eType="#//infrastructure/ComputingNode" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="generators" upperBound="-1" + eType="#//infrastructure/ComputingNodeGenerator" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="storages" upperBound="-1" + eType="#//infrastructure/Storage" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="faas" upperBound="-1" + eType="#//infrastructure/FunctionAsAService" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="credentials" upperBound="-1" + eType="#//infrastructure/Credentials" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="groups" upperBound="-1" + eType="#//infrastructure/ComputingGroup" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="securityGroups" upperBound="-1" + eType="#//infrastructure/SecurityGroup" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="networks" upperBound="-1" + eType="#//infrastructure/Network" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ComputingGroup" abstract="true" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="groupedNodes" upperBound="-1" + eType="#//infrastructure/ComputingNode" eOpposite="#//infrastructure/ComputingNode/group"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="AutoScalingGroup" eSuperTypes="#//infrastructure/ComputingGroup"> + <eStructuralFeatures xsi:type="ecore:EReference" name="machineDefinition" lowerBound="1" + eType="#//infrastructure/VirtualMachine" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="min" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject" + defaultValueLiteral="1"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="max" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject" + defaultValueLiteral="1"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="securityGroup" eType="#//infrastructure/SecurityGroup" + containment="true"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="loadBalancer" eType="#//infrastructure/LoadBalancerKind" + defaultValueLiteral="DEFAULT"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EEnum" name="LoadBalancerKind"> + <eLiterals name="DEFAULT"/> + <eLiterals name="INTERNAL" value="1"/> + <eLiterals name="EXTERNAL" value="2"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Rule" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="kind" eType="#//infrastructure/RuleKind"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="protocol" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="fromPort" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="toPort" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="cidr" upperBound="-1" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EEnum" name="RuleKind"> + <eLiterals name="EGRESS"/> + <eLiterals name="INGRESS" value="1"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="SecurityGroup" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="rules" upperBound="-1" + eType="#//infrastructure/Rule" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="ifaces" upperBound="-1" + eType="#//infrastructure/NetworkInterface" eOpposite="#//infrastructure/NetworkInterface/associated"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="AvailabilityGroup" eSuperTypes="#//infrastructure/ComputingGroup"/> + <eClassifiers xsi:type="ecore:EClass" name="InfrastructureElement" abstract="true" + eSuperTypes="#//commons/DOMLElement"/> + <eClassifiers xsi:type="ecore:EClass" name="ComputingNode" abstract="true" eSuperTypes="#//infrastructure/InfrastructureElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="architecture" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="os" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="memory_mb" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="storage" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="cpu_count" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="cost" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="ifaces" upperBound="-1" + eType="#//infrastructure/NetworkInterface" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="location" eType="#//infrastructure/Location" + containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="credentials" eType="#//infrastructure/Credentials"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="group" eType="#//infrastructure/ComputingGroup" + eOpposite="#//infrastructure/ComputingGroup/groupedNodes"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ComputingNodeGenerator" abstract="true" + eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="uri" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="kind" eType="#//infrastructure/GeneratorKind"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EEnum" name="GeneratorKind"> + <eLiterals name="SCRIPT"/> + <eLiterals name="IMAGE" value="1"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="VMImage" eSuperTypes="#//infrastructure/ComputingNodeGenerator"> + <eStructuralFeatures xsi:type="ecore:EReference" name="generatedVMs" upperBound="-1" + eType="#//infrastructure/VirtualMachine" eOpposite="#//infrastructure/VirtualMachine/generatedFrom"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ContainerImage" eSuperTypes="#//infrastructure/ComputingNodeGenerator"> + <eStructuralFeatures xsi:type="ecore:EReference" name="generatedContainers" + upperBound="-1" eType="#//infrastructure/Container" eOpposite="#//infrastructure/Container/generatedFrom"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="PhysicalComputingNode" eSuperTypes="#//infrastructure/ComputingNode"/> + <eClassifiers xsi:type="ecore:EClass" name="VirtualMachine" eSuperTypes="#//infrastructure/ComputingNode"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="sizeDescription" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="generatedFrom" eType="#//infrastructure/VMImage" + eOpposite="#//infrastructure/VMImage/generatedVMs"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Location" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="region" lowerBound="1" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="zone" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ContainerConfig" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="container_port" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="vm_port" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="host" eType="#//infrastructure/ComputingNode"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="iface" eType="#//infrastructure/NetworkInterface"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Container" eSuperTypes="#//infrastructure/ComputingNode"> + <eStructuralFeatures xsi:type="ecore:EReference" name="generatedFrom" eType="#//infrastructure/ContainerImage" + eOpposite="#//infrastructure/ContainerImage/generatedContainers"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="configs" upperBound="-1" + eType="#//infrastructure/ContainerConfig" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Network" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="protocol" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="addressRange" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="connectedIfaces" upperBound="-1" + eType="#//infrastructure/NetworkInterface" eOpposite="#//infrastructure/NetworkInterface/belongsTo"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="igws" upperBound="-1" + eType="#//infrastructure/InternetGateway" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="subnets" upperBound="-1" + eType="#//infrastructure/Subnet" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Subnet" eSuperTypes="#//infrastructure/Network"> + <eStructuralFeatures xsi:type="ecore:EReference" name="connectedTo" upperBound="-1" + eType="#//infrastructure/Subnet"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="NetworkInterface" eSuperTypes="#//infrastructure/InfrastructureElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="speed" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="endPoint" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="belongsTo" eType="#//infrastructure/Network" + eOpposite="#//infrastructure/Network/connectedIfaces"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="associated" eType="#//infrastructure/SecurityGroup" + eOpposite="#//infrastructure/SecurityGroup/ifaces"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="InternetGateway" eSuperTypes="#//infrastructure/NetworkInterface"/> + <eClassifiers xsi:type="ecore:EClass" name="Storage" eSuperTypes="#//infrastructure/InfrastructureElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="label" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="size_gb" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="cost" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="ifaces" upperBound="-1" + eType="#//infrastructure/NetworkInterface" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="FunctionAsAService" eSuperTypes="#//infrastructure/InfrastructureElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="cost" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="ifaces" upperBound="-1" + eType="#//infrastructure/NetworkInterface" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Credentials" abstract="true" eSuperTypes="#//commons/DOMLElement"/> + <eClassifiers xsi:type="ecore:EClass" name="KeyPair" eSuperTypes="#//infrastructure/Credentials"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="user" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="keyfile" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="algorithm" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="bits" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="UserPass" eSuperTypes="#//infrastructure/Credentials"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="username" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="password" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EEnum" name="RoleKind"> + <eLiterals name="NONE"/> + <eLiterals name="MANAGER" value="1"/> + <eLiterals name="WORKER" value="2"/> + <eLiterals name="MASTER" value="3"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="SwarmRole" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="kind" eType="#//infrastructure/RoleKind"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="nodes" upperBound="-1" + eType="#//infrastructure/ComputingNode" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Swarm" eSuperTypes="#//infrastructure/ComputingGroup"> + <eStructuralFeatures xsi:type="ecore:EReference" name="roles" upperBound="-1" + eType="#//infrastructure/SwarmRole" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ExtInfrastructureElement" eSuperTypes="#//infrastructure/InfrastructureElement #//commons/ExtensionElement"/> + </eSubpackages> + <eSubpackages name="concrete" nsURI="http://www.piacere-project.eu/doml/concrete" + nsPrefix="concrete"> + <eClassifiers xsi:type="ecore:EClass" name="ConcreteInfrastructure" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="providers" upperBound="-1" + eType="#//concrete/RuntimeProvider" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="RuntimeProvider" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="vms" upperBound="-1" + eType="#//concrete/VirtualMachine" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="vmImages" upperBound="-1" + eType="#//concrete/VMImage" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="containerImages" upperBound="-1" + eType="#//concrete/ContainerImage" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="networks" upperBound="-1" + eType="#//concrete/Network" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="storages" upperBound="-1" + eType="#//concrete/Storage" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="faas" upperBound="-1" + eType="#//concrete/FunctionAsAService" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="group" upperBound="-1" + eType="#//concrete/ComputingGroup" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ConcreteElement" abstract="true" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="configurationScript" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="VirtualMachine" eSuperTypes="#//concrete/ConcreteElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="maps" eType="#//infrastructure/VirtualMachine"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="VMImage" eSuperTypes="#//concrete/ConcreteElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="maps" eType="#//infrastructure/VMImage"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ContainerImage" eSuperTypes="#//concrete/ConcreteElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="maps" eType="#//infrastructure/ContainerImage"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Network" eSuperTypes="#//concrete/ConcreteElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="address" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="maps" eType="#//infrastructure/Network"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Storage" eSuperTypes="#//concrete/ConcreteElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="maps" eType="#//infrastructure/Storage"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="FunctionAsAService" eSuperTypes="#//concrete/ConcreteElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="maps" eType="#//infrastructure/FunctionAsAService"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ComputingGroup" eSuperTypes="#//concrete/ConcreteElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="maps" eType="#//infrastructure/ComputingGroup"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ExtConcreteElement" eSuperTypes="#//concrete/ConcreteElement #//commons/ExtensionElement"/> + </eSubpackages> + <eSubpackages name="optimization" nsURI="http://www.piacere-project.eu/doml/optimization" + nsPrefix="optimization"> + <eClassifiers xsi:type="ecore:EClass" name="OptimizationLayer" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="startingHint" eType="#//commons/Configuration"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="solutions" upperBound="-1" + eType="#//optimization/OptimizationSolution" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="objectives" upperBound="-1" + eType="#//optimization/OptimizationObjective" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="nonfunctionalRequirements" + upperBound="-1" eType="#//commons/Requirement" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ObjectiveValue"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="cost" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="availability" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="performance" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="OptimizationSolution" eSuperTypes="#//commons/Configuration"> + <eStructuralFeatures xsi:type="ecore:EReference" name="objectives" eType="#//optimization/ObjectiveValue" + containment="true"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="decisions" upperBound="-1" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="OptimizationObjective" abstract="true" + eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="kind" lowerBound="1" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString" + defaultValueLiteral="Max"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="property" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="CountObjective" eSuperTypes="#//optimization/OptimizationObjective"/> + <eClassifiers xsi:type="ecore:EClass" name="MeasurableObjective" eSuperTypes="#//optimization/OptimizationObjective"/> + <eClassifiers xsi:type="ecore:EClass" name="ExtOptimizationObjective" eSuperTypes="#//optimization/OptimizationObjective #//commons/ExtensionElement"/> + </eSubpackages> +</ecore:EPackage> diff --git a/icgparser/doml/v2/doml_v2.2.ecore b/icgparser/doml/v2/doml_v2.2.ecore new file mode 100644 index 0000000..f80777f --- /dev/null +++ b/icgparser/doml/v2/doml_v2.2.ecore @@ -0,0 +1,407 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="doml" nsURI="http://www.piacere-project.eu/doml" nsPrefix="doml"> + <eAnnotations source="emf.gen"> + <details key="basePackage" value="eu.piacere.doml"/> + <details key="fileExtensions" value="domlx"/> + <details key="complianceLevel" value="JDK80"/> + </eAnnotations> + <eSubpackages name="commons" nsURI="http://www.piacere-project.eu/doml/commons" + nsPrefix="commons"> + <eClassifiers xsi:type="ecore:EClass" name="DOMLModel" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="version" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString" + changeable="false" defaultValueLiteral="v2.1.1"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="application" eType="#//application/ApplicationLayer" + containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="infrastructure" eType="#//infrastructure/InfrastructureLayer" + containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="concretizations" upperBound="-1" + eType="#//concrete/ConcreteInfrastructure" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="optimization" eType="#//optimization/OptimizationLayer" + containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="configurations" upperBound="-1" + eType="#//commons/Configuration" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="activeConfiguration" + eType="#//commons/Configuration"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="activeInfrastructure" + eType="#//concrete/ConcreteInfrastructure"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="functionalRequirements" + upperBound="-1" eType="#//commons/Requirement" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Property" abstract="true"> + <eOperations name="getValue" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EJavaObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="key" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="reference" eType="#//commons/DOMLElement"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="IProperty" eSuperTypes="#//commons/Property"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="value" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="SProperty" eSuperTypes="#//commons/Property"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="value" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="FProperty" eSuperTypes="#//commons/Property"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="value" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="BProperty" eSuperTypes="#//commons/Property"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="value" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBooleanObject"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ExtensionElement" abstract="true"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="metaclassName" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="DOMLElement" abstract="true"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="description" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="annotations" upperBound="-1" + eType="#//commons/Property" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="contributesTo" upperBound="-1" + eType="#//commons/Requirement" eOpposite="#//commons/Requirement/predicatesOn"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Configuration" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="deployments" upperBound="-1" + eType="#//commons/Deployment" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="DeployableElement" abstract="true"/> + <eClassifiers xsi:type="ecore:EClass" name="Deployment"> + <eStructuralFeatures xsi:type="ecore:EReference" name="component" lowerBound="1" + eType="#//commons/DeployableElement"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="node" lowerBound="1" + eType="#//infrastructure/InfrastructureElement"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Requirement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="description" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="property" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="predicatesOn" upperBound="-1" + eType="#//commons/DOMLElement" eOpposite="#//commons/DOMLElement/contributesTo"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="RangedRequirement" eSuperTypes="#//commons/Requirement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="min" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="max" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="EnumeratedRequirement" eSuperTypes="#//commons/Requirement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="values" upperBound="-1" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="DeploymentRequirement" abstract="true" + eSuperTypes="#//commons/Requirement"/> + <eClassifiers xsi:type="ecore:EClass" name="DeploymentToNodeTypeRequirement" eSuperTypes="#//commons/DeploymentRequirement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="validTypes" upperBound="-1" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="DeploymentToNodeWithPropertyRequirement" + eSuperTypes="#//commons/DeploymentRequirement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="min" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="max" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="values" upperBound="-1" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="DeploymentToSpecificNodeRequirement" + eSuperTypes="#//commons/DeploymentRequirement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="validElements" upperBound="-1" + eType="#//infrastructure/InfrastructureElement"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Credentials" abstract="true" eSuperTypes="#//commons/DOMLElement"/> + <eClassifiers xsi:type="ecore:EClass" name="KeyPair" eSuperTypes="#//commons/Credentials"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="user" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="keyfile" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="algorithm" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="bits" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="UserPass" eSuperTypes="#//commons/Credentials"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="username" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="password" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Source" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="engine" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="uri" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="entry" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="backend" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="credential" eType="#//commons/Credentials" + containment="true"/> + </eClassifiers> + </eSubpackages> + <eSubpackages name="application" nsURI="http://www.piacere-project.eu/doml/application" + nsPrefix="app"> + <eClassifiers xsi:type="ecore:EClass" name="ApplicationLayer" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="components" upperBound="-1" + eType="#//application/ApplicationComponent" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ApplicationComponent" abstract="true" + eSuperTypes="#//commons/DOMLElement #//commons/DeployableElement"/> + <eClassifiers xsi:type="ecore:EClass" name="SoftwareComponent" eSuperTypes="#//application/ApplicationComponent"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="isPersistent" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBooleanObject" + defaultValueLiteral="false"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="licenseCost" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="exposedInterfaces" upperBound="-1" + eType="#//application/SoftwareInterface" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="consumedInterfaces" upperBound="-1" + eType="#//application/SoftwareInterface"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="src" eType="#//commons/Source" + containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="DBMS" eSuperTypes="#//application/SoftwareComponent"/> + <eClassifiers xsi:type="ecore:EClass" name="SaaS" eSuperTypes="#//application/ApplicationComponent"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="licenseCost" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="exposedInterfaces" upperBound="-1" + eType="#//application/SoftwareInterface" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="SaaSDBMS" eSuperTypes="#//application/SaaS"/> + <eClassifiers xsi:type="ecore:EClass" name="SoftwareInterface" eSuperTypes="#//application/ApplicationComponent"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="endPoint" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ExtApplicationComponent" eSuperTypes="#//application/ApplicationComponent #//commons/ExtensionElement"/> + </eSubpackages> + <eSubpackages name="infrastructure" nsURI="http://www.piacere-project.eu/doml/infrastructure" + nsPrefix="infra"> + <eClassifiers xsi:type="ecore:EClass" name="InfrastructureLayer" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="nodes" upperBound="-1" + eType="#//infrastructure/ComputingNode" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="generators" upperBound="-1" + eType="#//infrastructure/ComputingNodeGenerator" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="storages" upperBound="-1" + eType="#//infrastructure/Storage" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="faas" upperBound="-1" + eType="#//infrastructure/FunctionAsAService" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="credentials" upperBound="-1" + eType="#//commons/Credentials" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="groups" upperBound="-1" + eType="#//infrastructure/ComputingGroup" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="securityGroups" upperBound="-1" + eType="#//infrastructure/SecurityGroup" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="networks" upperBound="-1" + eType="#//infrastructure/Network" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ComputingGroup" abstract="true" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="groupedNodes" upperBound="-1" + eType="#//infrastructure/ComputingNode" eOpposite="#//infrastructure/ComputingNode/group"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="AutoScalingGroup" eSuperTypes="#//infrastructure/ComputingGroup"> + <eStructuralFeatures xsi:type="ecore:EReference" name="machineDefinition" lowerBound="1" + eType="#//infrastructure/VirtualMachine" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="min" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject" + defaultValueLiteral="1"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="max" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject" + defaultValueLiteral="1"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="securityGroup" eType="#//infrastructure/SecurityGroup" + containment="true"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="loadBalancer" eType="#//infrastructure/LoadBalancerKind" + defaultValueLiteral="DEFAULT"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EEnum" name="LoadBalancerKind"> + <eLiterals name="DEFAULT"/> + <eLiterals name="INTERNAL" value="1"/> + <eLiterals name="EXTERNAL" value="2"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Rule" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="kind" eType="#//infrastructure/RuleKind"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="protocol" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="fromPort" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="toPort" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="cidr" upperBound="-1" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EEnum" name="RuleKind"> + <eLiterals name="EGRESS"/> + <eLiterals name="INGRESS" value="1"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="SecurityGroup" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="rules" upperBound="-1" + eType="#//infrastructure/Rule" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="ifaces" upperBound="-1" + eType="#//infrastructure/NetworkInterface" eOpposite="#//infrastructure/NetworkInterface/associated"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="AvailabilityGroup" eSuperTypes="#//infrastructure/ComputingGroup"/> + <eClassifiers xsi:type="ecore:EClass" name="InfrastructureElement" abstract="true" + eSuperTypes="#//commons/DOMLElement #//commons/DeployableElement"/> + <eClassifiers xsi:type="ecore:EClass" name="ComputingNode" abstract="true" eSuperTypes="#//infrastructure/InfrastructureElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="architecture" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="os" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="memory_mb" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="storage" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="cpu_count" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="cost" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="ifaces" upperBound="-1" + eType="#//infrastructure/NetworkInterface" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="location" eType="#//infrastructure/Location" + containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="credentials" eType="#//commons/Credentials"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="group" eType="#//infrastructure/ComputingGroup" + eOpposite="#//infrastructure/ComputingGroup/groupedNodes"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ComputingNodeGenerator" abstract="true" + eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="uri" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="kind" eType="#//infrastructure/GeneratorKind"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EEnum" name="GeneratorKind"> + <eLiterals name="SCRIPT"/> + <eLiterals name="IMAGE" value="1"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="VMImage" eSuperTypes="#//infrastructure/ComputingNodeGenerator"> + <eStructuralFeatures xsi:type="ecore:EReference" name="generatedVMs" upperBound="-1" + eType="#//infrastructure/VirtualMachine" eOpposite="#//infrastructure/VirtualMachine/generatedFrom"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ContainerImage" eSuperTypes="#//infrastructure/ComputingNodeGenerator"> + <eStructuralFeatures xsi:type="ecore:EReference" name="generatedContainers" + upperBound="-1" eType="#//infrastructure/Container" eOpposite="#//infrastructure/Container/generatedFrom"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="PhysicalComputingNode" eSuperTypes="#//infrastructure/ComputingNode"/> + <eClassifiers xsi:type="ecore:EClass" name="VirtualMachine" eSuperTypes="#//infrastructure/ComputingNode"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="sizeDescription" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="generatedFrom" eType="#//infrastructure/VMImage" + eOpposite="#//infrastructure/VMImage/generatedVMs"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Location" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="region" lowerBound="1" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="zone" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ContainerConfig" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="container_port" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="vm_port" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EIntegerObject"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="host" eType="#//infrastructure/ComputingNode"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="iface" eType="#//infrastructure/NetworkInterface"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Container" eSuperTypes="#//infrastructure/ComputingNode"> + <eStructuralFeatures xsi:type="ecore:EReference" name="generatedFrom" eType="#//infrastructure/ContainerImage" + eOpposite="#//infrastructure/ContainerImage/generatedContainers"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="configs" upperBound="-1" + eType="#//infrastructure/ContainerConfig" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Network" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="protocol" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="addressRange" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="connectedIfaces" upperBound="-1" + eType="#//infrastructure/NetworkInterface" eOpposite="#//infrastructure/NetworkInterface/belongsTo"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="igws" upperBound="-1" + eType="#//infrastructure/InternetGateway" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="subnets" upperBound="-1" + eType="#//infrastructure/Subnet" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Subnet" eSuperTypes="#//infrastructure/Network"> + <eStructuralFeatures xsi:type="ecore:EReference" name="connectedTo" upperBound="-1" + eType="#//infrastructure/Subnet"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="NetworkInterface" eSuperTypes="#//infrastructure/InfrastructureElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="speed" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="endPoint" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="belongsTo" eType="#//infrastructure/Network" + eOpposite="#//infrastructure/Network/connectedIfaces"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="associated" eType="#//infrastructure/SecurityGroup" + eOpposite="#//infrastructure/SecurityGroup/ifaces"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="InternetGateway" eSuperTypes="#//infrastructure/NetworkInterface"/> + <eClassifiers xsi:type="ecore:EClass" name="Storage" eSuperTypes="#//infrastructure/InfrastructureElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="label" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="size_gb" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="cost" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="ifaces" upperBound="-1" + eType="#//infrastructure/NetworkInterface" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="FunctionAsAService" eSuperTypes="#//infrastructure/InfrastructureElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="cost" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="ifaces" upperBound="-1" + eType="#//infrastructure/NetworkInterface" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EEnum" name="RoleKind"> + <eLiterals name="NONE"/> + <eLiterals name="MANAGER" value="1"/> + <eLiterals name="WORKER" value="2"/> + <eLiterals name="MASTER" value="3"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="SwarmRole" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="kind" eType="#//infrastructure/RoleKind"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="nodes" upperBound="-1" + eType="#//infrastructure/ComputingNode" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Swarm" eSuperTypes="#//infrastructure/ComputingGroup"> + <eStructuralFeatures xsi:type="ecore:EReference" name="roles" upperBound="-1" + eType="#//infrastructure/SwarmRole" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ExtInfrastructureElement" eSuperTypes="#//infrastructure/InfrastructureElement #//commons/ExtensionElement"/> + </eSubpackages> + <eSubpackages name="concrete" nsURI="http://www.piacere-project.eu/doml/concrete" + nsPrefix="concrete"> + <eClassifiers xsi:type="ecore:EClass" name="ConcreteInfrastructure" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="providers" upperBound="-1" + eType="#//concrete/RuntimeProvider" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="RuntimeProvider" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="vms" upperBound="-1" + eType="#//concrete/VirtualMachine" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="vmImages" upperBound="-1" + eType="#//concrete/VMImage" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="containerImages" upperBound="-1" + eType="#//concrete/ContainerImage" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="networks" upperBound="-1" + eType="#//concrete/Network" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="storages" upperBound="-1" + eType="#//concrete/Storage" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="faas" upperBound="-1" + eType="#//concrete/FunctionAsAService" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="group" upperBound="-1" + eType="#//concrete/ComputingGroup" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ConcreteElement" abstract="true" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="configurationScript" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="preexisting" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBooleanObject" + defaultValueLiteral="false"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="VirtualMachine" eSuperTypes="#//concrete/ConcreteElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="maps" eType="#//infrastructure/VirtualMachine"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="VMImage" eSuperTypes="#//concrete/ConcreteElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="maps" eType="#//infrastructure/VMImage"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ContainerImage" eSuperTypes="#//concrete/ConcreteElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="maps" eType="#//infrastructure/ContainerImage"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Network" eSuperTypes="#//concrete/ConcreteElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="maps" eType="#//infrastructure/Network"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Storage" eSuperTypes="#//concrete/ConcreteElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="maps" eType="#//infrastructure/Storage"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="FunctionAsAService" eSuperTypes="#//concrete/ConcreteElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="maps" eType="#//infrastructure/FunctionAsAService"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ComputingGroup" eSuperTypes="#//concrete/ConcreteElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="maps" eType="#//infrastructure/ComputingGroup"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ExtConcreteElement" eSuperTypes="#//concrete/ConcreteElement #//commons/ExtensionElement"/> + </eSubpackages> + <eSubpackages name="optimization" nsURI="http://www.piacere-project.eu/doml/optimization" + nsPrefix="optimization"> + <eClassifiers xsi:type="ecore:EClass" name="OptimizationLayer" eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="startingHint" eType="#//commons/Configuration"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="solutions" upperBound="-1" + eType="#//optimization/OptimizationSolution" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="objectives" upperBound="-1" + eType="#//optimization/OptimizationObjective" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="nonfunctionalRequirements" + upperBound="-1" eType="#//commons/Requirement" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="ObjectiveValue"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="cost" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="availability" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="performance" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EFloatObject"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="OptimizationSolution" eSuperTypes="#//commons/Configuration"> + <eStructuralFeatures xsi:type="ecore:EReference" name="objectives" eType="#//optimization/ObjectiveValue" + containment="true"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="decisions" upperBound="-1" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="OptimizationObjective" abstract="true" + eSuperTypes="#//commons/DOMLElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="kind" lowerBound="1" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString" + defaultValueLiteral="Max"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="property" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="CountObjective" eSuperTypes="#//optimization/OptimizationObjective"/> + <eClassifiers xsi:type="ecore:EClass" name="MeasurableObjective" eSuperTypes="#//optimization/OptimizationObjective"/> + <eClassifiers xsi:type="ecore:EClass" name="ExtOptimizationObjective" eSuperTypes="#//optimization/OptimizationObjective #//commons/ExtensionElement"/> + </eSubpackages> +</ecore:EPackage> diff --git a/icgparser/doml/v2/nio3.domlx b/icgparser/doml/v2/nio3.domlx new file mode 100644 index 0000000..9b8a71d --- /dev/null +++ b/icgparser/doml/v2/nio3.domlx @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="ASCII"?> +<commons:DOMLModel xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:commons="http://www.piacere-project.eu/doml/commons" xmlns:infra="http://www.piacere-project.eu/doml/infrastructure" name="nio3_test_exec_env" activeInfrastructure="//@concretizations.0"> + <infrastructure name="infra"> + <nodes xsi:type="infra:VirtualMachine" name="vm1" os="centos7" memory_mb="8192.0" cpu_count="2"> + <ifaces name="i1" belongsTo="//@infrastructure/@networks.0"/> + </nodes> + <storages name="disk0" label="disk0" size_gb="40"/> + <networks name="net1" protocol="tcp/ip" addressRange="0.0.0.0/24" connectedIfaces="//@infrastructure/@nodes.0/@ifaces.0"/> + </infrastructure> + <concretizations name="con_infra"> + <providers name="openstack"> + <vms name="concrete_vm" maps="//@infrastructure/@nodes.0"/> + <networks name="concrete_net" maps="//@infrastructure/@networks.0"/> + <storages name="concrete_disk" maps="//@infrastructure/@storages.0"/> + </providers> + </concretizations> +</commons:DOMLModel> diff --git a/icgparser/doml/v2/posidonia_aws.domlx b/icgparser/doml/v2/posidonia_aws.domlx new file mode 100644 index 0000000..963d097 --- /dev/null +++ b/icgparser/doml/v2/posidonia_aws.domlx @@ -0,0 +1,133 @@ +<?xml version="1.0" encoding="ASCII"?> +<commons:DOMLModel xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:commons="http://www.piacere-project.eu/doml/commons" xmlns:infra="http://www.piacere-project.eu/doml/infrastructure" xmlns:optimization="http://www.piacere-project.eu/doml/optimization" name="posidonia" activeInfrastructure="//@concretizations.0" version="2.0"> + <annotations xsi:type="commons:SProperty" key="entorno" value="pre"/> + <annotations xsi:type="commons:SProperty" key="proyecto" value="baleares"/> + <infrastructure name="abstractInfra"> + <nodes xsi:type="infra:VirtualMachine" name="OracleDB" os="ami-02a6bfdcf8224bd77" storage="20" credentials="//@infrastructure/@credentials.3"> + <ifaces name="db1" belongsTo="//@infrastructure/@networks.0/@subnets.0"/> + <ifaces name="db2" belongsTo="//@infrastructure/@networks.0/@subnets.1"/> + <ifaces name="db3" belongsTo="//@infrastructure/@networks.0/@subnets.2"/> + </nodes> + <credentials xsi:type="infra:KeyPair" name="GestautKeyName" keyfile="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD3F6tyPEFEzV0LX3X8BsXdMsQz1x2cEikKDEY0aIj41qgxMCP/iteneqXSIFZBp5vizPvaoIR3Um9xK7PGoW8giupGn+EPuxIA4cDM4vzOqOkiMPhz5XK0whEjkVzTo4+S0puvDZuwIsdiW9mxhJc7tgBNL0cYlWSYVkz4G/fslNfRPW5mYAM49f4fhtxPb5ok4Q2Lg9dPKVHO/Bgeu5woMc7RY0p1ej6D4CKFE6lymSDJpW0YHX/wqE9+cfEauh7xZcG0q9t2ta6F6fmX0agvpFyZo8aFbXeUBr7osSCJNgvavWbM/06niWrOvYX2xwWdhXmXSrbX8ZbabVohBK41 email@example.com" algorithm="RSA" bits="4096"/> + <credentials xsi:type="infra:KeyPair" name="ESKeyName" keyfile="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD3F6tyPEFEzV0LX3X8BsXdMsQz1x2cEikKDEY0aIj41qgxMCP/iteneqXSIFZBp5vizPvaoIR3Um9xK7PGoW8giupGn+EPuxIA4cDM4vzOqOkiMPhz5XK0whEjkVzTo4+S0puvDZuwIsdiW9mxhJc7tgBNL0cYlWSYVkz4G/fslNfRPW5mYAM49f4fhtxPb5ok4Q2Lg9dPKVHO/Bgeu5woMc7RY0p1ej6D4CKFE6lymSDJpW0YHX/wqE9+cfEauh7xZcG0q9t2ta6F6fmX0agvpFyZo8aFbXeUBr7osSCJNgvavWbM/06niWrOvYX2xwWdhXmXSrbX8ZbabVohBK41 email@example.com" algorithm="RSA" bits="4096"/> + <credentials xsi:type="infra:KeyPair" name="EdiKeyName" keyfile="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD3F6tyPEFEzV0LX3X8BsXdMsQz1x2cEikKDEY0aIj41qgxMCP/iteneqXSIFZBp5vizPvaoIR3Um9xK7PGoW8giupGn+EPuxIA4cDM4vzOqOkiMPhz5XK0whEjkVzTo4+S0puvDZuwIsdiW9mxhJc7tgBNL0cYlWSYVkz4G/fslNfRPW5mYAM49f4fhtxPb5ok4Q2Lg9dPKVHO/Bgeu5woMc7RY0p1ej6D4CKFE6lymSDJpW0YHX/wqE9+cfEauh7xZcG0q9t2ta6F6fmX0agvpFyZo8aFbXeUBr7osSCJNgvavWbM/06niWrOvYX2xwWdhXmXSrbX8ZbabVohBK41 email@example.com" algorithm="RSA" bits="4096"/> + <credentials xsi:type="infra:UserPass" name="dbCredentials" username="balearesadm" password="balearesadm"/> + <groups xsi:type="infra:AutoScalingGroup" name="gestaut_asg" deploymentNetwork="//@infrastructure/@networks.0"> + <machineDefinition name="gestaut_vm" os="ami-02a6bfdcf8224bd77" credentials="//@infrastructure/@credentials.0"> + <ifaces name="db1" belongsTo="//@infrastructure/@networks.0/@subnets.0" associated="//@infrastructure/@securityGroups.0"/> + <ifaces name="db2" belongsTo="//@infrastructure/@networks.0/@subnets.1"/> + <ifaces name="db3" belongsTo="//@infrastructure/@networks.0/@subnets.2"/> + </machineDefinition> + </groups> + <groups xsi:type="infra:AutoScalingGroup" name="elasticsearch_asg" deploymentNetwork="//@infrastructure/@networks.0"> + <machineDefinition name="elasticsearch_vm" os="ami-02a6bfdcf8224bd77" credentials="//@infrastructure/@credentials.1"> + <ifaces name="db1" belongsTo="//@infrastructure/@networks.0/@subnets.0"/> + <ifaces name="db2" belongsTo="//@infrastructure/@networks.0/@subnets.1"/> + <ifaces name="db3" belongsTo="//@infrastructure/@networks.0/@subnets.2"/> + </machineDefinition> + </groups> + <groups xsi:type="infra:AutoScalingGroup" name="edi_asg" deploymentNetwork="//@infrastructure/@networks.0"> + <machineDefinition name="edi_vm" os="ami-02a6bfdcf8224bd77" credentials="//@infrastructure/@credentials.2"> + <ifaces name="db1" belongsTo="//@infrastructure/@networks.0/@subnets.0"/> + <ifaces name="db2" belongsTo="//@infrastructure/@networks.0/@subnets.1"/> + <ifaces name="db3" belongsTo="//@infrastructure/@networks.0/@subnets.2"/> + </machineDefinition> + </groups> + <securityGroups name="sg" ifaces="//@infrastructure/@groups.0/@machineDefinition/@ifaces.0"> + <rules name="salida" protocol="-1" fromPort="0" toPort="0"> + <cidr>0.0.0.0/0</cidr> + </rules> + <rules name="lb" kind="INGRESS" protocol="tcp" fromPort="80" toPort="80"> + <cidr>10.100.1.0/24</cidr> + <cidr>10.100.2.0/24</cidr> + <cidr>10.100.3.0/24</cidr> + </rules> + <rules name="es" kind="INGRESS" protocol="tcp" fromPort="9200" toPort="9200"> + <cidr>10.100.1.0/24</cidr> + <cidr>10.100.2.0/24</cidr> + <cidr>10.100.3.0/24</cidr> + </rules> + <rules name="monitor" kind="INGRESS" protocol="tcp" fromPort="6556" toPort="6556"> + <cidr>54.217.119.81/32</cidr> + </rules> + <rules name="ftp" kind="INGRESS" protocol="tcp" fromPort="22" toPort="22"> + <cidr>213.96.27.139/32</cidr> + <cidr>37.187.173.88/32</cidr> + <cidr>51.89.40.59/32</cidr> + <cidr>195.53.242.200/32</cidr> + </rules> + </securityGroups> + <securityGroups name="dbsg"> + <rules name="salida" protocol="-1" fromPort="0" toPort="0"> + <cidr>0.0.0.0/0</cidr> + </rules> + <rules name="ora" kind="INGRESS" protocol="tcp" fromPort="1521" toPort="1521"> + <cidr>10.100.1.0/24</cidr> + <cidr>10.100.2.0/24</cidr> + <cidr>10.100.3.0/24</cidr> + <cidr>84.124.78.66/32</cidr> + </rules> + </securityGroups> + <securityGroups name="elbsg"> + <rules name="salida" protocol="-1" fromPort="0" toPort="0"> + <cidr>0.0.0.0/0</cidr> + </rules> + <rules name="http" kind="INGRESS" protocol="tcp" fromPort="80" toPort="80"> + <cidr>0.0.0.0/0</cidr> + <cidr>::/0</cidr> + </rules> + <rules name="https" kind="INGRESS" protocol="tcp" fromPort="443" toPort="443"> + <cidr>0.0.0.0/0</cidr> + <cidr>::/0</cidr> + </rules> + <rules name="es" kind="INGRESS" protocol="tcp" fromPort="9200" toPort="9200"> + <cidr>10.100.1.0/24</cidr> + <cidr>10.100.2.0/24</cidr> + <cidr>10.100.3.0/24</cidr> + </rules> + </securityGroups> + <securityGroups name="checkmk"> + <rules name="salida" protocol="-1" fromPort="0" toPort="0"> + <cidr>0.0.0.0/0</cidr> + </rules> + <rules name="http" kind="INGRESS" protocol="tcp" fromPort="80" toPort="80"> + <cidr>84.124.78.66/32</cidr> + </rules> + <rules name="https" kind="INGRESS" protocol="tcp" fromPort="443" toPort="443"> + <cidr>84.124.78.66/32</cidr> + </rules> + <rules name="ftp" kind="INGRESS" protocol="tcp" fromPort="22" toPort="22"> + <cidr>84.124.78.66/32</cidr> + </rules> + </securityGroups> + <networks name="vpc" protocol="tcp/ip" addressRange="10.100.0.0/16"> + <igws name="internet"/> + <subnets name="subnet1" protocol="tcp/ip" addressRange="10.100.1.0/24" connectedIfaces="//@infrastructure/@nodes.0/@ifaces.0 //@infrastructure/@groups.0/@machineDefinition/@ifaces.0 //@infrastructure/@groups.1/@machineDefinition/@ifaces.0 //@infrastructure/@groups.2/@machineDefinition/@ifaces.0"/> + <subnets name="subnet2" protocol="tcp/ip" addressRange="10.100.2.0/24" connectedIfaces="//@infrastructure/@nodes.0/@ifaces.1 //@infrastructure/@groups.0/@machineDefinition/@ifaces.1 //@infrastructure/@groups.1/@machineDefinition/@ifaces.1 //@infrastructure/@groups.2/@machineDefinition/@ifaces.1"/> + <subnets name="subnet3" protocol="tcp/ip" addressRange="10.100.3.0/24" connectedIfaces="//@infrastructure/@nodes.0/@ifaces.2 //@infrastructure/@groups.0/@machineDefinition/@ifaces.2 //@infrastructure/@groups.1/@machineDefinition/@ifaces.2 //@infrastructure/@groups.2/@machineDefinition/@ifaces.2"/> + </networks> + </infrastructure> + <concretizations name="dev"> + <providers name="aws"> + <vms name="concrete_oracle_db" maps="//@infrastructure/@nodes.0"> + <annotations xsi:type="commons:SProperty" key="instance_type" value="t2.micro"/> + </vms> + <vms name="concrete_gestaut_vm" maps="//@infrastructure/@groups.0/@machineDefinition"> + <annotations xsi:type="commons:SProperty" key="instance_type" value="t2.micro"/> + </vms> + <vms name="elasticsearch_vm" maps="//@infrastructure/@groups.1/@machineDefinition"> + <annotations xsi:type="commons:SProperty" key="instance_type" value="t2.micro"/> + </vms> + <vms name="edi_vm" maps="//@infrastructure/@groups.2/@machineDefinition"/> + <networks name="concrete_vpc" maps="//@infrastructure/@networks.0"> + <annotations xsi:type="commons:SProperty" key="instance_type" value="t2.micro"/> + </networks> + </providers> + </concretizations> + <optimization name="opt"> + <objectives xsi:type="optimization:MeasurableObjective" kind="min" property="cost"/> + <objectives xsi:type="optimization:MeasurableObjective" kind="max" property="availability"/> + <nonfunctionalRequirements xsi:type="commons:RangedRequirement" name="req1" description="Cost <= 70.0" property="cost" max="70.0"/> + <nonfunctionalRequirements xsi:type="commons:RangedRequirement" name="req2" description="Availability >= 66.5%" property="availability" min="66.5"/> + </optimization> +</commons:DOMLModel> diff --git a/icgparser/doml/v2/posidonia_openstack.domlx b/icgparser/doml/v2/posidonia_openstack.domlx new file mode 100644 index 0000000..3e7b3c5 --- /dev/null +++ b/icgparser/doml/v2/posidonia_openstack.domlx @@ -0,0 +1,127 @@ +<?xml version="1.0" encoding="ASCII"?> +<commons:DOMLModel xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:commons="http://www.piacere-project.eu/doml/commons" xmlns:infra="http://www.piacere-project.eu/doml/infrastructure" xmlns:optimization="http://www.piacere-project.eu/doml/optimization" name="posidonia_openstack" activeInfrastructure="//@concretizations.0"> + <annotations xsi:type="commons:SProperty" key="entorno" value="pre"/> + <annotations xsi:type="commons:SProperty" key="proyecto" value="baleares"/> + <infrastructure name="abstractInfra"> + <nodes xsi:type="infra:VirtualMachine" name="OracleDB" os="Ubuntu-Focal-20.04-Daily-2022-04-19" storage="20" credentials="//@infrastructure/@credentials.3"> + <ifaces name="db" belongsTo="//@infrastructure/@networks.0"/> + </nodes> + <credentials xsi:type="infra:KeyPair" name="GestautKeyName" user="gestaut" algorithm="RSA" bits="4096"/> + <credentials xsi:type="infra:KeyPair" name="ESKeyName" user="es" keyfile="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD3F6tyPEFEzV0LX3X8BsXdMsQz1x2cEikKDEY0aIj41qgxMCP/iteneqXSIFZBp5vizPvaoIR3Um9xK7PGoW8giupGn+EPuxIA4cDM4vzOqOkiMPhz5XK0whEjkVzTo4+S0puvDZuwIsdiW9mxhJc7tgBNL0cYlWSYVkz4G/fslNfRPW5mYAM49f4fhtxPb5ok4Q2Lg9dPKVHO/Bgeu5woMc7RY0p1ej6D4CKFE6lymSDJpW0YHX/wqE9+cfEauh7xZcG0q9t2ta6F6fmX0agvpFyZo8aFbXeUBr7osSCJNgvavWbM/06niWrOvYX2xwWdhXmXSrbX8ZbabVohBK41 email@example.com" algorithm="RSA" bits="4096"/> + <credentials xsi:type="infra:KeyPair" name="EdiKeyName" user="edi" keyfile="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD3F6tyPEFEzV0LX3X8BsXdMsQz1x2cEikKDEY0aIj41qgxMCP/iteneqXSIFZBp5vizPvaoIR3Um9xK7PGoW8giupGn+EPuxIA4cDM4vzOqOkiMPhz5XK0whEjkVzTo4+S0puvDZuwIsdiW9mxhJc7tgBNL0cYlWSYVkz4G/fslNfRPW5mYAM49f4fhtxPb5ok4Q2Lg9dPKVHO/Bgeu5woMc7RY0p1ej6D4CKFE6lymSDJpW0YHX/wqE9+cfEauh7xZcG0q9t2ta6F6fmX0agvpFyZo8aFbXeUBr7osSCJNgvavWbM/06niWrOvYX2xwWdhXmXSrbX8ZbabVohBK41 email@example.com" algorithm="RSA" bits="4096"/> + <credentials xsi:type="infra:KeyPair" name="DbKeyName" user="oracledb" keyfile="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD3F6tyPEFEzV0LX3X8BsXdMsQz1x2cEikKDEY0aIj41qgxMCP/iteneqXSIFZBp5vizPvaoIR3Um9xK7PGoW8giupGn+EPuxIA4cDM4vzOqOkiMPhz5XK0whEjkVzTo4+S0puvDZuwIsdiW9mxhJc7tgBNL0cYlWSYVkz4G/fslNfRPW5mYAM49f4fhtxPb5ok4Q2Lg9dPKVHO/Bgeu5woMc7RY0p1ej6D4CKFE6lymSDJpW0YHX/wqE9+cfEauh7xZcG0q9t2ta6F6fmX0agvpFyZo8aFbXeUBr7osSCJNgvavWbM/06niWrOvYX2xwWdhXmXSrbX8ZbabVohBK41 email@example.com" algorithm="RSA" bits="4096"/> + <groups xsi:type="infra:AutoScalingGroup" name="gestaut_asg" deploymentNetwork="//@infrastructure/@networks.0"> + <machineDefinition name="gestaut_vm" os="Ubuntu-Focal-20.04-Daily-2022-04-19" credentials="//@infrastructure/@credentials.0"> + <ifaces name="gestaut" belongsTo="//@infrastructure/@networks.0"/> + </machineDefinition> + </groups> + <groups xsi:type="infra:AutoScalingGroup" name="elasticsearch_asg" deploymentNetwork="//@infrastructure/@networks.0"> + <machineDefinition name="elasticsearch_vm" os="Ubuntu-Focal-20.04-Daily-2022-04-19" credentials="//@infrastructure/@credentials.1"> + <ifaces name="elasticsearch" belongsTo="//@infrastructure/@networks.0"/> + </machineDefinition> + </groups> + <groups xsi:type="infra:AutoScalingGroup" name="edi_asg" deploymentNetwork="//@infrastructure/@networks.0"> + <machineDefinition name="edi_vm" os="Ubuntu-Focal-20.04-Daily-2022-04-19" credentials="//@infrastructure/@credentials.2"> + <ifaces name="edi" belongsTo="//@infrastructure/@networks.0"/> + </machineDefinition> + </groups> + <securityGroups name="sg"> + <rules name="salida" protocol="-1" fromPort="0" toPort="0"> + <cidr>0.0.0.0/0</cidr> + </rules> + <rules name="lb" kind="INGRESS" protocol="tcp" fromPort="80" toPort="80"> + <cidr>10.100.1.0/24</cidr> + <cidr>10.100.2.0/24</cidr> + <cidr>10.100.3.0/24</cidr> + </rules> + <rules name="es" kind="INGRESS" protocol="tcp" fromPort="9200" toPort="9200"> + <cidr>10.100.1.0/24</cidr> + <cidr>10.100.2.0/24</cidr> + <cidr>10.100.3.0/24</cidr> + </rules> + <rules name="monitor" kind="INGRESS" protocol="tcp" fromPort="6556" toPort="6556"> + <cidr>54.217.119.81/32</cidr> + </rules> + <rules name="ftp" kind="INGRESS" protocol="tcp" fromPort="22" toPort="22"> + <cidr>213.96.27.139/32</cidr> + <cidr>37.187.173.88/32</cidr> + <cidr>51.89.40.59/32</cidr> + <cidr>195.53.242.200/32</cidr> + </rules> + </securityGroups> + <securityGroups name="dbsg"> + <rules name="salida" protocol="-1" fromPort="0" toPort="0"> + <cidr>0.0.0.0/0</cidr> + </rules> + <rules name="ora" kind="INGRESS" protocol="tcp" fromPort="1521" toPort="1521"> + <cidr>10.100.1.0/24</cidr> + <cidr>10.100.2.0/24</cidr> + <cidr>10.100.3.0/24</cidr> + <cidr>84.124.78.66/32</cidr> + </rules> + </securityGroups> + <securityGroups name="elbsg"> + <rules name="salida" protocol="-1" fromPort="0" toPort="0"> + <cidr>0.0.0.0/0</cidr> + </rules> + <rules name="http" kind="INGRESS" protocol="tcp" fromPort="80" toPort="80"> + <cidr>0.0.0.0/0</cidr> + <cidr>::/0</cidr> + </rules> + <rules name="https" kind="INGRESS" protocol="tcp" fromPort="443" toPort="443"> + <cidr>0.0.0.0/0</cidr> + <cidr>::/0</cidr> + </rules> + <rules name="es" kind="INGRESS" protocol="tcp" fromPort="9200" toPort="9200"> + <cidr>10.100.1.0/24</cidr> + <cidr>10.100.2.0/24</cidr> + <cidr>10.100.3.0/24</cidr> + </rules> + </securityGroups> + <securityGroups name="checkmk"> + <rules name="salida" protocol="-1" fromPort="0" toPort="0"> + <cidr>0.0.0.0/0</cidr> + </rules> + <rules name="http" kind="INGRESS" protocol="tcp" fromPort="80" toPort="80"> + <cidr>84.124.78.66/32</cidr> + </rules> + <rules name="https" kind="INGRESS" protocol="tcp" fromPort="443" toPort="443"> + <cidr>84.124.78.66/32</cidr> + </rules> + <rules name="ftp" kind="INGRESS" protocol="tcp" fromPort="22" toPort="22"> + <cidr>84.124.78.66/32</cidr> + </rules> + </securityGroups> + <networks name="vpc" protocol="tcp/ip" addressRange="10.100.0.0/16" connectedIfaces="//@infrastructure/@nodes.0/@ifaces.0 //@infrastructure/@groups.0/@machineDefinition/@ifaces.0 //@infrastructure/@groups.1/@machineDefinition/@ifaces.0 //@infrastructure/@groups.2/@machineDefinition/@ifaces.0"> + <igws name="internet"/> + <subnets name="subnet1" protocol="tcp/ip" addressRange="10.100.1.0/24"/> + <subnets name="subnet2" protocol="tcp/ip" addressRange="10.100.2.0/24"/> + <subnets name="subnet3" protocol="tcp/ip" addressRange="10.100.3.0/24"/> + </networks> + </infrastructure> + <concretizations name="dev"> + <providers name="openstack"> + <vms name="concrete_oracle_db" maps="//@infrastructure/@nodes.0"> + <annotations xsi:type="commons:SProperty" key="vm_flavor" value="small"/> + </vms> + <vms name="concrete_gestaut_vm" maps="//@infrastructure/@groups.0/@machineDefinition"> + <annotations xsi:type="commons:SProperty" key="vm_flavor" value="small"/> + </vms> + <vms name="elasticsearch_vm" maps="//@infrastructure/@groups.1/@machineDefinition"> + <annotations xsi:type="commons:SProperty" key="vm_flavor" value="small"/> + </vms> + <vms name="edi_vm" maps="//@infrastructure/@groups.2/@machineDefinition"> + <annotations xsi:type="commons:SProperty" key="vm_flavor" value="small"/> + </vms> + <networks name="concrete_vpc" maps="//@infrastructure/@networks.0"> + <annotations xsi:type="commons:SProperty" key="vm_flavor" value="small"/> + </networks> + </providers> + </concretizations> + <optimization name="opt"> + <objectives xsi:type="optimization:MeasurableObjective" kind="min" property="cost"/> + <objectives xsi:type="optimization:MeasurableObjective" kind="max" property="availability"/> + <nonfunctionalRequirements xsi:type="commons:RangedRequirement" name="req1" description="Cost <= 70.0" property="cost" max="70.0"/> + <nonfunctionalRequirements xsi:type="commons:RangedRequirement" name="req2" description="Availability >= 66.5%" property="availability" min="66.5"/> + </optimization> +</commons:DOMLModel> diff --git a/icgparser/doml/v2/uc3.domlx b/icgparser/doml/v2/uc3.domlx new file mode 100644 index 0000000..aecc4e5 --- /dev/null +++ b/icgparser/doml/v2/uc3.domlx @@ -0,0 +1,116 @@ +<?xml version="1.0" encoding="ASCII"?> +<commons:DOMLModel xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:app="http://www.piacere-project.eu/doml/application" xmlns:commons="http://www.piacere-project.eu/doml/commons" xmlns:infra="http://www.piacere-project.eu/doml/infrastructure" xmlns:optimization="http://www.piacere-project.eu/doml/optimization" name="uc3_openstack" activeConfiguration="//@configurations.0" activeInfrastructure="//@concretizations.0"> + <application name="app"> + <components xsi:type="app:SoftwareComponent" name="iwg"> + <exposedInterfaces name="net_info"/> + </components> + <components xsi:type="app:SoftwareComponent" name="osint" consumedInterfaces="//@application/@components.0/@exposedInterfaces.0 //@application/@components.3/@exposedInterfaces.0 //@application/@components.2/@exposedInterfaces.0"> + <exposedInterfaces name="osint_info"/> + </components> + <components xsi:type="app:SoftwareComponent" name="ewcf" consumedInterfaces="//@application/@components.4/@exposedInterfaces.0"> + <exposedInterfaces name="ewcf_rest_interface"/> + </components> + <components xsi:type="app:SaaS" name="external_twitter"> + <exposedInterfaces name="get_twitter" endPoint="https://twitter_api/get"/> + </components> + <components xsi:type="app:SaaS" name="external_firebase"> + <exposedInterfaces name="get_firebase" endPoint="https://firebase_api/get"/> + </components> + </application> + <infrastructure name="infra"> + <generators xsi:type="infra:VMImage" name="v_img1" generatedVMs="//@infrastructure/@groups.1/@machineDefinition"/> + <generators xsi:type="infra:VMImage" name="v_img2" generatedVMs="//@infrastructure/@groups.0/@machineDefinition"/> + <generators xsi:type="infra:VMImage" name="v_img3" generatedVMs="//@infrastructure/@groups.2/@machineDefinition"/> + <credentials xsi:type="infra:KeyPair" name="ssh_key" user="ubuntu" keyfile="/home/user1/.ssh/openstack.key" algorithm="RSA" bits="4096"/> + <groups xsi:type="infra:AutoScalingGroup" name="igw_ag" deploymentNetwork="//@infrastructure/@networks.1"> + <machineDefinition name="igw_vm" os="ubuntu-20.04.3" credentials="//@infrastructure/@credentials.0" generatedFrom="//@infrastructure/@generators.1"> + <ifaces name="igw_vm_net2" belongsTo="//@infrastructure/@networks.1"/> + <ifaces name="igw_vm_oam" belongsTo="//@infrastructure/@networks.3"/> + </machineDefinition> + </groups> + <groups xsi:type="infra:AutoScalingGroup" name="osint_ag" deploymentNetwork="//@infrastructure/@networks.0"> + <machineDefinition name="osint_vm" os="ubuntu-20.04.3" credentials="//@infrastructure/@credentials.0" generatedFrom="//@infrastructure/@generators.0"> + <ifaces name="osint_vm_net1" belongsTo="//@infrastructure/@networks.0"/> + <ifaces name="osint_vm_oam" belongsTo="//@infrastructure/@networks.3"/> + <ifaces name="osint_igw_port" endPoint="127.0.0.1:5000" belongsTo="//@infrastructure/@networks.0"/> + </machineDefinition> + </groups> + <groups xsi:type="infra:AutoScalingGroup" name="ewcf_ag" deploymentNetwork="//@infrastructure/@networks.2"> + <machineDefinition name="ewcf_vm" os="ubuntu-20.04.3" credentials="//@infrastructure/@credentials.0" generatedFrom="//@infrastructure/@generators.2"> + <ifaces name="ewcf_vm_net3" belongsTo="//@infrastructure/@networks.2"/> + <ifaces name="ewcf_vm_oam" belongsTo="//@infrastructure/@networks.3"/> + </machineDefinition> + </groups> + <securityGroups name="sg"> + <rules name="icmp" protocol="icmp" fromPort="-1" toPort="-1"> + <cidr>0.0.0.0/0</cidr> + </rules> + <rules name="http" kind="INGRESS" protocol="tcp" fromPort="80" toPort="80"> + <cidr>0.0.0.0/0</cidr> + </rules> + <rules name="https" kind="INGRESS" protocol="tcp" fromPort="443" toPort="443"> + <cidr>0.0.0.0/0</cidr> + </rules> + <rules name="ssh" kind="INGRESS" protocol="tcp" fromPort="22" toPort="22"> + <cidr>0.0.0.0/0</cidr> + </rules> + </securityGroups> + <networks name="net1" protocol="tcp/ip" addressRange="16.0.0.0/24" connectedIfaces="//@infrastructure/@groups.1/@machineDefinition/@ifaces.0 //@infrastructure/@groups.1/@machineDefinition/@ifaces.2"/> + <networks name="net2" protocol="tcp/ip" addressRange="16.0.1.0/24" connectedIfaces="//@infrastructure/@groups.0/@machineDefinition/@ifaces.0"/> + <networks name="net3" protocol="tcp/ip" addressRange="16.0.2.0/24" connectedIfaces="//@infrastructure/@groups.2/@machineDefinition/@ifaces.0"/> + <networks name="oam" protocol="tcp/ip" addressRange="16.0.4.0/24" connectedIfaces="//@infrastructure/@groups.0/@machineDefinition/@ifaces.1 //@infrastructure/@groups.1/@machineDefinition/@ifaces.1 //@infrastructure/@groups.2/@machineDefinition/@ifaces.1"/> + </infrastructure> + <concretizations name="con_infra"> + <providers name="openstack"> + <vms name="concrete_osint_vm" maps="//@infrastructure/@groups.1/@machineDefinition"> + <annotations xsi:type="commons:SProperty" key="vm_name" value="osint"/> + <annotations xsi:type="commons:SProperty" key="vm_flavor" value="small"/> + <annotations xsi:type="commons:SProperty" key="vm_key_name" value="user1"/> + </vms> + <vms name="concrete_igw_vm" maps="//@infrastructure/@groups.0/@machineDefinition"> + <annotations xsi:type="commons:SProperty" key="vm_name" value="igw"/> + <annotations xsi:type="commons:SProperty" key="vm_flavor" value="small"/> + <annotations xsi:type="commons:SProperty" key="vm_key_name" value="user1"/> + </vms> + <vms name="concrete_ewcf_vm" maps="//@infrastructure/@groups.2/@machineDefinition"> + <annotations xsi:type="commons:SProperty" key="vm_name" value="ewcf"/> + <annotations xsi:type="commons:SProperty" key="vm_flavor" value="small"/> + <annotations xsi:type="commons:SProperty" key="vm_key_name" value="user1"/> + </vms> + <vmImages name="concrete_vm_image1" maps="//@infrastructure/@generators.0"> + <annotations xsi:type="commons:SProperty" key="name" value="ubuntu-20.04.3"/> + </vmImages> + <vmImages name="concrete_vm_image2" maps="//@infrastructure/@generators.1"> + <annotations xsi:type="commons:SProperty" key="name" value="ubuntu-20.04.3"/> + </vmImages> + <vmImages name="concrete_vm_image3" maps="//@infrastructure/@generators.2"> + <annotations xsi:type="commons:SProperty" key="name" value="ubuntu-20.04.3"/> + </vmImages> + <networks name="concrete_net1" maps="//@infrastructure/@networks.0"> + <annotations xsi:type="commons:SProperty" key="name" value="uc3_net1"/> + </networks> + <networks name="concrete_net2" maps="//@infrastructure/@networks.1"> + <annotations xsi:type="commons:SProperty" key="name" value="uc3_net2"/> + </networks> + <networks name="concrete_net3" maps="//@infrastructure/@networks.2"> + <annotations xsi:type="commons:SProperty" key="name" value="uc3_net3"/> + </networks> + <networks name="concrete_net4" maps="//@infrastructure/@networks.3"> + <annotations xsi:type="commons:SProperty" key="name" value="uc3_net4"/> + </networks> + </providers> + </concretizations> + <optimization name="opt"> + <objectives xsi:type="optimization:MeasurableObjective" kind="min" property="cost"/> + <nonfunctionalRequirements xsi:type="commons:RangedRequirement" name="req1" description="Cost <= 200" property="cost" max="200.0"/> + <nonfunctionalRequirements xsi:type="commons:EnumeratedRequirement" name="req2" description="Provider" property="provider"> + <values>AMAZ</values> + </nonfunctionalRequirements> + <nonfunctionalRequirements name="req3" description="elements" property="VM"/> + </optimization> + <configurations name="config"> + <deployments component="//@application/@components.1" node="//@infrastructure/@groups.1/@machineDefinition"/> + <deployments component="//@application/@components.0" node="//@infrastructure/@groups.0/@machineDefinition"/> + <deployments component="//@application/@components.2" node="//@infrastructure/@groups.2/@machineDefinition"/> + </configurations> +</commons:DOMLModel> diff --git a/icgparser/doml/v2/wordpress.domlx b/icgparser/doml/v2/wordpress.domlx new file mode 100644 index 0000000..67e6d9e --- /dev/null +++ b/icgparser/doml/v2/wordpress.domlx @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="ASCII"?> +<commons:DOMLModel xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:app="http://www.piacere-project.eu/doml/application" xmlns:commons="http://www.piacere-project.eu/doml/commons" xmlns:infra="http://www.piacere-project.eu/doml/infrastructure" name="wordpress" activeConfiguration="//@configurations.0" activeInfrastructure="//@concretizations.0"> + <application name="app"> + <components xsi:type="app:SoftwareComponent" name="mysql"> + <annotations xsi:type="commons:SProperty" key="db_user" value="app1user"/> + <annotations xsi:type="commons:SProperty" key="db_password" value="app1user"/> + <annotations xsi:type="commons:SProperty" key="db_name" value="app1"/> + <exposedInterfaces name="DB_interface"/> + </components> + <components xsi:type="app:SoftwareComponent" name="wordpress" consumedInterfaces="//@application/@components.0/@exposedInterfaces.0"> + <annotations xsi:type="commons:SProperty" key="wordpress_db_host" value="db_host"/> + <annotations xsi:type="commons:SProperty" key="wordpress_db_user" value="app1user"/> + <annotations xsi:type="commons:SProperty" key="wordpress_db_password" value="app1user"/> + <annotations xsi:type="commons:SProperty" key="wordpress_db_name" value="app1"/> + <annotations xsi:type="commons:SProperty" key="wordpress_table_prefix" value="wp"/> + </components> + </application> + <infrastructure name="infra"> + <nodes xsi:type="infra:VirtualMachine" name="vm1" os="ubuntu-20.04.3" credentials="//@infrastructure/@credentials.0"> + <ifaces name="i1" belongsTo="//@infrastructure/@networks.0"/> + </nodes> + <nodes xsi:type="infra:VirtualMachine" name="vm2" os="ubuntu-20.04.3" credentials="//@infrastructure/@credentials.0"> + <ifaces name="i2" belongsTo="//@infrastructure/@networks.0"/> + </nodes> + <networks name="net1" protocol="tcp/ip" addressRange="10.10.10.0/24" connectedIfaces="//@infrastructure/@nodes.0/@ifaces.0 //@infrastructure/@nodes.1/@ifaces.0"/> + <credentials xsi:type="infra:KeyPair" name="ssh_key" keyfile="local path to ssh key"/> + </infrastructure> + <concretizations name="con_os_infra"> + <providers name="openstack"> + <vms name="concrete_vm1" maps="//@infrastructure/@nodes.0"> + <annotations xsi:type="commons:SProperty" key="vm_flavor" value="small-centos"/> + </vms> + <vms name="concrete_vm2" maps="//@infrastructure/@nodes.1"> + <annotations xsi:type="commons:SProperty" key="vm_flavor" value="small-centos"/> + </vms> + <networks name="concrete_net" maps="//@infrastructure/@networks.0"/> + </providers> + </concretizations> + <concretizations name="con_aws_infra"> + <providers name="aws"> + <vms name="concrete_vm1" maps="//@infrastructure/@nodes.0"> + <annotations xsi:type="commons:SProperty" key="vm_flavor" value="t2.micro"/> + </vms> + <vms name="concrete_vm2" maps="//@infrastructure/@nodes.1"> + <annotations xsi:type="commons:SProperty" key="vm_flavor" value="t2.micro"/> + </vms> + <networks name="concrete_net" maps="//@infrastructure/@networks.0"/> + </providers> + </concretizations> + <configurations name="config"> + <deployments component="//@application/@components.0" node="//@infrastructure/@nodes.0"/> + <deployments component="//@application/@components.1" node="//@infrastructure/@nodes.1"/> + </configurations> +</commons:DOMLModel> diff --git a/icgparser/test_ModelResources.py b/icgparser/test_ModelResources.py new file mode 100644 index 0000000..1d962b5 --- /dev/null +++ b/icgparser/test_ModelResources.py @@ -0,0 +1,27 @@ +import unittest + +from icgparser.ModelResourcesUtilities import ModelResources, from_model_resources_to_ir_names_version1, \ + ModelResourcesUtilities + + +class MyTestCase(unittest.TestCase): + def test_ModelResources_returns_value_number(self): + first_enum = ModelResources.STEP_NAME.value + self.assertEqual(first_enum, 1) + + def test_ModelResources_returns_string_name(self): + first_enum = ModelResources.STEP_NAME.name + self.assertIsInstance(first_enum, str) + + def test_get_ir_key_name_from_model_resource_returns_doml_version_1_resourcename(self): + ir_key = from_model_resources_to_ir_names_version1(ModelResources.STEPS) + self.assertIsNotNone(ir_key) + + def test_convert_doml_version_into_integer_returns_float(self): + doml_version = "1.0" + model_resources = ModelResourcesUtilities(doml_version) + self.assertEqual(model_resources.convert_doml_version_into_integer(), 1) + + +if __name__ == '__main__': + unittest.main() diff --git a/input_file_example/nginx/parameter.json b/input_file_example/nginx/parameter.json deleted file mode 100644 index 37c1f8e..0000000 --- a/input_file_example/nginx/parameter.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "output_path": "output_files_generated/nginx_openstack/", - "steps": [ - { - "programming_language": "terraform", - "data": { - "provider": "openstack", - "vm": [{ - "name": "nginx-host", - "flavor": "small", - "vm_security_groups": "default", - "ssh_user": "ubuntu", - "ssh_key_file": "/home/user1/.ssh/openstack.key", - "address": "16.0.0.1", - "image": "ubuntu-20.04.3", - "network_name": "ostack2" - }], - "net": [{ - "name": "ostack2", - "address": "16.0.0.0/24", - "protocol": "tcp/ip", - "rules_name": ["rule_1", "rule_2"] - }], - "sg": [{ - "name": "rule_1", - "from_port": 80, - "to_port": 80, - "ip_protocol": "tcp", - "ipv6_cidr_blocks": "0.0.0.0/0" - }, { - "name": "rule_2", - "from_port": 22, - "to_port": 22, - "ip_protocol": "tcp", - "ipv6_cidr_blocks": "0.0.0.0/0" - }] - } - }, - { - "programming_language": "ansible", - "data": { - "operating_system": "ubuntu", - "nginx": { - "ssh_user": "ubuntu", - "ssh_key_file": "/home/user1/.ssh/openstack.key", - "address": "16.0.0.1", - "source_code": [ - "/var/www/html/index.html", - "/usr/share/nginx/html/index.html" - ] - } - } - } - ] -} \ No newline at end of file diff --git a/input_file_example/wordpress/parameters.json b/input_file_example/wordpress/parameters.json deleted file mode 100644 index 45a7d27..0000000 --- a/input_file_example/wordpress/parameters.json +++ /dev/null @@ -1,77 +0,0 @@ -{ - "output_path": "output_files_generated/wordpress_azure/", - "steps": [ - { - "programming_language": "terraform", - "data": { - "provider": "azure", - "resource_group": [ - { - "name": "wordpress-rg", - "location": "eastus" - } - ], - "vms": [ - { - "resource_group_name": "wordpress-example", - "name": "wordpress-vm", - "size": "Standard_B1s", - "admin_username": "adminuser", - "admin_password": "P@$$w0rd1234!", - "ssh_user": "adminuser", - "ssh_key_file": "/ssh_keys/wordpress_rsa.pub", - "image_offer": "UbuntuServer", - "image_sku": "18.04-LTS", - "network_name": "wordpress_net" - }, - { - "resource_group_name": "wordpress-example", - "name": "myql-vm", - "size": "Standard_B1s", - "admin_username": "adminuser", - "admin_password": "P@$$w0rd1234!", - "ssh_user": "adminuser", - "ssh_key_file": "/ssh_keys/wordpress_rsa.pub", - "image_offer": "UbuntuServer", - "image_sku": "18.04-LTS", - "network_name": "mysql_net" - } - ], - "net": [ - { - "resource_group_name": "wordpress-example", - "name": "wordpress_net" - }, - { - "resource_group_name": "mysql-example", - "name": "mysql_net" - } - ] - } - }, - { - "programming_language": "ansible", - "data": { - "operating_system": "ubuntu", - "mysql": { - "db_user": "app1user", - "db_password": "app1user", - "db_name": "app1" - } - } - }, - { - "programming_language": "ansible", - "data": { - "operating_system": "ubuntu", - "wordpress": { - "wordpress_db_host": "10.10.10.10", - "wordpress_db_user": "app1user", - "wordpress_db_password": "app1user", - "wordpress_db_name": "app1", - "wordpress_table_prefix": "wp" - } - } - } - ] -} \ No newline at end of file diff --git a/input_file_generated/ir.json b/input_file_generated/ir.json index 91bfefb..e36cae6 100644 --- a/input_file_generated/ir.json +++ b/input_file_generated/ir.json @@ -1,89 +1,186 @@ { - "output_path": "output_files_generated/nginx_openstack/", + "output_path": "output_files_generated/nio3_test_exec_env/", "steps": [ { "data": { - "computingGroup": [ + "credentials": [ + { + "algorithm": "RSA", + "bits": 4096, + "infra_element_name": "ssh_key" + } + ], + "networks": [ { - "http": { - "cidr": [ - "0.0.0.0/0" - ], - "fromPort": 80, - "kind": "INGRESS", - "name": "http", - "protocol": "tcp", - "toPort": 80 + "InternetGateway_g1": { + "address": "10.83.18.65", + "name": "g1" + }, + "addressRange": "/24", + "dc": { + "gname": "PIACDC", + "name": "dc", + "preexisting": true, + "type": "vsphere_datacenter" }, - "https": { - "cidr": [ - "0.0.0.0/0" - ], - "fromPort": 443, - "kind": "INGRESS", - "name": "https", - "protocol": "tcp", - "toPort": 443 + "infra_element_name": "net1", + "maps": "net1", + "name": "network", + "preexisting": true, + "protocol": "tcp/ip", + "vsphere_network_name": "Nested-ESXi" + } + ], + "provider_info": [ + { + "allow_unverified_ssl": true, + "password": "vc_password", + "provider_name": "vsphere", + "username": "vc_username", + "vsphere_server": "psvc10000002.cd.sigov.si" + } + ], + "resources": [ + { + "gname": "PIACDC", + "name": "dc", + "preexisting": true, + "type": "vsphere_datacenter" + }, + { + "dc": { + "gname": "PIACDC", + "name": "dc", + "preexisting": true, + "type": "vsphere_datacenter" }, - "icmp": { - "cidr": [ - "0.0.0.0/0" - ], - "fromPort": -1, - "kind": "EGRESS", - "name": "icmp", - "protocol": "icmp", - "toPort": -1 + "gname": "PIACC", + "name": "cl", + "preexisting": true, + "type": "vsphere_compute_cluster" + }, + { + "dc": { + "gname": "PIACDC", + "name": "dc", + "preexisting": true, + "type": "vsphere_datacenter" }, - "infra_element_name": "sg", - "ssh": { - "cidr": [ - "0.0.0.0/0" - ], - "fromPort": 22, - "kind": "INGRESS", - "name": "ssh", - "protocol": "tcp", - "toPort": 22 - } + "gname": "Piacere", + "name": "pool", + "preexisting": true, + "type": "vsphere_resource_pool" } ], - "credentials": [ + "storages": [ { - "infra_element_name": "user1", - "user": "user1" + "dc": { + "gname": "PIACDC", + "name": "dc", + "preexisting": true, + "type": "vsphere_datacenter" + }, + "infra_element_name": "disk0", + "label": "disk0", + "maps": "disk0", + "name": "datastore", + "preexisting": true, + "size_gb": 100, + "vsphere_datastore_name": "NFSShare01" } ], - "networks": [ + "vmImages": [ { - "addressRange": "16.0.0.0/24", - "infra_element_name": "net1", - "infra_sgs": [ - "icmp", - "http", - "https", - "ssh" - ], - "name": "concrete_net", - "protocol": "tcp/ip" + "dc": { + "gname": "PIACDC", + "name": "dc", + "preexisting": true, + "type": "vsphere_datacenter" + }, + "infra_element_name": "img", + "kind": "SCRIPT", + "maps": "img", + "name": "template", + "preexisting": true, + "vsphere_virtual_machine_name": "c7tmp" } ], - "provider": "openstack", "vms": [ { - "credentials": "user1", - "group": "sg", - "i1": { - "associated": "sg", + "NetworkInterface_i1": { "belongsTo": "net1", - "endPoint": "16.0.0.1", + "endPoint": "10.83.18.92", "name": "i1" }, + "cpu_count": 2, + "credentials": "ssh_key", + "datastore": { + "maps": "disk0", + "name": "datastore", + "preexisting": true + }, + "disabledMonitorings": [], + "disk": "disk0", + "disk_size": 100, + "domain": "tri.lan", + "generatedFrom": "img", + "guest_id": "centos7_64Guest", + "host_name": "simpa-test00-piacere", "infra_element_name": "vm1", - "name": "concrete_vm", - "os": "Ubuntu-Focal-20.04-Daily-2022-04-19", - "vm_flavor": "small", - "vm_name": "nginx-host" + "maps": "vm1", + "memory_mb": 1024.0, + "name": "con_vm1", + "os": "centos7_64Guest", + "pool": { + "gname": "Piacere", + "name": "pool", + "preexisting": true, + "type": "vsphere_resource_pool" + }, + "preexisting": false, + "template": { + "maps": "img", + "name": "template", + "preexisting": true + } + }, + { + "NetworkInterface_i1": { + "belongsTo": "net1", + "endPoint": "10.83.18.88", + "name": "i1" + }, + "cpu_count": 2, + "credentials": "ssh_key", + "datastore": { + "maps": "disk0", + "name": "datastore", + "preexisting": true + }, + "disabledMonitorings": [], + "disk": "disk1", + "disk_size": 100, + "domain": "tri.lan", + "generatedFrom": "img", + "guest_id": "centos7_64Guest", + "host_name": "simpa-test00-piacere", + "infra_element_name": "vm2", + "maps": "vm2", + "memory_mb": 1024.0, + "name": "con_vm2", + "os": "centos7_64Guest", + "pool": { + "gname": "Piacere", + "name": "pool", + "preexisting": true, + "type": "vsphere_resource_pool" + }, + "preexisting": false, + "template": { + "maps": "img", + "name": "template", + "preexisting": true + } } ] }, @@ -91,52 +188,183 @@ }, { "data": { - "piacere_monitoring": { - "name": "piacere_monitoring", - "node": { - "credentials": "user1", - "group": "sg", - "i1": { - "associated": "sg", - "belongsTo": "net1", - "endPoint": "16.0.0.1", - "name": "i1" + "performance_monitoring": { + "name": "performance_monitoring", + "nodes": [ + { + "NetworkInterface_i1": { + "belongsTo": "net1", + "endPoint": "10.83.18.92", + "name": "i1" + }, + "cpu_count": 2, + "credentials": "ssh_key", + "datastore": { + "maps": "disk0", + "name": "datastore", + "preexisting": true + }, + "disabledMonitorings": [], + "disk": "disk0", + "disk_size": 100, + "domain": "tri.lan", + "generatedFrom": "img", + "guest_id": "centos7_64Guest", + "host_name": "simpa-test00-piacere", + "infra_element_name": "vm1", + "maps": "vm1", + "memory_mb": 1024.0, + "name": "con_vm1", + "os": "centos7_64Guest", + "pool": { + "gname": "Piacere", + "name": "pool", + "preexisting": true, + "type": "vsphere_resource_pool" + }, + "preexisting": false, + "template": { + "maps": "img", + "name": "template", + "preexisting": true + } }, - "infra_element_name": "vm1", - "name": "concrete_vm", - "os": "Ubuntu-Focal-20.04-Daily-2022-04-19", - "vm_flavor": "small", - "vm_name": "nginx-host" - } + { + "NetworkInterface_i1": { + "belongsTo": "net1", + "endPoint": "10.83.18.88", + "name": "i1" + }, + "cpu_count": 2, + "credentials": "ssh_key", + "datastore": { + "maps": "disk0", + "name": "datastore", + "preexisting": true + }, + "disabledMonitorings": [], + "disk": "disk1", + "disk_size": 100, + "domain": "tri.lan", + "generatedFrom": "img", + "guest_id": "centos7_64Guest", + "host_name": "simpa-test00-piacere", + "infra_element_name": "vm2", + "maps": "vm2", + "memory_mb": 1024.0, + "name": "con_vm2", + "os": "centos7_64Guest", + "pool": { + "gname": "Piacere", + "name": "pool", + "preexisting": true, + "type": "vsphere_resource_pool" + }, + "preexisting": false, + "template": { + "maps": "img", + "name": "template", + "preexisting": true + } + } + ] } }, "programming_language": "ansible", - "step_name": "piacere_monitoring" + "step_name": "performance_monitoring" }, { "data": { - "nginx": { - "name": "nginx", - "node": { - "credentials": "user1", - "group": "sg", - "i1": { - "associated": "sg", - "belongsTo": "net1", - "endPoint": "16.0.0.1", - "name": "i1" + "security_monitoring": { + "name": "security_monitoring", + "nodes": [ + { + "NetworkInterface_i1": { + "belongsTo": "net1", + "endPoint": "10.83.18.92", + "name": "i1" + }, + "cpu_count": 2, + "credentials": "ssh_key", + "datastore": { + "maps": "disk0", + "name": "datastore", + "preexisting": true + }, + "disabledMonitorings": [], + "disk": "disk0", + "disk_size": 100, + "domain": "tri.lan", + "generatedFrom": "img", + "guest_id": "centos7_64Guest", + "host_name": "simpa-test00-piacere", + "infra_element_name": "vm1", + "maps": "vm1", + "memory_mb": 1024.0, + "name": "con_vm1", + "os": "centos7_64Guest", + "pool": { + "gname": "Piacere", + "name": "pool", + "preexisting": true, + "type": "vsphere_resource_pool" + }, + "preexisting": false, + "template": { + "maps": "img", + "name": "template", + "preexisting": true + } }, - "infra_element_name": "vm1", - "name": "concrete_vm", - "os": "Ubuntu-Focal-20.04-Daily-2022-04-19", - "vm_flavor": "small", - "vm_name": "nginx-host" - }, - "source_code": "/usr/share/nginx/html/index.html" + { + "NetworkInterface_i1": { + "belongsTo": "net1", + "endPoint": "10.83.18.88", + "name": "i1" + }, + "cpu_count": 2, + "credentials": "ssh_key", + "datastore": { + "maps": "disk0", + "name": "datastore", + "preexisting": true + }, + "disabledMonitorings": [], + "disk": "disk1", + "disk_size": 100, + "domain": "tri.lan", + "generatedFrom": "img", + "guest_id": "centos7_64Guest", + "host_name": "simpa-test00-piacere", + "infra_element_name": "vm2", + "maps": "vm2", + "memory_mb": 1024.0, + "name": "con_vm2", + "os": "centos7_64Guest", + "pool": { + "gname": "Piacere", + "name": "pool", + "preexisting": true, + "type": "vsphere_resource_pool" + }, + "preexisting": false, + "template": { + "maps": "img", + "name": "template", + "preexisting": true + } + } + ] } }, "programming_language": "ansible", - "step_name": "nginx" + "step_name": "security_monitoring" + }, + { + "data": { + "containerImages": [] + }, + "programming_language": "docker-compose" } ] } \ No newline at end of file diff --git a/main.py b/main.py index 2766ca7..6f4687c 100644 --- a/main.py +++ b/main.py @@ -45,8 +45,8 @@ logging.getLogger().setLevel(logging.INFO) # Parse parameters # ------------------------------------------------------------------------- skip_next = False -doml_directory = "./icgparser/doml/v1" -model_filename = "icgparser/doml/v1/nginx-openstack_v1.domlx" +doml_directory = "./icgparser/doml/v2" +model_filename = "icgparser/doml/v2/posidonia_openstack.domlx" load_split_model = False output_file_name = "iac_files.tar.gz" diff --git a/output_file_example/gitlab_1/config.yaml b/output_file_example/gitlab_1/config.yaml new file mode 100644 index 0000000..90e5a8c --- /dev/null +++ b/output_file_example/gitlab_1/config.yaml @@ -0,0 +1,7 @@ +--- +iac: +- terraform +- piacere_monitoring +- git +- portainer +... \ No newline at end of file diff --git a/output_file_example/gitlab_1/git/config.yaml b/output_file_example/gitlab_1/git/config.yaml new file mode 100644 index 0000000..0a994ba --- /dev/null +++ b/output_file_example/gitlab_1/git/config.yaml @@ -0,0 +1,11 @@ +## https://git.code.tecnalia.com/piacere/private/t31-doml-concepts/-/blob/main/case-studies-DOML-models/SIMPA_Validation_v2.doml + +--- +input: + - GIT_USER + - GIT_PASSWORD + - git.code.tecnalia.com/piacere/private/wp7-use-cases/uc1.si-mpa/-/tree/main/deploy-nio +output: + - ansible/provision.yml +engine: git +... diff --git a/output_files_generated/nginx_openstack/piacere_monitoring/config.yaml b/output_file_example/gitlab_1/portainer/config.yaml similarity index 79% rename from output_files_generated/nginx_openstack/piacere_monitoring/config.yaml rename to output_file_example/gitlab_1/portainer/config.yaml index 8b7284e..12a0d05 100644 --- a/output_files_generated/nginx_openstack/piacere_monitoring/config.yaml +++ b/output_file_example/gitlab_1/portainer/config.yaml @@ -1,8 +1,8 @@ - --- input: - instance_ip_vm1 - instance_server_private_key_user1 + - ansible/provision.yml output: [] engine: ansible ... diff --git a/output_file_example/nginx_openstack_with_agents/terraform/agents_playbook/ansible/playbooks/pma/ansible_requirements.yml b/output_file_example/nginx_openstack_with_agents/terraform/agents_playbook/ansible/playbooks/pma/ansible_requirements.yml index 58c0cb3..47808cf 100644 --- a/output_file_example/nginx_openstack_with_agents/terraform/agents_playbook/ansible/playbooks/pma/ansible_requirements.yml +++ b/output_file_example/nginx_openstack_with_agents/terraform/agents_playbook/ansible/playbooks/pma/ansible_requirements.yml @@ -1,8 +1,8 @@ roles: # - name: dj-wasabi.telegraf -# version: 0.13.2 +# version: 0.13.3 # source: https://galaxy.ansible.com - name: dj-wasabi.telegraf src: https://github.com/dj-wasabi/ansible-telegraf.git scm: git - version: 0.13.2 + version: 0.13.3 diff --git a/output_file_example/nginx_openstack_with_agents_new/ansible/config.yaml b/output_file_example/nginx_openstack_with_agents_new/ansible/config.yaml new file mode 100644 index 0000000..fff1792 --- /dev/null +++ b/output_file_example/nginx_openstack_with_agents_new/ansible/config.yaml @@ -0,0 +1,7 @@ +--- +input: + - instance_ip + - instance_server_public_key +output: [] +engine: ansible +... diff --git a/output_file_example/nginx_openstack_with_agents_new/ansible/inventory.j2 b/output_file_example/nginx_openstack_with_agents_new/ansible/inventory.j2 new file mode 100644 index 0000000..fb27937 --- /dev/null +++ b/output_file_example/nginx_openstack_with_agents_new/ansible/inventory.j2 @@ -0,0 +1,7 @@ +[vms] +{{ instance_ip }} + +[vms:vars] +ansible_connection=ssh +ansible_user=ubuntu #vm user variable potentialy +ansible_ssh_private_key_file=ssh_key \ No newline at end of file diff --git a/output_file_example/nginx_openstack_with_agents_new/ansible/ssh_key.j2 b/output_file_example/nginx_openstack_with_agents_new/ansible/ssh_key.j2 new file mode 100644 index 0000000..9d5f53a --- /dev/null +++ b/output_file_example/nginx_openstack_with_agents_new/ansible/ssh_key.j2 @@ -0,0 +1 @@ +{{ instance_server_public_key }} \ No newline at end of file diff --git a/output_file_example/nginx_openstack_with_agents_new/config.yaml b/output_file_example/nginx_openstack_with_agents_new/config.yaml new file mode 100644 index 0000000..c4ece8f --- /dev/null +++ b/output_file_example/nginx_openstack_with_agents_new/config.yaml @@ -0,0 +1,6 @@ +--- +iac: + - terraform + - monitoring + - ansible +... \ No newline at end of file diff --git a/output_file_example/nginx_openstack_with_agents_new/monitoring/ansible.cfg b/output_file_example/nginx_openstack_with_agents_new/monitoring/ansible.cfg new file mode 100644 index 0000000..660a5eb --- /dev/null +++ b/output_file_example/nginx_openstack_with_agents_new/monitoring/ansible.cfg @@ -0,0 +1,7 @@ +# https://docs.ansible.com/ansible/latest/reference_appendices/config.html +[defaults] +host_key_checking = False +inventory = {{CWD}}/hosts.yaml ; This points to the file that lists your hosts +remote_user = esilab +deprecation_warnings=False ; to remove the python version depretation warning +display_skipped_hosts = no \ No newline at end of file diff --git a/output_file_example/nginx_openstack_with_agents_new/monitoring/ansible_requirements.yml b/output_file_example/nginx_openstack_with_agents_new/monitoring/ansible_requirements.yml new file mode 100644 index 0000000..47808cf --- /dev/null +++ b/output_file_example/nginx_openstack_with_agents_new/monitoring/ansible_requirements.yml @@ -0,0 +1,8 @@ +roles: +# - name: dj-wasabi.telegraf +# version: 0.13.3 +# source: https://galaxy.ansible.com + - name: dj-wasabi.telegraf + src: https://github.com/dj-wasabi/ansible-telegraf.git + scm: git + version: 0.13.3 diff --git a/output_file_example/nginx_openstack_with_agents_new/monitoring/config.yaml b/output_file_example/nginx_openstack_with_agents_new/monitoring/config.yaml new file mode 100644 index 0000000..fff1792 --- /dev/null +++ b/output_file_example/nginx_openstack_with_agents_new/monitoring/config.yaml @@ -0,0 +1,7 @@ +--- +input: + - instance_ip + - instance_server_public_key +output: [] +engine: ansible +... diff --git a/output_file_example/nginx_openstack_with_agents_new/monitoring/hosts.yaml b/output_file_example/nginx_openstack_with_agents_new/monitoring/hosts.yaml new file mode 100644 index 0000000..b9cbfc6 --- /dev/null +++ b/output_file_example/nginx_openstack_with_agents_new/monitoring/hosts.yaml @@ -0,0 +1,4 @@ +all: + hosts: + localhost: + ansible_connection: local diff --git a/output_file_example/nginx_openstack_with_agents_new/monitoring/install_playbook_requirements.sh b/output_file_example/nginx_openstack_with_agents_new/monitoring/install_playbook_requirements.sh new file mode 100644 index 0000000..843bf3b --- /dev/null +++ b/output_file_example/nginx_openstack_with_agents_new/monitoring/install_playbook_requirements.sh @@ -0,0 +1,33 @@ +#!/bin/bash +set -e + +SCRIPT_DIR=$(dirname "$0") + +# to avoid the being run in a world writable directory we explicitly assign the ANSIBLE_CONFIG variable +if [[ -f ./ansible.cfg ]] +then + export ANSIBLE_CONFIG=./ansible.cfg +else + if [[ -f $SCRIPT_DIR/ansible.cfg ]] + then + export ANSIBLE_CONFIG=$SCRIPT_DIR/ansible.cfg + fi +fi + +if [[ -z "$ANSIBLE_CONFIG" ]] +then + echo ANSIBLE_CONFIG to assigned using default https://docs.ansible.com/ansible/latest/reference_appendices/config.html +else + echo ANSIBLE_CONFIG=$ANSIBLE_CONFIG +fi + +if [[ -z "$1" ]] +then + # echo without params + echo ansible-playbook $SCRIPT_DIR/site_requirements.yaml + ansible-playbook $SCRIPT_DIR/site_requirements.yaml +else + # echo with params + echo ansible-playbook $SCRIPT_DIR/site_requirements.yaml --extra-vars "$1" + ansible-playbook $SCRIPT_DIR/site_requirements.yaml --extra-vars "$1" +fi diff --git a/output_file_example/nginx_openstack_with_agents_new/monitoring/inventory.j2 b/output_file_example/nginx_openstack_with_agents_new/monitoring/inventory.j2 new file mode 100644 index 0000000..fb27937 --- /dev/null +++ b/output_file_example/nginx_openstack_with_agents_new/monitoring/inventory.j2 @@ -0,0 +1,7 @@ +[vms] +{{ instance_ip }} + +[vms:vars] +ansible_connection=ssh +ansible_user=ubuntu #vm user variable potentialy +ansible_ssh_private_key_file=ssh_key \ No newline at end of file diff --git a/templates/ansible/ubuntu/piacere_main.tpl b/output_file_example/nginx_openstack_with_agents_new/monitoring/main.yml similarity index 100% rename from templates/ansible/ubuntu/piacere_main.tpl rename to output_file_example/nginx_openstack_with_agents_new/monitoring/main.yml diff --git a/output_file_example/nginx_openstack_with_agents_new/monitoring/run-playbook.sh b/output_file_example/nginx_openstack_with_agents_new/monitoring/run-playbook.sh new file mode 100644 index 0000000..f2bba22 --- /dev/null +++ b/output_file_example/nginx_openstack_with_agents_new/monitoring/run-playbook.sh @@ -0,0 +1,33 @@ +#!/bin/bash +set -e + +SCRIPT_DIR=$(dirname "$0") + +# to avoid the being run in a world writable directory we explicitly assign the ANSIBLE_CONFIG variable +if [[ -f ./ansible.cfg ]] +then + export ANSIBLE_CONFIG=./ansible.cfg +else + if [[ -f $SCRIPT_DIR/ansible.cfg ]] + then + export ANSIBLE_CONFIG=$SCRIPT_DIR/ansible.cfg + fi +fi + +if [[ -z "$ANSIBLE_CONFIG" ]] +then + echo ANSIBLE_CONFIG to assigned using default https://docs.ansible.com/ansible/latest/reference_appendices/config.html +else + echo ANSIBLE_CONFIG=$ANSIBLE_CONFIG +fi + +if [[ -z "$1" ]] +then + # echo without params + echo ansible-playbook $SCRIPT_DIR/site.yaml + ansible-playbook $SCRIPT_DIR/site.yaml +else + # echo with params + echo ansible-playbook $SCRIPT_DIR/site.yaml --extra-vars "$1" + ansible-playbook $SCRIPT_DIR/site.yaml --extra-vars "$1" +fi diff --git a/output_file_example/nginx_openstack_with_agents_new/monitoring/site.yaml b/output_file_example/nginx_openstack_with_agents_new/monitoring/site.yaml new file mode 100644 index 0000000..531dbf5 --- /dev/null +++ b/output_file_example/nginx_openstack_with_agents_new/monitoring/site.yaml @@ -0,0 +1,30 @@ +- hosts: all + pre_tasks: + - name: Check parameters + fail: + msg: 'variable {{item}} not defined' + when: item is not defined + with_items: + - pma_deployment_id + - pma_influxdb_bucket + - pma_influxdb_token + - pma_influxdb_org + - pma_influxdb_addr + - name: Print parameters + debug: + msg: + - "pma_deployment_id: {{ pma_deployment_id }}" + - "pma_influxdb_bucket: {{ pma_influxdb_bucket }}" + - "pma_influxdb_token: {{ pma_influxdb_token }}" + - "pma_influxdb_org: {{ pma_influxdb_org }}" + - "pma_influxdb_addr: {{ pma_influxdb_addr }}" + - name: Ensure gnupg package + package: + name: gnupg + state: present + become: true + + vars_files: + - vars/main.yaml + roles: + - dj-wasabi.telegraf diff --git a/output_file_example/nginx_openstack_with_agents_new/monitoring/site_requirements.yaml b/output_file_example/nginx_openstack_with_agents_new/monitoring/site_requirements.yaml new file mode 100644 index 0000000..3e7665d --- /dev/null +++ b/output_file_example/nginx_openstack_with_agents_new/monitoring/site_requirements.yaml @@ -0,0 +1,9 @@ +- hosts: localhost + tasks: + - name: print disclamer + debug: + msg: this can also be done with "ansible-galaxy install -r requirements" + - name: install telegraf from galaxy + community.general.ansible_galaxy_install: + type: role + requirements_file: ansible_requirements.yml diff --git a/output_file_example/nginx_openstack_with_agents_new/monitoring/ssh_key.j2 b/output_file_example/nginx_openstack_with_agents_new/monitoring/ssh_key.j2 new file mode 100644 index 0000000..9d5f53a --- /dev/null +++ b/output_file_example/nginx_openstack_with_agents_new/monitoring/ssh_key.j2 @@ -0,0 +1 @@ +{{ instance_server_public_key }} \ No newline at end of file diff --git a/output_file_example/nginx_openstack_with_agents_new/monitoring/vars/main.yaml b/output_file_example/nginx_openstack_with_agents_new/monitoring/vars/main.yaml new file mode 100644 index 0000000..861faf3 --- /dev/null +++ b/output_file_example/nginx_openstack_with_agents_new/monitoring/vars/main.yaml @@ -0,0 +1,27 @@ +pma_deployment_id: "123e4567-e89b-12d3-a456-426614174002" +pma_influxdb_bucket: "bucket" +pma_influxdb_token: "piacerePassword" +pma_influxdb_org: "piacere" +pma_influxdb_addr: "https://influxdb.pm.ci.piacere.digital.tecnalia.dev" + +telegraf_agent_package_state: latest + +telegraf_agent_output: + - type: influxdb_v2 + config: + - urls = ["{{ pma_influxdb_addr }}"] + - token = "{{ pma_influxdb_token }}" + - organization = "{{ pma_influxdb_org }}" + - bucket = "{{ pma_influxdb_bucket }}" + - insecure_skip_verify = true + +telegraf_global_tags: + - tag_name: deployment_id + tag_value: "{{ pma_deployment_id }}" + +telegraf_plugins_default: + - plugin: cpu + - plugin: mem + - plugin: processes + - plugin: disk + - plugin: net \ No newline at end of file diff --git a/output_file_example/nginx_openstack_with_agents_new/terraform/config.yaml b/output_file_example/nginx_openstack_with_agents_new/terraform/config.yaml new file mode 100644 index 0000000..3cadda9 --- /dev/null +++ b/output_file_example/nginx_openstack_with_agents_new/terraform/config.yaml @@ -0,0 +1,8 @@ +--- +engine: terraform +input: [] +output: + - instance_server_public_key + - instance_server_private_key + - instance_ip +... diff --git a/output_file_example/nginx_openstack_with_agents_new/terraform/main.tf b/output_file_example/nginx_openstack_with_agents_new/terraform/main.tf new file mode 100644 index 0000000..75b5e23 --- /dev/null +++ b/output_file_example/nginx_openstack_with_agents_new/terraform/main.tf @@ -0,0 +1,116 @@ +terraform { +required_version = ">= 0.14.0" + required_providers { + openstack = { + source = "terraform-provider-openstack/openstack" + version = "~> 1.35.0" + } + } +} + +# Configure the OpenStack Provider +provider "openstack" { + insecure = true +} + +# Retrieve data +data "openstack_networking_network_v2" "external" { + name = "external" +} + +data "openstack_identity_project_v3" "test_tenant" { + name = "admin" +} + +data "openstack_networking_secgroup_v2" "default" { + name = "default" + tenant_id = data.openstack_identity_project_v3.test_tenant.id +} +# Create virtual machine +resource "openstack_compute_instance_v2" "nginx" { + name = "nginx-host" + image_name = "ubuntu-18.04" + flavor_name = "m1.tiny" + key_pair = openstack_compute_keypair_v2.user_key.name + network { + port = openstack_networking_port_v2.nginx.id + } +} + +# Create ssh keys +resource "openstack_compute_keypair_v2" "user_key" { + name = "user1" +} + +# Create floating ip +resource "openstack_networking_floatingip_v2" "nginx" { + pool = "external" + +} + +# Attach floating ip to instance +resource "openstack_compute_floatingip_associate_v2" "nginx" { + floating_ip = openstack_networking_floatingip_v2.nginx.address + instance_id = openstack_compute_instance_v2.nginx.id +} + +## Network + +# Create Network +resource "openstack_networking_network_v2" "generic" { + name = " " +} + +# Create Subnet +resource "openstack_networking_subnet_v2" "nginx" { + name = "subnet-nginx" + network_id = openstack_networking_network_v2.generic.id + cidr = "16.0.0.0/24" + dns_nameservers = ["8.8.8.8", "8.8.8.4"] +} + +# Attach networking port +resource "openstack_networking_port_v2" "nginx" { + name = "nginx" + network_id = openstack_networking_network_v2.generic.id + admin_state_up = true + security_group_ids = [ + data.openstack_networking_secgroup_v2.default.id #default flavour id + ] + fixed_ip { + subnet_id = openstack_networking_subnet_v2.nginx.id + } +} + +# Router creation. UUID external gateway +resource "openstack_networking_router_v2" "generic" { + name = "router-generic" + external_network_id = data.openstack_networking_network_v2.external.id #External network id +} +# Router interface configuration +resource "openstack_networking_router_interface_v2" "nginx" { + router_id = openstack_networking_router_v2.generic.id + subnet_id = openstack_networking_subnet_v2.nginx.id +} + +resource "openstack_compute_secgroup_v2" "http" { + name = "http" + description = "Open input http port" + rule { + from_port = 80 + to_port = 80 + ip_protocol = "tcp" + cidr = "0.0.0.0/0" + } +} + +resource "openstack_compute_secgroup_v2" "ssh" { + name = "ssh" + description = "Open input ssh port" + rule { + from_port = 22 + to_port = 22 + ip_protocol = "tcp" + cidr = "0.0.0.0/0" + } +} diff --git a/output_file_example/nginx_openstack_with_agents_new/terraform/output.tf b/output_file_example/nginx_openstack_with_agents_new/terraform/output.tf new file mode 100644 index 0000000..c04554d --- /dev/null +++ b/output_file_example/nginx_openstack_with_agents_new/terraform/output.tf @@ -0,0 +1,11 @@ +output "instance_server_public_key" { + value = openstack_compute_keypair_v2.user_key.public_key +} + +output "instance_server_private_key" { + value = openstack_compute_keypair_v2.user_key.private_key +} + +output "instance_ip" { + value = openstack_compute_floatingip_associate_v2.nginx.floating_ip +} \ No newline at end of file diff --git a/output_files_generated/nginx_openstack/config.yaml b/output_files_generated/nginx_openstack/config.yaml index 082e5e5..c1ab35f 100644 --- a/output_files_generated/nginx_openstack/config.yaml +++ b/output_files_generated/nginx_openstack/config.yaml @@ -1,6 +1,7 @@ --- iac: - terraform -- piacere_monitoring +- performance_monitoring +- security_monitoring - nginx ... \ No newline at end of file diff --git a/output_files_generated/nginx_openstack/nginx/config.yaml b/output_files_generated/nginx_openstack/nginx/config.yaml index 8b7284e..5d4a476 100644 --- a/output_files_generated/nginx_openstack/nginx/config.yaml +++ b/output_files_generated/nginx_openstack/nginx/config.yaml @@ -1,8 +1,8 @@ --- input: - - instance_ip_vm1 - - instance_server_private_key_user1 + - instance_server_public_ip_nginx_vm + - instance_server_private_key_ubuntu_nginx_vm output: [] engine: ansible ... diff --git a/output_files_generated/nginx_openstack/nginx/inventory.j2 b/output_files_generated/nginx_openstack/nginx/inventory.j2 index c869825..6b3e902 100644 --- a/output_files_generated/nginx_openstack/nginx/inventory.j2 +++ b/output_files_generated/nginx_openstack/nginx/inventory.j2 @@ -1,7 +1,7 @@ [servers_for_nginx] -{{ instance_ip_vm1 }} +{{ instance_server_public_ip_nginx_vm }} [servers_for_nginx:vars] ansible_connection=ssh diff --git a/output_files_generated/nginx_openstack/nginx/ssh_key.j2 b/output_files_generated/nginx_openstack/nginx/ssh_key.j2 index 1431f67..b3880a6 100644 --- a/output_files_generated/nginx_openstack/nginx/ssh_key.j2 +++ b/output_files_generated/nginx_openstack/nginx/ssh_key.j2 @@ -1 +1,2 @@ -{{ instance_server_private_key_user1 }} +{{ instance_server_private_key_ubuntu_nginx_vm}} + diff --git a/output_files_generated/nginx_openstack/piacere_monitoring/ssh_key.j2 b/output_files_generated/nginx_openstack/piacere_monitoring/ssh_key.j2 deleted file mode 100644 index 1431f67..0000000 --- a/output_files_generated/nginx_openstack/piacere_monitoring/ssh_key.j2 +++ /dev/null @@ -1 +0,0 @@ -{{ instance_server_private_key_user1 }} diff --git a/output_files_generated/nginx_openstack/terraform/config.yaml b/output_files_generated/nginx_openstack/terraform/config.yaml index 783f272..5baf71f 100644 --- a/output_files_generated/nginx_openstack/terraform/config.yaml +++ b/output_files_generated/nginx_openstack/terraform/config.yaml @@ -9,8 +9,8 @@ input: - OS_PROJECT_NAME output: - - instance_server_public_key_user1 - - instance_server_private_key_user1 - - instance_ip_vm1 + - instance_server_public_key_ubuntu_nginx_vm + - instance_server_private_key_ubuntu_nginx_vm + - instance_server_public_ip_nginx_vm ... diff --git a/output_files_generated/nginx_openstack/terraform/main.tf b/output_files_generated/nginx_openstack/terraform/main.tf index 40789ea..7517686 100644 --- a/output_files_generated/nginx_openstack/terraform/main.tf +++ b/output_files_generated/nginx_openstack/terraform/main.tf @@ -21,128 +21,112 @@ data "openstack_networking_network_v2" "external" { } + # Create virtual machine -resource "openstack_compute_instance_v2" "vm1" { - name = "nginx-host" +resource "openstack_compute_instance_v2" "nginx_vm" { + name = "nginx_host" image_name = "Ubuntu-Focal-20.04-Daily-2022-04-19" - flavor_name = "small" - key_pair = openstack_compute_keypair_v2.user1.name - network { - port = openstack_networking_port_v2.net1.id + flavor_name = "small-centos" + key_pair = openstack_compute_keypair_v2.ubuntu.name + network { + port = openstack_networking_port_v2.i1_networking_port.id + } } # Create floating ip -resource "openstack_networking_floatingip_v2" "vm1_floating_ip" { +resource "openstack_networking_floatingip_v2" "nginx_vm_floating_ip" { pool = "external" # fixed_ip = "" } # Attach floating ip to instance -resource "openstack_compute_floatingip_associate_v2" "vm1_floating_ip_association" { - floating_ip = openstack_networking_floatingip_v2.vm1_floating_ip.address - instance_id = openstack_compute_instance_v2.vm1.id +resource "openstack_compute_floatingip_associate_v2" "nginx_vm_floating_ip_association" { + floating_ip = openstack_networking_floatingip_v2.nginx_vm_floating_ip.address + instance_id = openstack_compute_instance_v2.nginx_vm.id } +# Router interface configuration + +resource "openstack_networking_router_interface_v2" "subnet1_router_interface" { + router_id = openstack_networking_router_v2.router.id + subnet_id = openstack_networking_subnet_v2.subnet1_subnet.id +} + + +# Attach networking port +resource "openstack_networking_port_v2" "i1_networking_port" { + name = "i1" + network_id = openstack_networking_network_v2.net1.id + admin_state_up = true + security_group_ids = [ openstack_compute_secgroup_v2.sg.id ] + fixed_ip { + subnet_id = openstack_networking_subnet_v2.subnet1_subnet.id + } +} + + ## Network # Create Network resource "openstack_networking_network_v2" "net1" { - name = "concrete_net" + name = "nginx_net" } -# Create Subnet -resource "openstack_networking_subnet_v2" "net1_subnet" { - name = "concrete_net_subnet" +# Subnet +resource "openstack_networking_subnet_v2" "subnet1_subnet" { + name = "subnet1_subnet" network_id = openstack_networking_network_v2.net1.id - cidr = "16.0.0.0/24" + cidr = "16.0.0.1/24" dns_nameservers = ["8.8.8.8", "8.8.8.4"] } -# Attach networking port -resource "openstack_networking_port_v2" "net1" { - name = "concrete_net" - network_id = openstack_networking_network_v2.net1.id - admin_state_up = true - security_group_ids = [ - openstack_compute_secgroup_v2.icmp.id, - openstack_compute_secgroup_v2.http.id, - openstack_compute_secgroup_v2.https.id, - openstack_compute_secgroup_v2.ssh.id, - - ] - fixed_ip { - subnet_id = openstack_networking_subnet_v2.net1_subnet.id - } -} - # Create router -resource "openstack_networking_router_v2" "net1_router" { - name = "net1_router" +resource "openstack_networking_router_v2" "router" { ## 1router, not parametric + name = "router" external_network_id = data.openstack_networking_network_v2.external.id #External network id } -# Router interface configuration -resource "openstack_networking_router_interface_v2" "net1_router_interface" { - router_id = openstack_networking_router_v2.net1_router.id - subnet_id = openstack_networking_subnet_v2.net1_subnet.id -} -# CREATING SECURITY_GROUP - -resource "openstack_compute_secgroup_v2" "icmp" { - name = "icmp" - description = "Security group rule for port -1" - rule { - from_port = -1 - to_port = -1 - ip_protocol = "icmp" - cidr = "0.0.0.0/0" - } -} - -resource "openstack_compute_secgroup_v2" "http" { - name = "http" - description = "Security group rule for port 80" - rule { - from_port = 80 - to_port = 80 - ip_protocol = "tcp" - cidr = "0.0.0.0/0" - } -} - -resource "openstack_compute_secgroup_v2" "https" { - name = "https" - description = "Security group rule for port 443" - rule { - from_port = 443 - to_port = 443 - ip_protocol = "tcp" - cidr = "0.0.0.0/0" - } -} - -resource "openstack_compute_secgroup_v2" "ssh" { - name = "ssh" - description = "Security group rule for port 22" - rule { - from_port = 22 - to_port = 22 - ip_protocol = "tcp" - cidr = "0.0.0.0/0" - } +# Create ssh keys +resource "openstack_compute_keypair_v2" "ubuntu" { + name = "ubuntu" + # public_key = "" } - -# Create ssh keys -resource "openstack_compute_keypair_v2" "user1" { - name = "user1" - # public_key = "user1" +# CREATING SECURITY_GROUP +resource "openstack_compute_secgroup_v2" "sg" { + name = "infra_element_name" + description = "PIACERE security group created - sg" + rule { + from_port = -1 + to_port = -1 + ip_protocol = "icmp" + cidr = "0.0.0.0/0" + } + rule { + from_port = 80 + to_port = 80 + ip_protocol = "tcp" + cidr = "0.0.0.0/0" + } + rule { + from_port = 443 + to_port = 443 + ip_protocol = "tcp" + cidr = "0.0.0.0/0" + } + rule { + from_port = 22 + to_port = 22 + ip_protocol = "tcp" + cidr = "0.0.0.0/0" + } } + diff --git a/output_files_generated/nginx_openstack/terraform/output.tf b/output_files_generated/nginx_openstack/terraform/output.tf index f0cc70b..d94c1df 100644 --- a/output_files_generated/nginx_openstack/terraform/output.tf +++ b/output_files_generated/nginx_openstack/terraform/output.tf @@ -1,14 +1,14 @@ -output "instance_server_public_key_user1" { - value = openstack_compute_keypair_v2.user1.public_key +output "instance_server_public_key_ubuntu_nginx_vm" { + value = openstack_compute_keypair_v2.ubuntu.public_key } -output "instance_server_private_key_user1" { - value = openstack_compute_keypair_v2.user1.private_key +output "instance_server_private_key_ubuntu_nginx_vm" { + value = openstack_compute_keypair_v2.ubuntu.private_key } -output "instance_ip_vm1" { - value = openstack_compute_floatingip_associate_v2.vm1_floating_ip_association.floating_ip +output "instance_server_public_ip_nginx_vm" { + value = openstack_compute_floatingip_associate_v2.nginx_vm_floating_ip_association.floating_ip } diff --git a/output_files_generated/posidonia/terraform/config.yaml b/output_files_generated/posidonia/terraform/config.yaml deleted file mode 100644 index cefdad1..0000000 --- a/output_files_generated/posidonia/terraform/config.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -engine: terraform -input: [] -output: [] -... \ No newline at end of file diff --git a/output_files_generated/wordpress_azure/terraform/main.tf b/output_files_generated/wordpress_azure/terraform/main.tf deleted file mode 100644 index 851c144..0000000 --- a/output_files_generated/wordpress_azure/terraform/main.tf +++ /dev/null @@ -1,47 +0,0 @@ -terraform { - required_providers { - azurerm = { - source = "hashicorp/azurerm" - version = "~> 2.65" - } - } - - required_version = ">= 0.14.9" -} - -provider "azurerm" { - features {} -} - -## VIRTUAL NETWORK -resource "azurerm_virtual_network" "wordpress_net_vnetwork" { - name = "wordpress_net" - address_space = ["10.0.0.0/16"] - location = azurerm_resource_group.wordpress-example.location - resource_group_name = azurerm_resource_group.wordpress-example.name -} - -## SUBNET -resource "azurerm_subnet" "wordpress_net_subnet" { - name = "internal" - resource_group_name = azurerm_resource_group.wordpress-example.name - virtual_network_name = azurerm_virtual_network.wordpress_net_vnetwork.name - address_prefixes = ["10.0.2.0/24"] -} - -## VIRTUAL NETWORK -resource "azurerm_virtual_network" "mysql_net_vnetwork" { - name = "mysql_net" - address_space = ["10.0.0.0/16"] - location = azurerm_resource_group.mysql-example.location - resource_group_name = azurerm_resource_group.mysql-example.name -} - -## SUBNET -resource "azurerm_subnet" "mysql_net_subnet" { - name = "internal" - resource_group_name = azurerm_resource_group.mysql-example.name - virtual_network_name = azurerm_virtual_network.mysql_net_vnetwork.name - address_prefixes = ["10.0.2.0/24"] -} - diff --git a/plugin/AnsiblePlugin.py b/plugin/AnsiblePlugin.py index c4d0146..705e290 100644 --- a/plugin/AnsiblePlugin.py +++ b/plugin/AnsiblePlugin.py @@ -15,7 +15,7 @@ import logging -from icgparser.IntermediateRepresentationUtility import IntermediateRepresentationResources +from icgparser.ModelResourcesUtilities import get_ir_key_name, ModelResources from plugin import TemplateUtils from plugin.PluginException import PluginResourceNotFoundError @@ -25,13 +25,18 @@ def clean_operating_system_name(operating_system): logging.info(f"AnsiblePlugin: extracting operating system from {operating_system}") if "ubuntu" in operating_system_lower_case: return "ubuntu" + if "centos" in operating_system_lower_case: + return "centos" + ## TODO to be update with more explicit parameter + if "ami" in operating_system_lower_case: + return "ubuntu" else: raise PluginResourceNotFoundError(plugin_name="AnsiblePlugin", resource_name="operating system") def find_operating_system(parameters): try: - operating_system = parameters.get("node").get("os") + operating_system = parameters.get("nodes")[0].get("os") operating_system_name = clean_operating_system_name(operating_system) return operating_system_name except Exception: @@ -46,8 +51,8 @@ def create_template_file(parameters, language, operating_system, template_name): def create_files(step, output_path): - language = step[IntermediateRepresentationResources.LANGUAGE.value] - step_name = step[IntermediateRepresentationResources.STEP_NAME.value] + language = step[get_ir_key_name(ModelResources.LANGUAGE)] + step_name = step[get_ir_key_name(ModelResources.STEP_NAME)] parameters = step["data"] for resource_name, resource in parameters.items(): logging.info("Creating template for resource '%s'", resource_name) @@ -62,7 +67,21 @@ def create_files(step, output_path): config_output_file_path = output_path + "/".join([step_name, "config"]) + ".yaml" ssh_key_output_file_path = output_path + "/".join([step_name, "ssh_key.j2"]) - template = TemplateUtils.read_template(ansible_template_path) + ### TODO Refactoring + if "," in ansible_template_path: + ansible_template_path = ansible_template_path.split(",") + template = TemplateUtils.read_template(ansible_template_path[0]) + for i in range(1, len(ansible_template_path)): + output_other_ansible_name_split = ansible_template_path[i].split('/') + output_other_ansible_name = output_other_ansible_name_split[-1].split('.') + other_ansible_output_file_path = output_path + "/".join([step_name, output_other_ansible_name[0]]) + ".yml" + + other_template = TemplateUtils.read_template(ansible_template_path[i]) + other_template_filled = TemplateUtils.edit_template(other_template, resource_params) + TemplateUtils.write_template(other_template_filled, other_ansible_output_file_path) + else: + template = TemplateUtils.read_template(ansible_template_path) + template_filled = TemplateUtils.edit_template(template, resource_params) inventory_template_filled = create_template_file(resource_params, language, operating_system, "inventory") diff --git a/plugin/DockerComposePlugin.py b/plugin/DockerComposePlugin.py new file mode 100644 index 0000000..75b093c --- /dev/null +++ b/plugin/DockerComposePlugin.py @@ -0,0 +1,60 @@ +import logging + +from icgparser.ModelResourcesUtilities import get_ir_key_name, ModelResources +from plugin import TemplateUtils + + +def search_containers_to_be_created(container_image_resource): + logging.info("Searching for containers") + containers = [] + if container_image_resource["generatedContainers"]: + containers = list(container_image_resource["generatedContainers"]) + logging.info(f"Found containers {containers}") + return containers + +def create_template_file(resource_name, parameters, extra_parameters, language): + inventory_template_path = TemplateUtils.find_template_path(iac_language=False, key=language, + resource_name=resource_name) + template = TemplateUtils.read_template(inventory_template_path) + template_filled = TemplateUtils.edit_template(template, parameters) + return template_filled + +def create_metadata_files(resource_params, output_path, language): + inventory_template_stored_path = output_path + "inventory.j2" + ssh_template_stored_path = output_path + "ssh_key.j2" + ansible_template_file_path = output_path + "main.yml" + config_template_file_path = output_path + "config.yaml" + + inventory_template_filled = create_template_file("inventory", resource_params, None, language) + config_template_filled = create_template_file("config", resource_params, None, language) + ssh_key_template_filled = create_template_file("ssh_key", resource_params, None, language) + ansible_template_filled = create_template_file("main", resource_params, None, language) + + TemplateUtils.write_template(inventory_template_filled, inventory_template_stored_path) + TemplateUtils.write_template(ansible_template_filled, ansible_template_file_path) + TemplateUtils.write_template(config_template_filled, config_template_file_path) + TemplateUtils.write_template(ssh_key_template_filled, ssh_template_stored_path) + + +def create_files(step_data, output_path): + logging.info(f"Using Docker Compose Plugin for step {step_data}") + language = step_data[get_ir_key_name(ModelResources.LANGUAGE)] + parameters = step_data["data"] + for resource_name, resources in parameters.items(): + logging.info(f"Found resource type {resource_name}") + for resource_params in resources: + containers = search_containers_to_be_created(resource_params) + for container in containers: + logging.info(f"Creating templates for resource {resource_params}") + container_name = container["name"] + output_base_folder_path = output_path + f"{container_name}/" + template_stored_path = output_base_folder_path + "docker-compose.yml" + template_path = TemplateUtils.find_template_path(iac_language=False, key=language, + resource_name=resource_name) + template = TemplateUtils.read_template(template_path) + template_filled = TemplateUtils.edit_template(template, container, resource_params) + TemplateUtils.write_template(template_filled, template_stored_path) + + create_metadata_files(container, output_base_folder_path, language) + logging.info(f"Docker compose files created for {container_name}") + logging.info(f"Docker compose files created") diff --git a/plugin/PluginUtility.py b/plugin/PluginUtility.py index ece9bbb..1870f17 100644 --- a/plugin/PluginUtility.py +++ b/plugin/PluginUtility.py @@ -11,13 +11,34 @@ # 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. -#------------------------------------------------------------------------- +# ------------------------------------------------------------------------- +import logging from plugin import TemplateUtils +from utility import PropertiesReaderUtility +plugin_properties_file_name = "external-plugins.properties" +plugin_properties_main_section = "plugins" -def createExecutionFileInstructions(iac_language, key, data): - template_for_config_path = TemplateUtils.find_template_path(iac_language, key, "config") - template_for_config = TemplateUtils.read_template(template_for_config_path) - template_for_config_path_edited = TemplateUtils.edit_template(template_for_config, data) - return template_for_config_path_edited + +def createExecutionFileInstructions(iac_language, key, data, file_key_name): + template_path = TemplateUtils.find_template_path(iac_language, key, file_key_name) + template = TemplateUtils.read_template(template_path) + template_path_edited = TemplateUtils.edit_template(template, data) + return template_path_edited + + +def find_resources_names_for_plugin(plugin_name): + logging.info(f"Searching for resources name for plugin {plugin_name}") + resources = PropertiesReaderUtility.get_items_from_key(plugin_properties_file_name, + plugin_properties_main_section, plugin_name) + logging.info(f"Founded resources: {resources}") + return resources + + +def find_external_plugins_name(): + logging.info("Searching for external plugins") + plugins_name = PropertiesReaderUtility.get_key_from_properties(plugin_properties_file_name, + plugin_properties_main_section) + logging.info(f"Founded plugins: {plugins_name}") + return plugins_name diff --git a/plugin/TemplateUtils.py b/plugin/TemplateUtils.py index 6f5a140..31b0c72 100644 --- a/plugin/TemplateUtils.py +++ b/plugin/TemplateUtils.py @@ -16,6 +16,7 @@ import configparser import logging import os +from collections import OrderedDict import jinja2 from jinja2 import Template @@ -25,12 +26,14 @@ from jinja2 import Template def get_context(c): return c - def find_template_path(iac_language, key, resource_name): try: properties_reader = configparser.ConfigParser() properties_reader.read("template-location.properties") - template_path = properties_reader.get(iac_language + "." + key, resource_name) + if not iac_language: + template_path = properties_reader.get(key, resource_name) + else: + template_path = properties_reader.get(iac_language + "." + key, resource_name) logging.info("Chosen template at: '%s'", template_path) return template_path except configparser.NoOptionError as error: @@ -38,10 +41,11 @@ def find_template_path(iac_language, key, resource_name): pass -def edit_template(template, parameters: dict): +def edit_template(template, parameters: dict, extra_parameters=None): logging.info(f"Starting editing template '{template}'") template.globals['context'] = get_context template.globals['callable'] = callable + template.globals['extra_parameters'] = extra_parameters render = template.render(parameters) template_with_custom_params = "" + render + "\n" return template_with_custom_params diff --git a/plugin/TerraformPlugin.py b/plugin/TerraformPlugin.py index 01fa43b..1aab4f5 100644 --- a/plugin/TerraformPlugin.py +++ b/plugin/TerraformPlugin.py @@ -21,31 +21,31 @@ from plugin import TemplateUtils, PluginUtility def create_files(parameters, output_path): language = "terraform" - provider = parameters["provider"] + provider_name = parameters["provider_info"][0]["provider_name"] - config_file = PluginUtility.createExecutionFileInstructions(language, provider, parameters) + config_file = PluginUtility.createExecutionFileInstructions(language, provider_name, parameters, "config") resources = parameters.keys() - terraform_main_file = create_init_file(language, provider) + terraform_main_file = "" terraform_out_file = "" for resource_name in resources: logging.info("Creating output and main terraform template for resource '%s'", resource_name) - template_for_main_path = TemplateUtils.find_template_path(language, provider, resource_name) - template_for_output_path = TemplateUtils.find_template_path(language, provider, + template_for_main_path = TemplateUtils.find_template_path(language, provider_name, resource_name) + template_for_output_path = TemplateUtils.find_template_path(language, provider_name, get_resource_out_path(resource_name)) if template_for_main_path: for resource_params in parameters[resource_name]: template = TemplateUtils.read_template(template_for_main_path) # resource = parameters[resource_name] - template_filled = TemplateUtils.edit_template(template, resource_params) + template_filled = TemplateUtils.edit_template(template, resource_params, parameters) terraform_main_file = terraform_main_file + template_filled + "\n" if template_for_output_path: for resource_params in parameters[resource_name]: template_out = TemplateUtils.read_template(template_for_output_path) # resource = parameters[resource_name] - template_out_filled = TemplateUtils.edit_template(template_out, resource_params) + template_out_filled = TemplateUtils.edit_template(template_out, resource_params, None) terraform_out_file = terraform_out_file + template_out_filled + "\n" main_file_stored_path = output_path + "/main.tf" TemplateUtils.write_template(terraform_main_file, main_file_stored_path) @@ -56,13 +56,6 @@ def create_files(parameters, output_path): logging.info("Terraform main file available at: {}".format(main_file_stored_path)) logging.info(f"Terraform output file available at {output_file_stored_path}") - -def create_init_file(language, provider): - logging.info("Creating init %s file for provider %s", language, provider) - template_path = TemplateUtils.find_template_path(language, provider, "init") - template = TemplateUtils.read_template(template_path) - return template.render() + "\n" - ## TODO spostare i template di out in una cartella?? es. cartella vms&vms_out? altrimenti come prendo nome di out? ## non è nel doml def get_resource_out_path(resource_name): diff --git a/requirements.txt b/requirements.txt index 7637f4d..a970358 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,6 @@ Jinja2==3.0.3 PyYAML==6.0 fastapi~=0.74.1 uvicorn==0.17.5 -pyecore~=0.12.2 \ No newline at end of file +pyecore~=0.12.2 +aiofiles~=23.1.0 +python-multipart~=0.0.6 \ No newline at end of file diff --git a/service.yml b/service.yml deleted file mode 100644 index 0ba8b8a..0000000 --- a/service.yml +++ /dev/null @@ -1,16 +0,0 @@ ---- -- deployment: - - name: vm - language: terraform - code: - - outputNetwork.tf - outputdb.tf - outputvm.tf - -- orchestration: - - name: postgres - language: ansible - code: postgres-play.yml - - name: wordpress - language: ansible - code: wordpress-play.yml \ No newline at end of file diff --git a/template-location.properties b/template-location.properties index 3dcafba..183dff1 100644 --- a/template-location.properties +++ b/template-location.properties @@ -14,25 +14,50 @@ #------------------------------------------------------------------------- [terraform.openstack] -init = templates/terraform/open_stack/init.tpl +provider_info = templates/terraform/open_stack/init.tpl config = templates/terraform/open_stack/config.tpl vms = templates/terraform/open_stack/virtual_machine.tpl vms_out = templates/terraform/open_stack/virtual_machine_out.tpl networks = templates/terraform/open_stack/network.tpl computingGroup = templates/terraform/open_stack/port_rule.tpl +securityGroup = templates/terraform/open_stack/port_rule.tpl credentials = templates/terraform/open_stack/ssh_key.tpl [terraform.azure] -init = templates/terraform/azure/init.tpl +provider_info = templates/terraform/azure/init.tpl vm = templates/terraform/azure/virtual_machine.tpl net = templates/terraform/azure/network.tpl rg = templates/terraform/azure/resource_group.tpl +securityGroup = templates/terraform/azure/port_rule.tpl [terraform.aws] -init = templates/terraform/aws/init.tpl +provider_info = templates/terraform/aws/init.tpl +config = templates/terraform/aws/config.tpl vms = templates/terraform/aws/virtual_machine.tpl +vms_out = templates/terraform/aws/virtual_machine_out.tpl networks = templates/terraform/aws/network.tpl computingGroup = templates/terraform/aws/port_rule.tpl +securityGroup = templates/terraform/aws/port_rule.tpl +credentials = templates/terraform/aws/ssh_key.tpl +group = templates/terraform/aws/autoscaling_group.tpl + +[terraform.vsphere] +provider_info = templates/terraform/vsphere/init.tpl +config = templates/terraform/vsphere/config.tpl +networks = templates/terraform/vsphere/network.tpl +vms = templates/terraform/vsphere/virtual_machine.tpl +credentials = templates/terraform/vsphere/ssh_key.tpl +resources = templates/terraform/vsphere/data_resources.tpl +storages = templates/terraform/vsphere/datastore.tpl +vms_out = templates/terraform/vsphere/virtual_machine_out.tpl + +[terraform.ionoscloud] +provider_info = templates/terraform/ionos/init.tpl +config = templates/terraform/ionos/config.tpl +vms = templates/terraform/ionos/virtual_machine.tpl +vms_out = templates/terraform/ionos/virtual_machine_out.tpl +networks = templates/terraform/ionos/network.tpl +resources = templates/terraform/ionos/datacenter.tpl [ansible.ubuntu] inventory = templates/ansible/ubuntu/inventory.tpl @@ -41,10 +66,28 @@ config = templates/ansible/ubuntu/config.tpl nginx = templates/ansible/ubuntu/nginx.tpl mysql = templates/ansible/ubuntu/mysql.tpl wordpress = templates/ansible/ubuntu/wordpress.tpl +elasticsearch = templates/ansible/ubuntu/elasticsearch_main.tpl,templates/ansible/ubuntu/elasticsearch.tpl postgres = templates/ansible/ubuntu/postgres.tpl -piacere_monitoring = templates/ansible/ubuntu/piacere_main.tpl +performance_monitoring = templates/ansible/ubuntu/performance_monitoring_main.tpl +security_monitoring = templates/ansible/ubuntu/security_monitoring_main.tpl [ansible.centos] mysql = templates/ansible/centos/mysql.tpl postgres = templates/ansible/centos/postgres.tpl -wordpress = templates/ansible/centos/wordpress.tpl \ No newline at end of file +wordpress = templates/ansible/centos/wordpress.tpl +elasticsearch = templates/ansible/centos/elasticsearch_main.tpl,templates/ansible/centos/elasticsearch.tpl +performance_monitoring = templates/ansible/centos/performance_monitoring_main.tpl +security_monitoring = templates/ansible/centos/security_monitoring_main.tpl +inventory = templates/ansible/centos/inventory.tpl +config = templates/ansible/centos/config.tpl +ssh_key = templates/ansible/centos/ssh_key.tpl + +[common] +gaiax_self_description = templates/common/gaiax_self_description.yaml.tpl + +[docker-compose] +config = templates/docker_compose/config.tpl +inventory = templates/docker_compose/inventory.tpl +ssh_key = templates/docker_compose/ssh_key.tpl +main = templates/docker_compose/main.tpl +containerimages = templates/docker_compose/docker_compose.tpl \ No newline at end of file diff --git a/templates/ansible/centos/config.tpl b/templates/ansible/centos/config.tpl new file mode 100644 index 0000000..507247f --- /dev/null +++ b/templates/ansible/centos/config.tpl @@ -0,0 +1,35 @@ +{# Copyright 2022 Hewlett Packard Enterprise Development LP +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#------------------------------------------------------------------------- +#} +--- +input: + {%- if name == "performance_monitoring" %} + - INFLUXDB_BUCKET + - INFLUXDB_TOKEN + - INFLUXDB_ORG + - INFLUXDB_ADDR + - DEPLOYMENT_ID + {%- endif %} + {%- if name == "security_monitoring" %} + - DEPLOYMENT_ID + - WAZUH_MANAGER_HOST + - WAZUH_MANAGER_PORT{%- endif %} + {%- for node in nodes %} + - instance_server_public_ip_{{ node.infra_element_name }} + - instance_server_private_key_{{ node.credentials }}_{{ node.infra_element_name }} + {%- endfor %} +output: [] +engine: ansible +... diff --git a/templates/ansible/centos/elasticsearch.tpl b/templates/ansible/centos/elasticsearch.tpl new file mode 100644 index 0000000..05d0432 --- /dev/null +++ b/templates/ansible/centos/elasticsearch.tpl @@ -0,0 +1,21 @@ +--- + +- name: Elasticsearch with custom configuration + hosts: servers_for_elasticsearch + roles: + - role: elastic.elasticsearch + vars: + es_data_dirs: + - "/opt/elasticsearch/data" + es_log_dir: "/opt/elasticsearch/logs" + es_config: + node.name: "node1" + cluster.name: "custom-cluster" + discovery.seed_hosts: "localhost:9301" + http.port: 9201 + transport.port: 9301 + node.data: false + node.master: true + bootstrap.memory_lock: true + es_heap_size: 1g + es_api_port: 9201 \ No newline at end of file diff --git a/templates/ansible/centos/elasticsearch_main.tpl b/templates/ansible/centos/elasticsearch_main.tpl new file mode 100644 index 0000000..5a0e4fd --- /dev/null +++ b/templates/ansible/centos/elasticsearch_main.tpl @@ -0,0 +1,12 @@ +--- + +- hosts: localhost + + pre_tasks: + - file: + path: roles + state: absent + + - command: ansible-galaxy install elastic.elasticsearch,v7.17.0 + tasks: + - command: ansible-playbook -i inventory elasticsearch.yml \ No newline at end of file diff --git a/templates/ansible/centos/inventory.tpl b/templates/ansible/centos/inventory.tpl new file mode 100644 index 0000000..8db3ec0 --- /dev/null +++ b/templates/ansible/centos/inventory.tpl @@ -0,0 +1,25 @@ +{# Copyright 2022 Hewlett Packard Enterprise Development LP +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#------------------------------------------------------------------------- +#} + +[{{ "servers_for_" ~ name }}] +{%- for node in nodes %} +{% raw %}{{ instance_server_public_ip_{% endraw %}{{ node.infra_element_name }} {% raw %}}}{% endraw %} +{%- endfor %} + +[{{ "servers_for_" ~ name }}:vars] +ansible_connection=ssh +ansible_user=centos +ansible_ssh_private_key_file=ssh_key diff --git a/templates/ansible/centos/performance_monitoring_main.tpl b/templates/ansible/centos/performance_monitoring_main.tpl new file mode 100644 index 0000000..8b32d63 --- /dev/null +++ b/templates/ansible/centos/performance_monitoring_main.tpl @@ -0,0 +1,42 @@ +--- +- hosts: localhost + tasks: + - name: print disclamer + debug: + msg: this can also be done with "ansible-galaxy install -r requirements" + - name: install telegraf from galaxy + community.general.ansible_galaxy_install: + type: role + requirements_file: ansible_requirements.yml + +- hosts: servers_for_performance_monitoring + pre_tasks: + - name: Check parameters + fail: + msg: 'variable {{item}} not defined' + when: item is not defined + with_items: + - pma_deployment_id + - pma_influxdb_bucket + - pma_influxdb_token + - pma_influxdb_org + - pma_influxdb_addr + - name: Print parameters + debug: + msg: + - "pma_deployment_id: {{ pma_deployment_id }}" + - "pma_influxdb_bucket: {{ pma_influxdb_bucket }}" + - "pma_influxdb_token: {{ pma_influxdb_token }}" + - "pma_influxdb_org: {{ pma_influxdb_org }}" + - "pma_influxdb_addr: {{ pma_influxdb_addr }}" + - name: Ensure gnupg package + package: + name: gnupg + state: present + become: true + vars_files: + - vars/main.yaml + tasks: + - name: Install telegraf + ansible.builtin.include_role: + name: dj-wasabi.telegraf diff --git a/templates/ansible/centos/security_monitoring_main.tpl b/templates/ansible/centos/security_monitoring_main.tpl new file mode 100644 index 0000000..43a939e --- /dev/null +++ b/templates/ansible/centos/security_monitoring_main.tpl @@ -0,0 +1,2 @@ +--- +- import_playbook: deploy-wazuh-agent.yml \ No newline at end of file diff --git a/templates/ansible/centos/ssh_key.tpl b/templates/ansible/centos/ssh_key.tpl new file mode 100644 index 0000000..d7a25b0 --- /dev/null +++ b/templates/ansible/centos/ssh_key.tpl @@ -0,0 +1,4 @@ +{%- for node in nodes %} +{%- raw %}{{ instance_server_private_key_{% endraw %}{{ node.credentials }}{% raw %}_{%- endraw %}{{node.infra_element_name}}{% raw %}}} +{% endraw %} +{%- endfor %} \ No newline at end of file diff --git a/templates/ansible/cross-platform/performance_monitoring/.gitignore b/templates/ansible/cross-platform/performance_monitoring/.gitignore new file mode 100644 index 0000000..2eea525 --- /dev/null +++ b/templates/ansible/cross-platform/performance_monitoring/.gitignore @@ -0,0 +1 @@ +.env \ No newline at end of file diff --git a/templates/ansible/cross-platform/performance_monitoring/LICENSE b/templates/ansible/cross-platform/performance_monitoring/LICENSE new file mode 100644 index 0000000..d4f1283 --- /dev/null +++ b/templates/ansible/cross-platform/performance_monitoring/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2023 PIACERE / public / agents + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/templates/ansible/cross-platform/performance_monitoring/README.md b/templates/ansible/cross-platform/performance_monitoring/README.md new file mode 100644 index 0000000..96f678b --- /dev/null +++ b/templates/ansible/cross-platform/performance_monitoring/README.md @@ -0,0 +1,31 @@ +# pma playbook + +This is an ansible playbook that install telegraf and cofigure to the needs of the performance monitoring component of piacere + +## How to use + +This playbook is automatically embeeded as iac by yhe ICG, the iac is then run by the IEM + + +## How to test +There are may ways to test a playbook here we document the procedure followed in our case. +* Obtain a ssh docker image of some platform +* instantiate the ssh docker +* install the playbook requirements +* launch the playbook against it + +i.e. Providing we have already an ssh docker image ... i.e. ubuntu-ssh https://git.code.tecnalia.com/smartdatalab/libraries/docker/ubuntu-ssh.git + +``` +docker rm -f ubuntu-ssh +docker network rm -f ubuntu-ssh +docker network create --driver=bridge --subnet=10.0.55.0/24 --driver=bridge ubuntu-ssh +docker run -d --name ubuntu-ssh --network ubuntu-ssh --ip 10.0.55.5 --env PUB_SSH_CERT_0="$(cat ~/.ssh/id_rsa.pub)" ubuntu-ssh +./ansible/playbooks/pma/install_playbook_requirements.sh +./ansible/playbooks/pma/run-playbook.sh '{"pma_deployment_id": "123e4567-e89b-12d3-a456-426614174001", "pma_influxdb_bucket": "bucket", "pma_influxdb_token": "piacerePassword", "pma_influxdb_org": "piacere", "pma_influxdb_addr": "https://influxdb.pm.ci.piacere.digital.tecnalia.dev" }' +ssh -o StrictHostKeyChecking=no root@10.0.55.5 service telegraf status +``` + +the output shoud be that the "telegraf Process is running `[[ OK ]]" + +## Notes diff --git a/templates/ansible/ubuntu/monitoring/ansible_requirements.yml b/templates/ansible/cross-platform/performance_monitoring/ansible_requirements.yml similarity index 90% rename from templates/ansible/ubuntu/monitoring/ansible_requirements.yml rename to templates/ansible/cross-platform/performance_monitoring/ansible_requirements.yml index 58c0cb3..77b0ab3 100644 --- a/templates/ansible/ubuntu/monitoring/ansible_requirements.yml +++ b/templates/ansible/cross-platform/performance_monitoring/ansible_requirements.yml @@ -5,4 +5,4 @@ roles: - name: dj-wasabi.telegraf src: https://github.com/dj-wasabi/ansible-telegraf.git scm: git - version: 0.13.2 + version: 0.14.0 diff --git a/templates/ansible/cross-platform/performance_monitoring/config.yaml b/templates/ansible/cross-platform/performance_monitoring/config.yaml new file mode 100644 index 0000000..8720808 --- /dev/null +++ b/templates/ansible/cross-platform/performance_monitoring/config.yaml @@ -0,0 +1,12 @@ +--- +input: + - INFLUXDB_BUCKET + - INFLUXDB_TOKEN + - INFLUXDB_ORG + - INFLUXDB_ADDR + - DEPLOYMENT_ID + # - instance_ip_vm1 + # - instance_server_private_key_user1 +output: [] +engine: ansible +... \ No newline at end of file diff --git a/templates/ansible/cross-platform/performance_monitoring/inventory.j2 b/templates/ansible/cross-platform/performance_monitoring/inventory.j2 new file mode 100644 index 0000000..489cb89 --- /dev/null +++ b/templates/ansible/cross-platform/performance_monitoring/inventory.j2 @@ -0,0 +1,9 @@ + + +[servers_for_performance_monitoring] +demo-server-for-agents + +[servers_for_performance_monitoring:vars] +ansible_connection=ssh +ansible_user=ubuntu +ansible_ssh_private_key_file=ssh_key diff --git a/templates/ansible/cross-platform/performance_monitoring/inventory.txt b/templates/ansible/cross-platform/performance_monitoring/inventory.txt new file mode 100644 index 0000000..4c1b112 --- /dev/null +++ b/templates/ansible/cross-platform/performance_monitoring/inventory.txt @@ -0,0 +1,2 @@ +[docker] +localhost \ No newline at end of file diff --git a/templates/ansible/cross-platform/performance_monitoring/main.yml b/templates/ansible/cross-platform/performance_monitoring/main.yml new file mode 100644 index 0000000..8b32d63 --- /dev/null +++ b/templates/ansible/cross-platform/performance_monitoring/main.yml @@ -0,0 +1,42 @@ +--- +- hosts: localhost + tasks: + - name: print disclamer + debug: + msg: this can also be done with "ansible-galaxy install -r requirements" + - name: install telegraf from galaxy + community.general.ansible_galaxy_install: + type: role + requirements_file: ansible_requirements.yml + +- hosts: servers_for_performance_monitoring + pre_tasks: + - name: Check parameters + fail: + msg: 'variable {{item}} not defined' + when: item is not defined + with_items: + - pma_deployment_id + - pma_influxdb_bucket + - pma_influxdb_token + - pma_influxdb_org + - pma_influxdb_addr + - name: Print parameters + debug: + msg: + - "pma_deployment_id: {{ pma_deployment_id }}" + - "pma_influxdb_bucket: {{ pma_influxdb_bucket }}" + - "pma_influxdb_token: {{ pma_influxdb_token }}" + - "pma_influxdb_org: {{ pma_influxdb_org }}" + - "pma_influxdb_addr: {{ pma_influxdb_addr }}" + - name: Ensure gnupg package + package: + name: gnupg + state: present + become: true + vars_files: + - vars/main.yaml + tasks: + - name: Install telegraf + ansible.builtin.include_role: + name: dj-wasabi.telegraf diff --git a/templates/ansible/cross-platform/performance_monitoring/ssh_key.j2 b/templates/ansible/cross-platform/performance_monitoring/ssh_key.j2 new file mode 100644 index 0000000..86a197b --- /dev/null +++ b/templates/ansible/cross-platform/performance_monitoring/ssh_key.j2 @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA1FrTNE42EgZr9WJNMtvpKFHYhPUJ4lzEp83EM0jYY3TyjmIe +ThMuqMLAHCk22fl4a8PttucggJ5ZWKhcJh623/y8AybJcmqZgq9a41Q609dmirf0 +7frCl+6zL8Mqy2Le2BD4eRADcq11s8r8Ys6J+EBPHQgEnK9CeZLSc/WFRlVr4bOD +s0bEouDxjTAMYjYcpsCwqYgGdIXI9WWsnt3RvcEe8CaiTqoyDN8ZtgkG6MweSrTQ +js8ySHO6o25cOoF7aT9Ihhf32I+KUanNIOvk3RAw2z1FK5xkFbbqMggZqz7rJn3M +sn2dDiCQi2CWox2OYXV/jJKLC3UFuOX64fS9cwIDAQABAoIBAQCs69Tm1/Vx0ibh +aA4DJ06C1bsh8cP9v5soJgfp1xzWSGooBcA1xasOI6B6jhkrgNlNr/uIIEe4VLne +1yJKrGIwnUagrloGQMYGxDKXwYQx80p+FXRuwe7p96eUcjIL8tQSUCd1tdOI87VQ +FjBVaWiybfO+aUQQLytLgoK7iKfhb7vO+9F+ZK1iDjBDNxFuiOM5zoeWOI7boYkD +2yXIkwoBePS2rosbPLa649sVakKex2WhQdUFst4Zba2RhnWQBXUY44LvEK5TzScF +FyYphPOUSplbzzM2+fuOna91NIWmJyHmf15lj7X9kC66XFIZMlvapksB8stEpDiA +4al3IdBJAoGBAPPuM3xkr/kQYYn7E42fgpmINZ78V48gMLhpyUOabZtF8inMyMPB +q7kfHns8Vx0ET8grSNr3qwDDV82lwvGqRCFChASMdQRR9LanydkDSeqpuZyQtVlt +A/65YUdcNY7Vy+M+fRh5Srh/6qcO3beLeLWXbJ4RHBP/OEmHuF4mLfgVAoGBAN7c +qdxTOhXPvOU69Bs5rJdfo6qBI1Yg8MCGctsUwPFpw1kW773ROOPa6XI6D74Dsdg8 +ypZ+IC3pRVtx61Xi3NOwxWNTTG+dyUgTSFz+WKjywXZXeHIbWngiFqk8JFYQWPzk +6YaJk4tZhk2YuNNaCCYRgQqyWv8coEurRlMXZHlnAoGBALcJwdaQ0z8oXJimL4jw +7ZX5kIrpPWanuAdZUe4Jfj+qX8mf4fKKbCowQLYmlBOw/ZDtcfDlMYsUCdnFjZ+7 +rP3sJJYpM1F3khJRm3PdNOUCUMY8C+i7lejZADcE6SdyJFkztbjcowYI7nJHBHZL +ENvqcVW27wPOWlVKozz6lzn1AoGALVwmaoS6DtRwcwuzwZLUkR7TNhIAujgMKHN1 +DyhDOR+4tfpYI39hH+dfmnM83wTrfsKozUawkAepqToflySMo72X/2Zl6VXpMPVT +xjGyo/h87fRRvI/asxblG9702luLcTW6XjrEQBmhn0uVWtc5T15CsIWqxb/y1FPx +BVp+hcMCgYAlJXbjzjbbDoIOCsXPSPe9voBL8zVwp0aNuvQcuB/vCt1n1c1DWuPr +AGMy/fRwY0Znag+ODMuulm7RgXUQy6ifJHiz9cKVGg/mGifaJSjgC+1AI9HFlij3 +asM5CueU0gK974rDxQkwmIWpRH57+kf6s8tGDrPPvqX9S4p3oxFlTw== +-----END RSA PRIVATE KEY----- diff --git a/templates/ansible/cross-platform/performance_monitoring/vars/main.yaml b/templates/ansible/cross-platform/performance_monitoring/vars/main.yaml new file mode 100644 index 0000000..7119eb9 --- /dev/null +++ b/templates/ansible/cross-platform/performance_monitoring/vars/main.yaml @@ -0,0 +1,28 @@ +pma_deployment_id: "{{ lookup('env', 'DEPLOYMENT_ID' ) }}" +pma_influxdb_bucket: "{{ lookup('env', 'INFLUXDB_BUCKET' ) }}" +pma_influxdb_token: "{{ lookup('env', 'INFLUXDB_TOKEN' ) }}" +pma_influxdb_org: "{{ lookup('env', 'INFLUXDB_ORG' ) }}" +pma_influxdb_addr: "{{ lookup('env', 'INFLUXDB_ADDR' ) }}" + +telegraf_agent_package_state: latest + +telegraf_agent_output: + - type: influxdb_v2 + config: + - urls = ["{{ pma_influxdb_addr }}"] + - token = "{{ pma_influxdb_token }}" + - organization = "{{ pma_influxdb_org }}" + - bucket = "{{ pma_influxdb_bucket }}" + - insecure_skip_verify = true + +telegraf_global_tags: + - tag_name: deployment_id + tag_value: "{{ pma_deployment_id }}" + +telegraf_plugins_default: + - plugin: cpu + - plugin: mem + - plugin: processes + - plugin: disk + - plugin: net + - plugin: system diff --git a/templates/ansible/cross-platform/security_monitoring/README.md b/templates/ansible/cross-platform/security_monitoring/README.md new file mode 100644 index 0000000..16225ff --- /dev/null +++ b/templates/ansible/cross-platform/security_monitoring/README.md @@ -0,0 +1,88 @@ +# sma-playbook + +Security Monitoring Agent (Wazuh agent) deployment as a docker + +## Usage - "baremetal" + +### Configuration + +`vars.yml` include: + +``` +--- +wazuh_manager_hostname: "wazuh-manager" +wazuh_manager_port: "1514" + +piacere_deployment_id: "123e4567-e89b-12d3-a456-demo-PIACERE" +``` + +All these variables can be overriden via environemnt. + +### Run the playbook + +To run the playbook: + +``` +ansible-playbook main.yml -i inventory.txt +``` + +## Usage - Docker + +To build the agent's docker image on `docker` host from the `inventory`, run this command: + +``` +ansible-playbook build-wazuh-agent.yml -i inventory.txt +``` + +You could also build the image manually and push it to some other docker registry. In this case you should change the variable for the image name within `vars.yml`. + +To start the deployment, run this command: + +``` +ansible-playbook deploy-wazuh-docker-agent.yml -i inventory.txt +``` + +Example of the configuration (`vars.yml`): + +``` +--- +service_config_dir: "{{ ansible_env.HOME }}/piacere-wazuh-agent" +docker_image_build_dir: "{{ ansible_env.HOME }}/piacere-wazuh-agent/image" +wazuh_manager_hostname: "wazuh-manager" +wazuh_manager_port: "1514" + +wazuh_agent_network: "security-monitoring-deployment_default" +wazuh_agent_name: "wazuh-agent-container-2" +wazuh_agent_group: "default" +wazuh_agent_config_volume: "{{ service_config_dir }}/ossec.conf:/var/ossec/etc/ossec.conf" +wazuh_agent_image_name: "wazuh-agent-image" + +piacere_deployment_id: "123e4567-e89b-12d3-a456-426614174002" +``` + +All these variables can be overriden via environemnt. + +### `Build Wazuh Agent` playbook + +It uses `community.docker.docker_image` module. It copies `docker-deploy` dir to the target and then it builds the agent's image with the name from the `vars.yml` on the target machine from the inventory. + +### `Deploy Wazuh Docker Agent` playbook + +It uses `community.docker.docker_container` module. The module runs the image with a name of `wazuh-agent-deploy:latest` by default (configurable within `vars.yml`), using the network `security-monitoring-deployment_default`, on the target machine. It is very important that the Wazuh Manager runs on the same network, otherwise the agent will not be able to contact the manager. `hostname` of the Agent will be set accordingly and visible in the Manager. ENV variable `WAZUH_MANAGER` sets the hostname of the Manager running on the network mentioned above. `WAZUH_AGENT_GROUP` will also to be taken into account by the Agent deployment. `ossec.conf` from the `docker-deploy` directory will be copied to the container's `/var/ossec/` directory. + +## Run the agent as a docker instance manually, not advisable + +Consider this section as a backup in the case you can not use the playbooks above. + +Build the image + +``` +cd docker-deploy +docker build -t docker-wazuh-agent:latest . +``` + +Run the agent attached to network `security-monitoring-deployment_default` where Wazuh Manager should be already running. + +``` +docker run -d --name wazuh-agent --network=security-monitoring-deployment_default --hostname localhost -e WAZUH_MANAGER=wazuh-manager -e WAZUH_AGENT_GROUP=default -v ${PWD}/ossec.conf:/var/ossec/etc/ossec.conf docker-wazuh-agent:latest +``` \ No newline at end of file diff --git a/templates/ansible/cross-platform/security_monitoring/build-wazuh-agent.yml b/templates/ansible/cross-platform/security_monitoring/build-wazuh-agent.yml new file mode 100644 index 0000000..93693cf --- /dev/null +++ b/templates/ansible/cross-platform/security_monitoring/build-wazuh-agent.yml @@ -0,0 +1,20 @@ +--- +- hosts: docker + tasks: + + - name: include vars + include_vars: vars.yml + + - name: Copy build dir to dest + copy: + src: "./docker-deploy" + dest: "{{ docker_image_build_dir }}" + mode: 0644 + + - name: Build docker image + community.docker.docker_image: + build: + path: "{{ docker_image_build_dir }}/docker-deploy" + name: "{{ wazuh_agent_image_name }}" + tag: latest + source: build \ No newline at end of file diff --git a/templates/ansible/cross-platform/security_monitoring/config.yaml b/templates/ansible/cross-platform/security_monitoring/config.yaml new file mode 100644 index 0000000..99deb2a --- /dev/null +++ b/templates/ansible/cross-platform/security_monitoring/config.yaml @@ -0,0 +1,10 @@ +--- +input: + - DEPLOYMENT_ID + - WAZUH_MANAGER_HOST + - WAZUH_MANAGER_PORT + # - instance_ip_vm1 + # - instance_server_private_key_user1 +output: [] +engine: ansible +... diff --git a/templates/ansible/cross-platform/security_monitoring/config/ossec.conf.j2 b/templates/ansible/cross-platform/security_monitoring/config/ossec.conf.j2 new file mode 100644 index 0000000..b96de85 --- /dev/null +++ b/templates/ansible/cross-platform/security_monitoring/config/ossec.conf.j2 @@ -0,0 +1,203 @@ +<ossec_config> + <client> + <server> + <address>{{ wazuh_manager_hostname }}</address> + <port>{{ wazuh_manager_port }}</port> + <protocol>tcp</protocol> + </server> + <config-profile>ubuntu, ubuntu20, ubuntu20.04</config-profile> + <notify_time>60</notify_time> + <time-reconnect>120</time-reconnect> + <auto_restart>yes</auto_restart> + <crypto_method>aes</crypto_method> + </client> + + + <client_buffer> + <!-- Agent buffer options --> + <disabled>no</disabled> + <queue_size>5000</queue_size> + <events_per_second>500</events_per_second> + </client_buffer> + + <!-- Policy monitoring --> + <rootcheck> + <disabled>no</disabled> + <check_files>yes</check_files> + <check_trojans>yes</check_trojans> + <check_dev>yes</check_dev> + <check_sys>yes</check_sys> + <check_pids>yes</check_pids> + <check_ports>yes</check_ports> + <check_if>yes</check_if> + + <!-- Frequency that rootcheck is executed - every 12 hours --> + <frequency>43200</frequency> + + <rootkit_files>etc/shared/rootkit_files.txt</rootkit_files> + <rootkit_trojans>etc/shared/rootkit_trojans.txt</rootkit_trojans> + + <skip_nfs>yes</skip_nfs> + </rootcheck> + + <wodle name="cis-cat"> + <disabled>yes</disabled> + <timeout>1800</timeout> + <interval>1d</interval> + <scan-on-start>yes</scan-on-start> + + <java_path>wodles/java</java_path> + <ciscat_path>wodles/ciscat</ciscat_path> + </wodle> + + <!-- Osquery integration --> + <wodle name="osquery"> + <disabled>yes</disabled> + <run_daemon>yes</run_daemon> + <log_path>/var/log/osquery/osqueryd.results.log</log_path> + <config_path>/etc/osquery/osquery.conf</config_path> + <add_labels>yes</add_labels> + </wodle> + + <!-- System inventory --> + <wodle name="syscollector"> + <disabled>no</disabled> + <interval>1h</interval> + <scan_on_start>yes</scan_on_start> + <hardware>yes</hardware> + <os>yes</os> + <network>yes</network> + <packages>yes</packages> + <ports all="no">yes</ports> + <processes>yes</processes> + + <!-- Database synchronization settings --> + <synchronization> + <max_eps>10</max_eps> + </synchronization> + </wodle> + + <sca> + <enabled>yes</enabled> + <scan_on_start>yes</scan_on_start> + <interval>12h</interval> + <skip_nfs>yes</skip_nfs> + </sca> + + <!-- File integrity monitoring --> + <syscheck> + <disabled>no</disabled> + + <!-- Frequency that syscheck is executed default every 12 hours --> + <frequency>43200</frequency> + + <scan_on_start>yes</scan_on_start> + + <!-- Directories to check (perform all possible verifications) --> + <directories>/etc,/usr/bin,/usr/sbin</directories> + <directories>/bin,/sbin,/boot</directories> + + <!-- Files/directories to ignore --> + <ignore>/etc/mtab</ignore> + <ignore>/etc/hosts.deny</ignore> + <ignore>/etc/mail/statistics</ignore> + <ignore>/etc/random-seed</ignore> + <ignore>/etc/random.seed</ignore> + <ignore>/etc/adjtime</ignore> + <ignore>/etc/httpd/logs</ignore> + <ignore>/etc/utmpx</ignore> + <ignore>/etc/wtmpx</ignore> + <ignore>/etc/cups/certs</ignore> + <ignore>/etc/dumpdates</ignore> + <ignore>/etc/svc/volatile</ignore> + + <!-- File types to ignore --> + <ignore type="sregex">.log$|.swp$</ignore> + + <!-- Check the file, but never compute the diff --> + <nodiff>/etc/ssl/private.key</nodiff> + + <skip_nfs>yes</skip_nfs> + <skip_dev>yes</skip_dev> + <skip_proc>yes</skip_proc> + <skip_sys>yes</skip_sys> + + <!-- Nice value for Syscheck process --> + <process_priority>10</process_priority> + + <!-- Maximum output throughput --> + <max_eps>100</max_eps> + + <!-- Database synchronization settings --> + <synchronization> + <enabled>yes</enabled> + <interval>5m</interval> + <max_interval>1h</max_interval> + <max_eps>10</max_eps> + </synchronization> + </syscheck> + + <!-- Log analysis --> + <localfile> + <log_format>command</log_format> + <command>df -P</command> + <frequency>360</frequency> + </localfile> + + <localfile> + <log_format>full_command</log_format> + <command>netstat -tulpn | sed 's/\([[:alnum:]]\+\)\ \+[[:digit:]]\+\ \+[[:digit:]]\+\ \+\(.*\):\([[:digit:]]*\)\ \+\([0-9\.\:\*]\+\).\+\ \([[:digit:]]*\/[[:alnum:]\-]*\).*/\1 \2 == \3 == \4 \5/' | sort -k 4 -g | sed 's/ == \(.*\) ==/:\1/' | sed 1,2d</command> + <alias>netstat listening ports</alias> + <frequency>360</frequency> + </localfile> + + <localfile> + <log_format>full_command</log_format> + <command>last -n 20</command> + <frequency>360</frequency> + </localfile> + + <!-- Active response --> + <active-response> + <disabled>no</disabled> + <ca_store>etc/wpk_root.pem</ca_store> + <ca_verification>yes</ca_verification> + </active-response> + + <!-- Choose between "plain", "json", or "plain,json" for the format of internal logs --> + <logging> + <log_format>plain</log_format> + </logging> + + <labels> + <label key="piacere-deployment-id">{{ piacere_deployment_id }}</label> + </labels> +</ossec_config> + +<ossec_config> + <localfile> + <log_format>audit</log_format> + <location>/var/log/audit/audit.log</location> + </localfile> + + <localfile> + <log_format>syslog</log_format> + <location>/var/ossec/logs/active-responses.log</location> + </localfile> + + <localfile> + <log_format>syslog</log_format> + <location>/var/log/messages</location> + </localfile> + + <localfile> + <log_format>syslog</log_format> + <location>/var/log/secure</location> + </localfile> + + <localfile> + <log_format>syslog</log_format> + <location>/var/log/maillog</location> + </localfile> + +</ossec_config> \ No newline at end of file diff --git a/templates/ansible/cross-platform/security_monitoring/deploy-wazuh-agent.yml b/templates/ansible/cross-platform/security_monitoring/deploy-wazuh-agent.yml new file mode 100644 index 0000000..408b261 --- /dev/null +++ b/templates/ansible/cross-platform/security_monitoring/deploy-wazuh-agent.yml @@ -0,0 +1,110 @@ +- hosts: servers_for_security_monitoring + gather_facts: yes + + pre_tasks: + - name: Check parameters + fail: + msg: 'variable {{item}} not defined' + when: item is not defined + with_items: + - piacere_deployment_id + - wazuh_manager_hostname + - wazuh_manager_port + - name: Print parameters + debug: + msg: + - "piacere_deployment_id: {{ piacere_deployment_id }}" + - "wazuh_manager_hostname: {{ wazuh_manager_hostname }}" + - "wazuh_manager_port: {{ wazuh_manager_port }}" + - name: Ensure gnupg package + package: + name: gnupg + state: present + become: true + vars_files: + - vars.yml + + tasks: + + - name: System details + ansible.builtin.debug: msg="{{ item }}" + with_items: + - "{{ ansible_distribution }}" + - "{{ ansible_distribution_version }}" + - "{{ ansible_distribution_major_version }}" + + - name: Other distributions not supported + ansible.builtin.shell: echo "only on Ubuntu or Debian" + when: ansible_distribution != 'Debian' and ansible_distribution != 'Ubuntu' + + - name: System upgrade + ansible.builtin.apt: + name: "*" + state: latest + update_cache: yes + force_apt_get: True + cache_valid_time: 3600 + become: yes + register: apt_action + retries: 100 + until: apt_action is success + when: ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu' + + - name: APT install required packages + become: yes + ansible.builtin.apt: + name: + - curl + - python3 + state: present + when: ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu' + + - name: Add wazuh apt repository and install wazuh-agent + become: yes + block: + - name: Get wazuh apt-key + ansible.builtin.apt_key: + url: https://packages.wazuh.com/key/GPG-KEY-WAZUH + state: present + - name: Add wazuh apt repository + ansible.builtin.apt_repository: + repo: "deb https://packages.wazuh.com/4.x/apt/ stable main" + state: present + filename: wazuh + update_cache: yes + - name: Install wazuh-agent + register: updatesys + apt: + name: wazuh-agent + when: ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu' + + - name: Create config path + ansible.builtin.file: + path: "{{ service_config_dir }}" + state: directory + mode: 0755 + when: ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu' + + - name: Copy config template to remote host + become: yes + ansible.builtin.template: + src: "{{ playbook_dir }}/config/ossec.conf.j2" + dest: "/var/ossec/etc/ossec.conf" + mode: 0644 + register: config_changed + when: ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu' + + - name: Start wazuh agent service + become: yes + block: + - name: Make sure wazuh-agent service is enabled and not masked + ansible.builtin.systemd: + daemon_reload: yes + name: wazuh-agent + enabled: yes + masked: no + - name: Start the service + ansible.builtin.systemd: + name: wazuh-agent + state: started + when: ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu' \ No newline at end of file diff --git a/templates/ansible/cross-platform/security_monitoring/deploy-wazuh-docker-agent.yml b/templates/ansible/cross-platform/security_monitoring/deploy-wazuh-docker-agent.yml new file mode 100644 index 0000000..2f50296 --- /dev/null +++ b/templates/ansible/cross-platform/security_monitoring/deploy-wazuh-docker-agent.yml @@ -0,0 +1,34 @@ +--- +- hosts: docker + tasks: + + - name: include vars + include_vars: vars.yml + + - name: Create config path + file: + path: "{{ service_config_dir }}" + state: directory + mode: 0755 + + - name: Copy config template to remote host + template: + src: "{{ playbook_dir }}/config/ossec.conf.j2" + dest: "{{ service_config_dir }}/ossec.conf" + mode: 0644 + register: config_changed + + - name: Run docker image + docker_container: + name: wazuh-agent-container + networks: + - name: "{{ wazuh_agent_network }}" + image: "{{ wazuh_agent_image_name }}" + state: started + hostname: "{{ wazuh_agent_name }}" + auto_remove: false + env: + WAZUH_MANAGER: "{{ wazuh_manager_hostname }}" + WAZUH_AGENT_GROUP: "{{ wazuh_agent_group }}" + volumes: + - "{{ wazuh_agent_config_volume }}" \ No newline at end of file diff --git a/templates/ansible/cross-platform/security_monitoring/docker-deploy/Dockerfile b/templates/ansible/cross-platform/security_monitoring/docker-deploy/Dockerfile new file mode 100644 index 0000000..c669855 --- /dev/null +++ b/templates/ansible/cross-platform/security_monitoring/docker-deploy/Dockerfile @@ -0,0 +1,16 @@ +FROM debian:10.12-slim + +RUN apt-get update && apt-get install -y \ + procps curl apt-transport-https gnupg2 inotify-tools python-docker && \ + curl -s https://packages.wazuh.com/key/GPG-KEY-WAZUH | apt-key add - && \ + echo "deb https://packages.wazuh.com/4.x/apt/ stable main" | tee /etc/apt/sources.list.d/wazuh.list && \ + apt-get update && \ + apt-get install -y lsb-release && \ + rm -rf /var/lib/apt/lists/* + +RUN curl -so wazuh-agent-4.2.5.deb https://packages.wazuh.com/4.x/apt/pool/main/w/wazuh-agent/wazuh-agent_4.2.5-1_amd64.deb && dpkg -i ./wazuh-agent-4.2.5.deb + +COPY entrypoint.sh /entrypoint.sh +COPY ossec.conf /var/ossec/etc/ + +ENTRYPOINT ["bash","entrypoint.sh"] \ No newline at end of file diff --git a/templates/ansible/cross-platform/security_monitoring/docker-deploy/entrypoint.sh b/templates/ansible/cross-platform/security_monitoring/docker-deploy/entrypoint.sh new file mode 100644 index 0000000..dbd7d2b --- /dev/null +++ b/templates/ansible/cross-platform/security_monitoring/docker-deploy/entrypoint.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +# Start the agent + +/var/ossec/bin/wazuh-control start +status=$? +if [ $status -ne 0 ]; then + echo "Failed to start agent: $status" + exit $status +fi + +echo "background jobs running, listening for changes" + +while sleep 60; do + /var/ossec/bin/wazuh-control status > /dev/null 2>&1 + status=$? + if [ $status -ne 0 ]; then + echo "looks like the agent died." + exit 1 + fi +done diff --git a/templates/ansible/cross-platform/security_monitoring/docker-deploy/ossec.conf b/templates/ansible/cross-platform/security_monitoring/docker-deploy/ossec.conf new file mode 100644 index 0000000..e49eb11 --- /dev/null +++ b/templates/ansible/cross-platform/security_monitoring/docker-deploy/ossec.conf @@ -0,0 +1,203 @@ +<ossec_config> + <client> + <server> + <address>wazuh-manager</address> + <port>1514</port> + <protocol>tcp</protocol> + </server> + <config-profile>ubuntu, ubuntu20, ubuntu20.04</config-profile> + <notify_time>60</notify_time> + <time-reconnect>120</time-reconnect> + <auto_restart>yes</auto_restart> + <crypto_method>aes</crypto_method> + </client> + + + <client_buffer> + <!-- Agent buffer options --> + <disabled>no</disabled> + <queue_size>5000</queue_size> + <events_per_second>500</events_per_second> + </client_buffer> + + <!-- Policy monitoring --> + <rootcheck> + <disabled>no</disabled> + <check_files>yes</check_files> + <check_trojans>yes</check_trojans> + <check_dev>yes</check_dev> + <check_sys>yes</check_sys> + <check_pids>yes</check_pids> + <check_ports>yes</check_ports> + <check_if>yes</check_if> + + <!-- Frequency that rootcheck is executed - every 12 hours --> + <frequency>43200</frequency> + + <rootkit_files>etc/shared/rootkit_files.txt</rootkit_files> + <rootkit_trojans>etc/shared/rootkit_trojans.txt</rootkit_trojans> + + <skip_nfs>yes</skip_nfs> + </rootcheck> + + <wodle name="cis-cat"> + <disabled>yes</disabled> + <timeout>1800</timeout> + <interval>1d</interval> + <scan-on-start>yes</scan-on-start> + + <java_path>wodles/java</java_path> + <ciscat_path>wodles/ciscat</ciscat_path> + </wodle> + + <!-- Osquery integration --> + <wodle name="osquery"> + <disabled>yes</disabled> + <run_daemon>yes</run_daemon> + <log_path>/var/log/osquery/osqueryd.results.log</log_path> + <config_path>/etc/osquery/osquery.conf</config_path> + <add_labels>yes</add_labels> + </wodle> + + <!-- System inventory --> + <wodle name="syscollector"> + <disabled>no</disabled> + <interval>1h</interval> + <scan_on_start>yes</scan_on_start> + <hardware>yes</hardware> + <os>yes</os> + <network>yes</network> + <packages>yes</packages> + <ports all="no">yes</ports> + <processes>yes</processes> + + <!-- Database synchronization settings --> + <synchronization> + <max_eps>10</max_eps> + </synchronization> + </wodle> + + <sca> + <enabled>yes</enabled> + <scan_on_start>yes</scan_on_start> + <interval>12h</interval> + <skip_nfs>yes</skip_nfs> + </sca> + + <!-- File integrity monitoring --> + <syscheck> + <disabled>no</disabled> + + <!-- Frequency that syscheck is executed default every 12 hours --> + <frequency>43200</frequency> + + <scan_on_start>yes</scan_on_start> + + <!-- Directories to check (perform all possible verifications) --> + <directories>/etc,/usr/bin,/usr/sbin</directories> + <directories>/bin,/sbin,/boot</directories> + + <!-- Files/directories to ignore --> + <ignore>/etc/mtab</ignore> + <ignore>/etc/hosts.deny</ignore> + <ignore>/etc/mail/statistics</ignore> + <ignore>/etc/random-seed</ignore> + <ignore>/etc/random.seed</ignore> + <ignore>/etc/adjtime</ignore> + <ignore>/etc/httpd/logs</ignore> + <ignore>/etc/utmpx</ignore> + <ignore>/etc/wtmpx</ignore> + <ignore>/etc/cups/certs</ignore> + <ignore>/etc/dumpdates</ignore> + <ignore>/etc/svc/volatile</ignore> + + <!-- File types to ignore --> + <ignore type="sregex">.log$|.swp$</ignore> + + <!-- Check the file, but never compute the diff --> + <nodiff>/etc/ssl/private.key</nodiff> + + <skip_nfs>yes</skip_nfs> + <skip_dev>yes</skip_dev> + <skip_proc>yes</skip_proc> + <skip_sys>yes</skip_sys> + + <!-- Nice value for Syscheck process --> + <process_priority>10</process_priority> + + <!-- Maximum output throughput --> + <max_eps>100</max_eps> + + <!-- Database synchronization settings --> + <synchronization> + <enabled>yes</enabled> + <interval>5m</interval> + <max_interval>1h</max_interval> + <max_eps>10</max_eps> + </synchronization> + </syscheck> + + <!-- Log analysis --> + <localfile> + <log_format>command</log_format> + <command>df -P</command> + <frequency>360</frequency> + </localfile> + + <localfile> + <log_format>full_command</log_format> + <command>netstat -tulpn | sed 's/\([[:alnum:]]\+\)\ \+[[:digit:]]\+\ \+[[:digit:]]\+\ \+\(.*\):\([[:digit:]]*\)\ \+\([0-9\.\:\*]\+\).\+\ \([[:digit:]]*\/[[:alnum:]\-]*\).*/\1 \2 == \3 == \4 \5/' | sort -k 4 -g | sed 's/ == \(.*\) ==/:\1/' | sed 1,2d</command> + <alias>netstat listening ports</alias> + <frequency>360</frequency> + </localfile> + + <localfile> + <log_format>full_command</log_format> + <command>last -n 20</command> + <frequency>360</frequency> + </localfile> + + <!-- Active response --> + <active-response> + <disabled>no</disabled> + <ca_store>etc/wpk_root.pem</ca_store> + <ca_verification>yes</ca_verification> + </active-response> + + <!-- Choose between "plain", "json", or "plain,json" for the format of internal logs --> + <logging> + <log_format>plain</log_format> + </logging> + + <labels> + <label key="piacere-deployment-id">123e4567-e89b-12d3-a456-426614174001</label> + </labels> +</ossec_config> + +<ossec_config> + <localfile> + <log_format>audit</log_format> + <location>/var/log/audit/audit.log</location> + </localfile> + + <localfile> + <log_format>syslog</log_format> + <location>/var/ossec/logs/active-responses.log</location> + </localfile> + + <localfile> + <log_format>syslog</log_format> + <location>/var/log/messages</location> + </localfile> + + <localfile> + <log_format>syslog</log_format> + <location>/var/log/secure</location> + </localfile> + + <localfile> + <log_format>syslog</log_format> + <location>/var/log/maillog</location> + </localfile> + +</ossec_config> \ No newline at end of file diff --git a/templates/ansible/cross-platform/security_monitoring/inventory.j2 b/templates/ansible/cross-platform/security_monitoring/inventory.j2 new file mode 100644 index 0000000..ac48c34 --- /dev/null +++ b/templates/ansible/cross-platform/security_monitoring/inventory.j2 @@ -0,0 +1,9 @@ + + +[servers_for_security_monitoring] +demo-server-for-agents + +[servers_for_security_monitoring:vars] +ansible_connection=ssh +ansible_user=ubuntu +ansible_ssh_private_key_file=ssh_key diff --git a/templates/ansible/cross-platform/security_monitoring/inventory.txt b/templates/ansible/cross-platform/security_monitoring/inventory.txt new file mode 100644 index 0000000..eac7201 --- /dev/null +++ b/templates/ansible/cross-platform/security_monitoring/inventory.txt @@ -0,0 +1,5 @@ +[docker] +localhost + +[sma_host] +localhost ansible_user=vagrant ansible_password=vagrant ansible_port=2222 \ No newline at end of file diff --git a/templates/ansible/cross-platform/security_monitoring/main.yml b/templates/ansible/cross-platform/security_monitoring/main.yml new file mode 100644 index 0000000..40b8c6f --- /dev/null +++ b/templates/ansible/cross-platform/security_monitoring/main.yml @@ -0,0 +1 @@ +- import_playbook: deploy-wazuh-agent.yml \ No newline at end of file diff --git a/templates/ansible/cross-platform/security_monitoring/ssh_key.j2 b/templates/ansible/cross-platform/security_monitoring/ssh_key.j2 new file mode 100644 index 0000000..86a197b --- /dev/null +++ b/templates/ansible/cross-platform/security_monitoring/ssh_key.j2 @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA1FrTNE42EgZr9WJNMtvpKFHYhPUJ4lzEp83EM0jYY3TyjmIe +ThMuqMLAHCk22fl4a8PttucggJ5ZWKhcJh623/y8AybJcmqZgq9a41Q609dmirf0 +7frCl+6zL8Mqy2Le2BD4eRADcq11s8r8Ys6J+EBPHQgEnK9CeZLSc/WFRlVr4bOD +s0bEouDxjTAMYjYcpsCwqYgGdIXI9WWsnt3RvcEe8CaiTqoyDN8ZtgkG6MweSrTQ +js8ySHO6o25cOoF7aT9Ihhf32I+KUanNIOvk3RAw2z1FK5xkFbbqMggZqz7rJn3M +sn2dDiCQi2CWox2OYXV/jJKLC3UFuOX64fS9cwIDAQABAoIBAQCs69Tm1/Vx0ibh +aA4DJ06C1bsh8cP9v5soJgfp1xzWSGooBcA1xasOI6B6jhkrgNlNr/uIIEe4VLne +1yJKrGIwnUagrloGQMYGxDKXwYQx80p+FXRuwe7p96eUcjIL8tQSUCd1tdOI87VQ +FjBVaWiybfO+aUQQLytLgoK7iKfhb7vO+9F+ZK1iDjBDNxFuiOM5zoeWOI7boYkD +2yXIkwoBePS2rosbPLa649sVakKex2WhQdUFst4Zba2RhnWQBXUY44LvEK5TzScF +FyYphPOUSplbzzM2+fuOna91NIWmJyHmf15lj7X9kC66XFIZMlvapksB8stEpDiA +4al3IdBJAoGBAPPuM3xkr/kQYYn7E42fgpmINZ78V48gMLhpyUOabZtF8inMyMPB +q7kfHns8Vx0ET8grSNr3qwDDV82lwvGqRCFChASMdQRR9LanydkDSeqpuZyQtVlt +A/65YUdcNY7Vy+M+fRh5Srh/6qcO3beLeLWXbJ4RHBP/OEmHuF4mLfgVAoGBAN7c +qdxTOhXPvOU69Bs5rJdfo6qBI1Yg8MCGctsUwPFpw1kW773ROOPa6XI6D74Dsdg8 +ypZ+IC3pRVtx61Xi3NOwxWNTTG+dyUgTSFz+WKjywXZXeHIbWngiFqk8JFYQWPzk +6YaJk4tZhk2YuNNaCCYRgQqyWv8coEurRlMXZHlnAoGBALcJwdaQ0z8oXJimL4jw +7ZX5kIrpPWanuAdZUe4Jfj+qX8mf4fKKbCowQLYmlBOw/ZDtcfDlMYsUCdnFjZ+7 +rP3sJJYpM1F3khJRm3PdNOUCUMY8C+i7lejZADcE6SdyJFkztbjcowYI7nJHBHZL +ENvqcVW27wPOWlVKozz6lzn1AoGALVwmaoS6DtRwcwuzwZLUkR7TNhIAujgMKHN1 +DyhDOR+4tfpYI39hH+dfmnM83wTrfsKozUawkAepqToflySMo72X/2Zl6VXpMPVT +xjGyo/h87fRRvI/asxblG9702luLcTW6XjrEQBmhn0uVWtc5T15CsIWqxb/y1FPx +BVp+hcMCgYAlJXbjzjbbDoIOCsXPSPe9voBL8zVwp0aNuvQcuB/vCt1n1c1DWuPr +AGMy/fRwY0Znag+ODMuulm7RgXUQy6ifJHiz9cKVGg/mGifaJSjgC+1AI9HFlij3 +asM5CueU0gK974rDxQkwmIWpRH57+kf6s8tGDrPPvqX9S4p3oxFlTw== +-----END RSA PRIVATE KEY----- diff --git a/templates/ansible/cross-platform/security_monitoring/vars.yml b/templates/ansible/cross-platform/security_monitoring/vars.yml new file mode 100644 index 0000000..339a384 --- /dev/null +++ b/templates/ansible/cross-platform/security_monitoring/vars.yml @@ -0,0 +1,13 @@ +--- +service_config_dir: "{{ ansible_env.HOME }}/piacere-wazuh-agent" +docker_image_build_dir: "{{ ansible_env.HOME }}/piacere-wazuh-agent/image" +wazuh_manager_hostname: "{{ lookup('env', 'WAZUH_MANAGER_HOST' ) }}" +wazuh_manager_port: "{{ lookup('env', 'WAZUH_MANAGER_PORT' ) }}" + +wazuh_agent_network: "security-monitoring-deployment_default" +wazuh_agent_name: "wazuh-agent-container-2" +wazuh_agent_group: "default" +wazuh_agent_config_volume: "{{ service_config_dir }}/ossec.conf:/var/ossec/etc/ossec.conf" +wazuh_agent_image_name: "wazuh-agent-image" + +piacere_deployment_id: "{{ lookup('env', 'DEPLOYMENT_ID' ) }}" diff --git a/templates/ansible/ubuntu/config.tpl b/templates/ansible/ubuntu/config.tpl index 91ddcce..29f289e 100644 --- a/templates/ansible/ubuntu/config.tpl +++ b/templates/ansible/ubuntu/config.tpl @@ -15,8 +15,20 @@ #} --- input: - - instance_ip_{{ node.infra_element_name }} - - instance_server_private_key_{{ node.credentials }} + {%- if name == "performance_monitoring" %} + - INFLUXDB_BUCKET + - INFLUXDB_TOKEN + - INFLUXDB_ORG + - INFLUXDB_ADDR + - DEPLOYMENT_ID{%- endif %} + {%- if name == "security_monitoring" %} + - DEPLOYMENT_ID + - WAZUH_MANAGER_HOST + - WAZUH_MANAGER_PORT{%- endif %} + {%- for node in nodes %} + - instance_server_public_ip_{{ node.infra_element_name }} + - instance_server_private_key_{{ node.credentials }}_{{ node.infra_element_name }} + {%- endfor %} output: [] engine: ansible ... diff --git a/templates/ansible/ubuntu/elasticsearch.tpl b/templates/ansible/ubuntu/elasticsearch.tpl new file mode 100644 index 0000000..05d0432 --- /dev/null +++ b/templates/ansible/ubuntu/elasticsearch.tpl @@ -0,0 +1,21 @@ +--- + +- name: Elasticsearch with custom configuration + hosts: servers_for_elasticsearch + roles: + - role: elastic.elasticsearch + vars: + es_data_dirs: + - "/opt/elasticsearch/data" + es_log_dir: "/opt/elasticsearch/logs" + es_config: + node.name: "node1" + cluster.name: "custom-cluster" + discovery.seed_hosts: "localhost:9301" + http.port: 9201 + transport.port: 9301 + node.data: false + node.master: true + bootstrap.memory_lock: true + es_heap_size: 1g + es_api_port: 9201 \ No newline at end of file diff --git a/templates/ansible/ubuntu/elasticsearch_main.tpl b/templates/ansible/ubuntu/elasticsearch_main.tpl new file mode 100644 index 0000000..5a0e4fd --- /dev/null +++ b/templates/ansible/ubuntu/elasticsearch_main.tpl @@ -0,0 +1,12 @@ +--- + +- hosts: localhost + + pre_tasks: + - file: + path: roles + state: absent + + - command: ansible-galaxy install elastic.elasticsearch,v7.17.0 + tasks: + - command: ansible-playbook -i inventory elasticsearch.yml \ No newline at end of file diff --git a/templates/ansible/ubuntu/inventory.tpl b/templates/ansible/ubuntu/inventory.tpl index 2c64481..9988722 100644 --- a/templates/ansible/ubuntu/inventory.tpl +++ b/templates/ansible/ubuntu/inventory.tpl @@ -15,7 +15,9 @@ #} [{{ "servers_for_" ~ name }}] -{% raw %}{{ instance_ip_{% endraw %}{{ node.infra_element_name }} {% raw %}}}{% endraw %} +{%- for node in nodes %} +{% raw %}{{ instance_server_public_ip_{% endraw %}{{ node.infra_element_name }} {% raw %}}}{% endraw %} +{%- endfor %} [{{ "servers_for_" ~ name }}:vars] ansible_connection=ssh diff --git a/templates/ansible/ubuntu/performance_monitoring_main.tpl b/templates/ansible/ubuntu/performance_monitoring_main.tpl new file mode 100644 index 0000000..8b32d63 --- /dev/null +++ b/templates/ansible/ubuntu/performance_monitoring_main.tpl @@ -0,0 +1,42 @@ +--- +- hosts: localhost + tasks: + - name: print disclamer + debug: + msg: this can also be done with "ansible-galaxy install -r requirements" + - name: install telegraf from galaxy + community.general.ansible_galaxy_install: + type: role + requirements_file: ansible_requirements.yml + +- hosts: servers_for_performance_monitoring + pre_tasks: + - name: Check parameters + fail: + msg: 'variable {{item}} not defined' + when: item is not defined + with_items: + - pma_deployment_id + - pma_influxdb_bucket + - pma_influxdb_token + - pma_influxdb_org + - pma_influxdb_addr + - name: Print parameters + debug: + msg: + - "pma_deployment_id: {{ pma_deployment_id }}" + - "pma_influxdb_bucket: {{ pma_influxdb_bucket }}" + - "pma_influxdb_token: {{ pma_influxdb_token }}" + - "pma_influxdb_org: {{ pma_influxdb_org }}" + - "pma_influxdb_addr: {{ pma_influxdb_addr }}" + - name: Ensure gnupg package + package: + name: gnupg + state: present + become: true + vars_files: + - vars/main.yaml + tasks: + - name: Install telegraf + ansible.builtin.include_role: + name: dj-wasabi.telegraf diff --git a/templates/ansible/ubuntu/security_monitoring_main.tpl b/templates/ansible/ubuntu/security_monitoring_main.tpl new file mode 100644 index 0000000..43a939e --- /dev/null +++ b/templates/ansible/ubuntu/security_monitoring_main.tpl @@ -0,0 +1,2 @@ +--- +- import_playbook: deploy-wazuh-agent.yml \ No newline at end of file diff --git a/templates/ansible/ubuntu/ssh_key.tpl b/templates/ansible/ubuntu/ssh_key.tpl index 798ef20..d7a25b0 100644 --- a/templates/ansible/ubuntu/ssh_key.tpl +++ b/templates/ansible/ubuntu/ssh_key.tpl @@ -1 +1,4 @@ -{% raw %}{{ instance_server_private_key_{% endraw %}{{ node.credentials }} {% raw %}}}{% endraw %} \ No newline at end of file +{%- for node in nodes %} +{%- raw %}{{ instance_server_private_key_{% endraw %}{{ node.credentials }}{% raw %}_{%- endraw %}{{node.infra_element_name}}{% raw %}}} +{% endraw %} +{%- endfor %} \ No newline at end of file diff --git a/templates/common/gaiax_self_description.yaml.tpl b/templates/common/gaiax_self_description.yaml.tpl new file mode 100644 index 0000000..5403076 --- /dev/null +++ b/templates/common/gaiax_self_description.yaml.tpl @@ -0,0 +1,65 @@ +{ + "@context": { + "gax-participant": "http://w3id.org/gaia-x/participant#", + "gax-service": "http://w3id.org/gaia-x/service#", + "dct": "http://purl.org/dc/terms/", + "sh": "http://www.w3.org/ns/shacl#", + "xsd": "http://www.w3.org/2001/XMLSchema#", + "gax-validation": "http://w3id.org/gaia-x/validation#", + "gax-node": "http://w3id.org/gaia-x/node#", + "vcard": "http://www.w3.org/2006/vcard/ns#", + "dcat": "http://www.w3.org/ns/dcat#", + "gax-resource": "http://w3id.org/gaia-x/resource#", + "gax-core": "http://w3id.org/gaia-x/core#" + }, + "@id": "ServiceOffering-{{'{:0=10}'.format(range(0, 9999999999) | random) }}", + "@type": "gax-service:ServiceOffering", + "gax-service:hasServiceTitle": { + {% set list = output_path.split('/') %} + "@value": "{{ list[1] }}", + "@type": "xsd:string" + }, + "dct:description": { + "@value": "Gaia-X self-description generated by PIACERE ICG", + "@type": "xsd:string" + }, + "dcat:keyword": [ + "Service", + {%- for step in steps %} {% if step.data.provider_info is defined %} + {%- if step.data.provider_info[0].provider_name is defined %} + "{{ step.data.provider_info[0].provider_name }}", + {%- endif %}{%- endif %}{% endfor %} + "IaC", + "Terraform" + ], + "gax-service:maintainedBy": [ + "PIACERE Community" + ], + "gax-service:hasProvisionType": { + "@value": "Hybrid", + "@type": "xsd:string" + }, + "gax-service:hostedOn": { + {%- for step in steps %} {% if step.data.provider_info is defined %} + {%- if step.data.provider_info[0].provider_name is defined %} + "@id": "{{ step.data.provider_info[0].provider_name }}", + {%- endif %}{%- endif %}{% endfor %} + "@type": "gax-participant:Provider" + }, + "gax-service:infrastructureAsCode": [ + { + "@type": "iac:DOML", + "iac:url": { + "@value": {% raw %}"{{User placeholder: URL to the DOML source in Git}}",{% endraw %} + "@type": "xsd:anyURI" + } + }, + { + "@type": "iac:terraform", + "iac:url": { + "@value": {% raw %}"{{User placeholder: URL to the ICG result}}",{% endraw %} + "@type": "xsd:anyURI" + } + } + ] +} diff --git a/templates/docker_compose/config.tpl b/templates/docker_compose/config.tpl new file mode 100644 index 0000000..a41fd52 --- /dev/null +++ b/templates/docker_compose/config.tpl @@ -0,0 +1,24 @@ +{# Copyright 2022 Hewlett Packard Enterprise Development LP +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#------------------------------------------------------------------------- +#} +--- +input: + {%- for config in configs %} + - instance_server_public_ip_{{ config.host.name }} + - instance_server_private_key_{{ config.host.credentials }}_{{ config.host.name }} + {%- endfor %} +output: [] +engine: ansible +... diff --git a/templates/docker_compose/docker_compose.tpl b/templates/docker_compose/docker_compose.tpl new file mode 100644 index 0000000..239c40b --- /dev/null +++ b/templates/docker_compose/docker_compose.tpl @@ -0,0 +1,9 @@ +version: '3' +services: + {{ name }}: + image: {{ generatedFrom.uri }} + restart: on-failure + ports: + {%- for config in configs %} + - "{{ "127.0.0.1:" ~ config.container_port }}:{{ config.iface.name }}:{{ config.vm_port }}" + {%- endfor %} diff --git a/templates/docker_compose/inventory.tpl b/templates/docker_compose/inventory.tpl new file mode 100644 index 0000000..5687052 --- /dev/null +++ b/templates/docker_compose/inventory.tpl @@ -0,0 +1,27 @@ +{# Copyright 2022 Hewlett Packard Enterprise Development LP +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#------------------------------------------------------------------------- +#} + +[{{ "servers_for_" ~ name }}] +{%- for config in configs %} +{% raw %}{{ instance_server_public_ip_{% endraw %}{{ config.host.name }} {% raw %}}}{% endraw %} +{%- endfor %} + +[{{ "servers_for_" ~ name }}:vars] +ansible_connection=ssh +{%- for config in configs %} +ansible_user={%- if "ubuntu" in config.host.os.lower() %}ubuntu{%- elif "centos" in config.host.os.lower() %}centos{%- endif %} +{%- endfor %} +ansible_ssh_private_key_file=ssh_key diff --git a/templates/docker_compose/main.tpl b/templates/docker_compose/main.tpl new file mode 100644 index 0000000..60809d8 --- /dev/null +++ b/templates/docker_compose/main.tpl @@ -0,0 +1,12 @@ +--- +- hosts: {{ "servers_for_" ~ name }} + gather_facts: no + become: yes + tasks: + - name: Copy over docker compose + copy: + src: docker_compose.yml + dest: . + - name: Deploy application + docker_compose: + definition: docker_compose.yml diff --git a/templates/docker_compose/ssh_key.tpl b/templates/docker_compose/ssh_key.tpl new file mode 100644 index 0000000..fd56742 --- /dev/null +++ b/templates/docker_compose/ssh_key.tpl @@ -0,0 +1,4 @@ +{%- for config in configs %} +{%- raw %}{{ instance_server_private_key_{% endraw %}{{ config.host.credentials }}{% raw %}_{%- endraw %}{{config.host.name}}{% raw %} }} +{% endraw %} +{%- endfor %} diff --git a/templates/terraform/aws/autoscaling_group.tpl b/templates/terraform/aws/autoscaling_group.tpl new file mode 100644 index 0000000..c46e66e --- /dev/null +++ b/templates/terraform/aws/autoscaling_group.tpl @@ -0,0 +1,19 @@ +resource "aws_launch_template" "{{infra_element_name}}" { + name_prefix = "{{name}}_" + //image_id = "ami-1a2b3c" + {%- for key, value in context().items() %}{% if not callable(value)%}{%if key.lower().startswith('virtualmachine') %} + instance_type = "{{value.sizeDescription}}" + image_id = "{{value.os}}" + {%- endif %}{% endif %}{% endfor %} +} + +resource "aws_autoscaling_group" "{{infra_element_name}}" { + desired_capacity = {{min}} + max_size = {{max}} + min_size = {{min}} + + launch_template { + id = aws_launch_template.{{infra_element_name}}.id + version = "$Latest" + } +} \ No newline at end of file diff --git a/templates/terraform/aws/config.tpl b/templates/terraform/aws/config.tpl new file mode 100644 index 0000000..8a4c878 --- /dev/null +++ b/templates/terraform/aws/config.tpl @@ -0,0 +1,29 @@ +{# Copyright 2022 Hewlett Packard Enterprise Development LP +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#------------------------------------------------------------------------- +#} + +--- +engine: terraform +input: + - AWS_ACCESS_KEY_ID + - AWS_SECRET_ACCESS_KEY + - AWS_REGION +output: +{%- for vm in vms %} + - instance_server_public_key_{{ vm.credentials }}_{{ vm.infra_element_name }} + - instance_server_public_ip_{{ vm.infra_element_name }} + - instance_public_dns_{{ vm.infra_element_name }} +{% endfor %} +... diff --git a/templates/terraform/aws/network.tpl b/templates/terraform/aws/network.tpl index 20c8201..1a53ddd 100644 --- a/templates/terraform/aws/network.tpl +++ b/templates/terraform/aws/network.tpl @@ -13,27 +13,31 @@ # limitations under the License. #------------------------------------------------------------------------- #} - +#VPC resource "aws_vpc" "{{infra_element_name}}" { cidr_block = "{{ addressRange }}" tags = { Name = "{{name}}" } } - -resource "aws_subnet" "{{infra_element_name ~ "_subnet"}}" { +{##-------- Subnets Here ##} +{% for key, value in context().items() %}{% if not callable(value)%}{%if key.startswith('Subnet') %} +# Subnet +resource "aws_subnet" "{{value.name ~ "_subnet"}}" { vpc_id = aws_vpc.{{infra_element_name}}.id - cidr_block = "{{vpc_subnet.addressRange}}" - # map_public_ip_on_launch = true + cidr_block = "{{value.addressRange}}" + map_public_ip_on_launch = false tags = { - Name = "{{name}}" + Name = "{{value.name}}" } -} - -resource "aws_network_interface" {{infra_element_name ~ "_network_interface"}} { - subnet_id = aws_subnet.{{infra_element_name ~ "_subnet"}}.id - security_groups = [aws_security_group.{{ name ~ "_security_group_rule" }}.id] ##TOBECHANGED +}{% endif %}{% endif %}{% endfor %} +{##-------- Internet Gateway Subnets Here ##} +{% for key, value in context().items() %}{% if not callable(value)%}{%if key.startswith('InternetGateway') %} +# Internet Gateway Subnet +resource "aws_internet_gateway" "{{value.name ~ "_internet_gw_subnet"}}" { + vpc_id = aws_vpc.{{infra_element_name}}.id tags = { - Name = "{{infra_element_name ~ "_network_interface"}}" + Name = "{{value.name}}" } -} +}{% endif %}{% endif %}{% endfor %} + diff --git a/templates/terraform/aws/port_rule.tpl b/templates/terraform/aws/port_rule.tpl index 7d2f8f8..4bee5b6 100644 --- a/templates/terraform/aws/port_rule.tpl +++ b/templates/terraform/aws/port_rule.tpl @@ -15,16 +15,16 @@ #} # CREATING SECURITY_GROUP -resource "aws_security_group" "{{ infra_element_name ~ "_security_group_rule" }}" { ## TOBECHANGE +resource "aws_security_group" "{{ infra_element_name ~ "_security_group" }}" { name = "{{ infra_element_name }}" # description = "Security group rule for port {{ fromPort }}" - vpc_id = aws_vpc.{{vpc_name}}.id ##ADD VPC NAME REFERENCE + vpc_id = aws_vpc.{{vpc_name}}.id ##MISSING VPC NAME REFERENCE FROM DOML {% for key, value in context().items() %}{% if not callable(value)%} {%if value.kind and value.kind is defined %} {% if value == "INGRESS" %} ingress {% else %} egress {% endif %} { from_port = {{ value.fromPort }} to_port = {{ value.toPort }} protocol = "{{ value.protocol }}" - cidr_blocks = [{% for range in value.cidr %}"{{ range }}"{% endfor %}] + cidr_blocks = [{% for range in value.cidr %}"{{ range }}",{% endfor %}] } {% endif %}{% endif %}{% endfor %} } \ No newline at end of file diff --git a/controller/test_Orchestrator.py b/templates/terraform/aws/ssh_key.tpl similarity index 76% rename from controller/test_Orchestrator.py rename to templates/terraform/aws/ssh_key.tpl index 2544c45..e953e39 100644 --- a/controller/test_Orchestrator.py +++ b/templates/terraform/aws/ssh_key.tpl @@ -1,4 +1,4 @@ -# Copyright 2022 Hewlett Packard Enterprise Development LP +{# Copyright 2022 Hewlett Packard Enterprise Development LP # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -12,11 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. #------------------------------------------------------------------------- - -from unittest import TestCase - - -class TestOrchestrator(TestCase): - - def test_create_iac_from_doml(self): - self.fail() +#} +resource "aws_key_pair" "{{infra_element_name}}" { + key_name = "{{infra_element_name}}" + public_key = "{{keyfile}}" +} \ No newline at end of file diff --git a/templates/terraform/aws/temp.tpl b/templates/terraform/aws/temp.tpl new file mode 100644 index 0000000..e69de29 diff --git a/templates/terraform/aws/virtual_machine.tpl b/templates/terraform/aws/virtual_machine.tpl index 67d49a3..e3307dd 100644 --- a/templates/terraform/aws/virtual_machine.tpl +++ b/templates/terraform/aws/virtual_machine.tpl @@ -14,13 +14,12 @@ #------------------------------------------------------------------------- #} -resource "aws_instance" "{{name}}" { +resource "aws_instance" "{{infra_element_name}}" { ami = "{{ os }}" - instance_type = "{{ instance_type }}" - key_name = "{{ssh_key_name}}" - - network_interface { - network_interface_id = aws_network_interface.{{i1.belongsTo ~ "_network_interface"}}.id - device_index = 0 + instance_type = "{% if 'vm_flavor' in context().keys() %}{{ vm_flavor }}{% else %}{{ instance_type }}{% endif %}" + key_name = "{{credentials}}" + {% if 'group' in context().keys() %}vpc_security_group_ids = [aws_security_group.{{group ~ "_security_group"}}.id]{% endif %} + tags = { + "Name" = "{{name}}" } } diff --git a/templates/terraform/aws/virtual_machine_out.tpl b/templates/terraform/aws/virtual_machine_out.tpl new file mode 100644 index 0000000..f7da256 --- /dev/null +++ b/templates/terraform/aws/virtual_machine_out.tpl @@ -0,0 +1,27 @@ +{# Copyright 2022 Hewlett Packard Enterprise Development LP +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#------------------------------------------------------------------------- +#} + +output "instance_server_public_key_{{ credentials }}_{{ infra_element_name }}" { + value = aws_key_pair.{{ credentials }}.public_key +} + +output "instance_server_public_ip_{{ infra_element_name }}" { + value = aws_instance.{{infra_element_name}}.public_ip +} + +output "instance_public_dns_{{ infra_element_name }}" { + value = aws_instance.{{infra_element_name}}.public_dns +} \ No newline at end of file diff --git a/templates/terraform/azure/port_rule.tpl b/templates/terraform/azure/port_rule.tpl new file mode 100644 index 0000000..8ada56b --- /dev/null +++ b/templates/terraform/azure/port_rule.tpl @@ -0,0 +1,37 @@ +{# Copyright 2022 Hewlett Packard Enterprise Development LP +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#------------------------------------------------------------------------- +#} + +resource "azurerm_network_security_group" " {{ infra_element_name ~ "_security_group" }}" { + name = " {{ infra_element_name }}" + location = azurerm_resource_group. {{ resource_name }}.location + resource_group_name = azurerm_resource_group. {{ resource_name }}.name + {%- for key, value in context().items() %}{% if not callable(value)%} {%if value.kind and value.kind is defined %} + security_rule { + name = "{{ value.name }}" + priority = 100 + direction = "{% if value == "INGRESS" %} Inbound {% else %} Outbound {% endif %} " + access = "Allow" + protocol = "{{ value.protocol }}"" + source_port_range = {{ value.fromPort }}" + destination_port_range = "{{ value.toPort }}"" + source_address_prefix = "{% for range in value.cidr %}"{{ range }}"{% endfor %}" + destination_address_prefix = "{% for range in value.cidr %}"{{ range }}"{% endfor %}" + } + {%- endif %}{% endif %}{% endfor %} + tags = { + environment = "Production" + } +} diff --git a/templates/terraform/google_cloud/vm.tpl b/templates/terraform/google_cloud/vm.tpl index bd42a80..d908534 100644 --- a/templates/terraform/google_cloud/vm.tpl +++ b/templates/terraform/google_cloud/vm.tpl @@ -16,12 +16,12 @@ resource "google_compute_instance" "{{ default }}" { name = "{{ name }}" - machine_type = "{{ machine_type }}" - zone = "{{ zone }}" + machine_type = "{% if 'vm_flavor' in context().keys() %}{{ vm_flavor }}{% else %}{{ instance_type }}{% endif %}" + zone = "{% if 'vm_Zone' in context().keys() %}{{ vm_Zone }}{% else %}{{ zone }}{% endif %}" boot_disk { initialize_params { - image = "debian-cloud/debian-9" + image = "{{ os }}" #"debian-cloud/debian-9" } } diff --git a/templates/terraform/ionos/config.tpl b/templates/terraform/ionos/config.tpl new file mode 100644 index 0000000..51cc877 --- /dev/null +++ b/templates/terraform/ionos/config.tpl @@ -0,0 +1,28 @@ +{# Copyright 2022 Hewlett Packard Enterprise Development LP +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#------------------------------------------------------------------------- +#} + +--- +engine: terraform +input: + - IONOS_USERNAME + - IONOS_PASSWORD + - IONOS_API_URL +output: +{% for vm in vms %} + - instance_server_password_{{ vm.infra_element_name }} + - instance_public_ip_{{ vm.infra_element_name }} +{% endfor %} +... diff --git a/templates/terraform/ionos/datacenter.tpl b/templates/terraform/ionos/datacenter.tpl new file mode 100644 index 0000000..a30fb00 --- /dev/null +++ b/templates/terraform/ionos/datacenter.tpl @@ -0,0 +1,21 @@ +{# Copyright 2022 Hewlett Packard Enterprise Development LP +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#------------------------------------------------------------------------- +#} + +resource "ionoscloud_datacenter" "dc_for_vm" { + name = "{{gname}}" + location = "{% if 'vm_Region' in context().keys() %}{{ vm_Region }}{% else %}de/txl{% endif %}" + description = "Piacere demo with IONOS, Terraform and Gaia-X orchestrator" +} diff --git a/templates/terraform/ionos/init.tpl b/templates/terraform/ionos/init.tpl new file mode 100644 index 0000000..07b31d3 --- /dev/null +++ b/templates/terraform/ionos/init.tpl @@ -0,0 +1,25 @@ +{# Copyright 2022 Hewlett Packard Enterprise Development LP +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#------------------------------------------------------------------------- +#} + +terraform { + required_version = ">= 1.0.0" + required_providers { + ionoscloud = { + source = "ionos-cloud/ionoscloud" + version = "{{ _version_ }}" + } + } +} \ No newline at end of file diff --git a/templates/terraform/ionos/network.tpl b/templates/terraform/ionos/network.tpl new file mode 100644 index 0000000..e17073d --- /dev/null +++ b/templates/terraform/ionos/network.tpl @@ -0,0 +1,22 @@ +{# Copyright 2022 Hewlett Packard Enterprise Development LP +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#------------------------------------------------------------------------- +#} + +# lan connected to the server in the nic defined on the server +resource "ionoscloud_lan" "lan_for_{{ infra_element_name }}" { + name = "LAN for Piacere demo" + datacenter_id = ionoscloud_datacenter.dc_for_{{ infra_element_name }}.id + public = true +} \ No newline at end of file diff --git a/templates/terraform/ionos/virtual_machine.tpl b/templates/terraform/ionos/virtual_machine.tpl new file mode 100644 index 0000000..b95df2f --- /dev/null +++ b/templates/terraform/ionos/virtual_machine.tpl @@ -0,0 +1,115 @@ +{# Copyright 2022 Hewlett Packard Enterprise Development LP +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#------------------------------------------------------------------------- +#} + +# we need 1 public ip +resource "ionoscloud_ipblock" "public_ip_{{ infra_element_name }}" { + name = "Public IP for Piacere demo" + location = ionoscloud_datacenter.dc_for_vm.location + size = 1 +} + +# Create virtual machine +resource "ionoscloud_server" "{{ infra_element_name }}" { + name = "{{ name }}" + datacenter_id = ionoscloud_datacenter.dc_for_vm.id + cores = {% if 'vm_Virtual_CPU_Cores' in context().keys() %}{{ vm_Virtual_CPU_Cores }}{% else %}{{ cpu_count }}{% endif %} + ram = {% if 'vm_Memory' in context().keys() %}{{ vm_Memory }}{% else %}{{ memory_mb }}{% endif %} + image_name = "{% if 'vm_Flavor' in context().keys() %}{{ vm_Flavor }}{% else %}{{ os }}{% endif %}" + availability_zone = "AUTO" + ssh_key_path = [ + "./ssh_key" + ] + nic { + lan = ionoscloud_lan.lan_for_{{ infra_element_name }}.id + dhcp = true + firewall_active = false + name = "public_nic_{{ infra_element_name }}" + ips = [ + ionoscloud_ipblock.public_ip_{{ infra_element_name }}.ips[0]] + } + volume { + # /dev/vda1 + name = "main-hdd" + size = {% if 'vm_Instance_Storage' in context().keys() %}{{ vm_Instance_Storage }}{% else %}{{ storage }}{% endif %} + disk_type = "HDD" + user_data = base64encode(<<EOF +#!/bin/bash +apt update +apt -y install ffmpeg vlc wget iproute2 + +adduser vlc-user +usermod -aG sudo vlc-user + +mkdir -p /srv/piacere /srv/piacere/pipes /srv/piacere/data + +wget -O /srv/piacere/data/piacere.mkv "https://koofr.islonline.com/content/links/6541b213-ddc8-4d71-7369-f70642b9d73f/files/get/piacere.mkv?path=%2F" + +mkfifo /srv/piacere/pipes/pipe1 + +tee /srv/piacere/loop-and-transcode.sh <<LAT >/dev/null +#!/usr/bin/env bash + +exec ffmpeg \ + -nostdin \ + -re \ + -y \ + -fflags +genpts \ + -stream_loop -1 \ + -i "/srv/piacere/data/piacere.mkv" \ + -g 75 \ + -quality realtime \ + -speed 5 \ + -threads 4 \ + -tile-columns 4 \ + -frame-parallel 1 \ + -row-mt 1 \ + -qmin 4 \ + -qmax 48 \ + -b:v 500k \ + -c:v libvpx-vp9 \ + -an \ + -f webm \ + "/srv/piacere/pipes/pipe1" +LAT + +tee /srv/piacere/http-server.sh <<HS >/dev/null +#!/usr/bin/env bash + +export SUDO_UID=$(id -u vlc-user) +exec vlc-wrapper \ + -I dummy \ + /srv/piacere/pipes/pipe1 \ + --sout '#http{mux=webm, dst=:5001}' \ + --no-sout-all \ + --sout-keep +HS + +chown -R vlc-user:vlc-user /srv/piacere +chmod a+x /srv/piacere/loop-and-transcode.sh /srv/piacere/http-server.sh + +systemd-run \ + --unit=piacere-loop-and-transcode \ + --description="Piacere stream looper and transcoder" \ + /srv/piacere/loop-and-transcode.sh + +systemd-run \ + --unit=piacere-http-server \ + --description="Piacere stream HTTP server" \ + /srv/piacere/http-server.sh + EOF + ) + } +} \ No newline at end of file diff --git a/templates/terraform/ionos/virtual_machine_out.tpl b/templates/terraform/ionos/virtual_machine_out.tpl new file mode 100644 index 0000000..abaf2a5 --- /dev/null +++ b/templates/terraform/ionos/virtual_machine_out.tpl @@ -0,0 +1,27 @@ +{# Copyright 2022 Hewlett Packard Enterprise Development LP +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#------------------------------------------------------------------------- +#} + +output "instance_server_password_{{ infra_element_name }}" { + value = random_password.server_image_password.result +} + +output "instance_server_public_ip_{{ infra_element_name }}" { + value = ionoscloud_ipblock.public_ip_{{ infra_element_name }}.ips[0] +} + +output "ip_address" { + value = ionoscloud_ipblock.public_ip_{{ infra_element_name }}.ips[0] +} \ No newline at end of file diff --git a/templates/terraform/open_stack/agents_playbook/ansible/playbooks/pma/ansible_requirements.yml b/templates/terraform/open_stack/agents_playbook/ansible/playbooks/pma/ansible_requirements.yml index 58c0cb3..47808cf 100644 --- a/templates/terraform/open_stack/agents_playbook/ansible/playbooks/pma/ansible_requirements.yml +++ b/templates/terraform/open_stack/agents_playbook/ansible/playbooks/pma/ansible_requirements.yml @@ -1,8 +1,8 @@ roles: # - name: dj-wasabi.telegraf -# version: 0.13.2 +# version: 0.13.3 # source: https://galaxy.ansible.com - name: dj-wasabi.telegraf src: https://github.com/dj-wasabi/ansible-telegraf.git scm: git - version: 0.13.2 + version: 0.13.3 diff --git a/templates/terraform/open_stack/config.tpl b/templates/terraform/open_stack/config.tpl index 9aa85ac..612e8ae 100644 --- a/templates/terraform/open_stack/config.tpl +++ b/templates/terraform/open_stack/config.tpl @@ -23,8 +23,8 @@ input: - OS_PROJECT_NAME output: {% for vm in vms %} - - instance_server_public_key_{{ vm.credentials }} - - instance_server_private_key_{{ vm.credentials }} - - instance_ip_{{ vm.infra_element_name }} + - instance_server_public_key_{{ vm.credentials }}_{{ vm.infra_element_name }} + - instance_server_private_key_{{ vm.credentials }}_{{ vm.infra_element_name }} + - instance_server_public_ip_{{ vm.infra_element_name }} {% endfor %} ... diff --git a/templates/terraform/open_stack/network.tpl b/templates/terraform/open_stack/network.tpl index 6ab4d6a..e558567 100644 --- a/templates/terraform/open_stack/network.tpl +++ b/templates/terraform/open_stack/network.tpl @@ -13,43 +13,36 @@ # limitations under the License. #------------------------------------------------------------------------- #} +{##-------- Variables ##} +{%- set var_network_name = infra_element_name -%} +{%- set var_security_groups = infra_sgs -%} ## Network # Create Network -resource "openstack_networking_network_v2" "{{ infra_element_name }}" { +resource "openstack_networking_network_v2" "{{ var_network_name }}" { name = "{{ name }}" } -# Create Subnet -resource "openstack_networking_subnet_v2" "{{ infra_element_name ~ "_subnet" }}" { - name = "{{ name ~ "_subnet" }}" - network_id = openstack_networking_network_v2.{{ infra_element_name }}.id - cidr = "{{ addressRange }}" - dns_nameservers = ["8.8.8.8", "8.8.8.4"] +# Create router +resource "openstack_networking_router_v2" "router_{{ var_network_name }}" { + name = "router_{{ var_network_name }}" + external_network_id = data.openstack_networking_network_v2.external.id #External network id } -# Attach networking port -resource "openstack_networking_port_v2" "{{ infra_element_name }}" { - name = "{{ name }}" - network_id = openstack_networking_network_v2.{{ infra_element_name }}.id - admin_state_up = true - security_group_ids = [ - {% for sg in infra_sgs %}openstack_compute_secgroup_v2.{{sg}}.id, - {% endfor %} - ] - fixed_ip { - subnet_id = openstack_networking_subnet_v2.{{ infra_element_name ~ "_subnet" }}.id - } +{##-------- Subnets Here ##} +{%- for key, value in context().items() -%}{%- if not callable(value) -%}{%-if key.startswith('Subnet') -%} +# Subnet +resource "openstack_networking_subnet_v2" "{{ value.name ~ "_subnet" }}" { + name = "{{ value.name ~ "_subnet" }}" + network_id = openstack_networking_network_v2.{{ var_network_name }}.id + cidr = "{{ value.addressRange }}" + dns_nameservers = ["8.8.8.8", "8.8.8.4"] } -# Create router -resource "openstack_networking_router_v2" "{{ infra_element_name ~ "_router" }}" { - name = "{{ infra_element_name ~ "_router" }}" - external_network_id = data.openstack_networking_network_v2.external.id #External network id +# Create router interface on subnet +resource "openstack_networking_router_interface_v2" "router_interface_{{ var_network_name }}_{{ value.name ~ "_subnet" }}" { + router_id = "${openstack_networking_router_v2.router_{{ var_network_name }}.id}" + subnet_id = "${openstack_networking_subnet_v2.{{ value.name ~ "_subnet" }}.id}" } -# Router interface configuration -resource "openstack_networking_router_interface_v2" "{{ infra_element_name ~ "_router_interface" }}" { - router_id = openstack_networking_router_v2.{{ infra_element_name ~ "_router" }}.id - subnet_id = openstack_networking_subnet_v2.{{ infra_element_name ~ "_subnet" }}.id -} \ No newline at end of file +{%-endif %}{% endif %}{% endfor %} \ No newline at end of file diff --git a/templates/terraform/open_stack/port_rule.tpl b/templates/terraform/open_stack/port_rule.tpl index 3d7b10e..3560703 100644 --- a/templates/terraform/open_stack/port_rule.tpl +++ b/templates/terraform/open_stack/port_rule.tpl @@ -15,15 +15,17 @@ #} # CREATING SECURITY_GROUP -{% for key, value in context().items() %}{% if not callable(value)%} {%if value.kind and value.kind is defined %} -resource "openstack_compute_secgroup_v2" "{{ key }}" { - name = "{{ key }}" - description = "Security group rule for port {{ value.fromPort }}" - rule { - from_port = {{ value.fromPort }} - to_port = {{ value.toPort }} - ip_protocol = "{{ value.protocol }}" - cidr = {% for range in value.cidr %}"{{ range }}"{% endfor %} - } +resource "openstack_compute_secgroup_v2" "{{infra_element_name}}" { + name = "infra_element_name" + description = "PIACERE security group created - {{infra_element_name}}" + + {%- for key, value in context().items() %}{% if not callable(value)%} {%if value.kind and value.kind is defined %} + rule { + from_port = {{ value.fromPort }} + to_port = {{ value.toPort }} + ip_protocol = "{{ value.protocol }}" + cidr = {% for range in value.cidr %}"{{ range }}"{% endfor %} + } + {%- endif %}{% endif %}{% endfor %} } -{% endif %}{% endif %}{% endfor %} \ No newline at end of file + diff --git a/templates/terraform/open_stack/ssh_key.tpl b/templates/terraform/open_stack/ssh_key.tpl index a8850d9..633ac35 100644 --- a/templates/terraform/open_stack/ssh_key.tpl +++ b/templates/terraform/open_stack/ssh_key.tpl @@ -17,5 +17,5 @@ # Create ssh keys resource "openstack_compute_keypair_v2" "{{ infra_element_name }}" { name = "{{ user }}" - # public_key = "{{ user }}" + # public_key = "{{ keyfile }}" } \ No newline at end of file diff --git a/templates/terraform/open_stack/virtual_machine.tpl b/templates/terraform/open_stack/virtual_machine.tpl index 9e37772..3a7a4df 100644 --- a/templates/terraform/open_stack/virtual_machine.tpl +++ b/templates/terraform/open_stack/virtual_machine.tpl @@ -16,13 +16,15 @@ # Create virtual machine resource "openstack_compute_instance_v2" "{{ infra_element_name }}" { - name = "{{ vm_name }}" + name = "{{ name }}" image_name = "{{ os }}" - flavor_name = "{{ vm_flavor }}" + flavor_name = "{% if 'sizeDescription' in context().keys() %}{{ sizeDescription }}{% elif 'vm_flavor' in context().keys() %}{{ vm_flavor }}{% else %}{{ instance_type }}{% endif %}" key_pair = openstack_compute_keypair_v2.{{ credentials }}.name + {%- for key, value in context().items() %}{% if not callable(value)%}{%if key.startswith('NetworkInterface') %} network { - port = openstack_networking_port_v2.{{ i1.belongsTo }}.id + port = openstack_networking_port_v2.{{ value.name ~ "_networking_port"}}.id } + {%- endif %}{% endif %}{% endfor %} } # Create floating ip @@ -36,3 +38,28 @@ resource "openstack_compute_floatingip_associate_v2" "{{ infra_element_name ~ "_ floating_ip = openstack_networking_floatingip_v2.{{ infra_element_name ~ "_floating_ip" }}.address instance_id = openstack_compute_instance_v2.{{ infra_element_name }}.id } + +# Router interface configuration +{% for key, value in context().items() %}{% if not callable(value)%}{%- if key.startswith('NetworkInterface') %} +resource "openstack_networking_router_interface_v2" "{{ value.belongsTo ~ "_router_interface" }}" { + router_id = openstack_networking_router_v2.router.id + subnet_id = openstack_networking_subnet_v2.{{ value.belongsTo ~ "_subnet"}}.id +} + +{# adding security groups for interfaces #} +{%- if value.associated is defined %} +# Attach networking port +resource "openstack_networking_port_v2" "{{ value.name ~ "_networking_port" }}" { + name = "{{ value.name }}" + network_id = openstack_networking_network_v2.{{ extra_parameters.networks[0].infra_element_name }}.id + admin_state_up = true + security_group_ids = [ openstack_compute_secgroup_v2.{{ value.associated }}.id ] + fixed_ip { + subnet_id = openstack_networking_subnet_v2.{{ value.belongsTo ~ "_subnet" }}.id + } +} +{%- endif%} + +{%- endif %}{% endif %}{% endfor %} + + diff --git a/templates/terraform/open_stack/virtual_machine_out.tpl b/templates/terraform/open_stack/virtual_machine_out.tpl index 3b25ad5..14b3aa3 100644 --- a/templates/terraform/open_stack/virtual_machine_out.tpl +++ b/templates/terraform/open_stack/virtual_machine_out.tpl @@ -14,14 +14,14 @@ #------------------------------------------------------------------------- #} -output "instance_server_public_key_{{ credentials }}" { +output "instance_server_public_key_{{ credentials }}_{{ infra_element_name }}" { value = openstack_compute_keypair_v2.{{ credentials }}.public_key } -output "instance_server_private_key_{{ credentials }}" { +output "instance_server_private_key_{{ credentials }}_{{ infra_element_name }}" { value = openstack_compute_keypair_v2.{{ credentials }}.private_key } -output "instance_ip_{{ infra_element_name }}" { +output "instance_server_public_ip_{{ infra_element_name }}" { value = openstack_compute_floatingip_associate_v2.{{ infra_element_name ~ "_floating_ip_association" }}.floating_ip } \ No newline at end of file diff --git a/templates/terraform/vsphere/config.tpl b/templates/terraform/vsphere/config.tpl new file mode 100644 index 0000000..57da218 --- /dev/null +++ b/templates/terraform/vsphere/config.tpl @@ -0,0 +1,30 @@ +{# Copyright 2022 Hewlett Packard Enterprise Development LP +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#------------------------------------------------------------------------- +#} + +--- +engine: terraform +input: + - VSPHERE_USER + - VSPHERE_PASSWORD + - VSPHERE_SERVER + - VSPHERE_ALLOW_UNVERIFIED_SSL +output: +{% for vm in vms %} + - instance_server_public_key_{{ vm.credentials }} + - instance_public_ip_{{ vm.infra_element_name }} + - instance_public_dns_{{ vm.infra_element_name }} +{% endfor %} +... diff --git a/templates/terraform/vsphere/data_resources.tpl b/templates/terraform/vsphere/data_resources.tpl new file mode 100644 index 0000000..f6f2f5a --- /dev/null +++ b/templates/terraform/vsphere/data_resources.tpl @@ -0,0 +1,6 @@ +data "{{type}}" "{{name}}" { + name = "{{gname}}" +{%- for key, value in context().items() %}{% if value is mapping%}{% if value.type == "vsphere_datacenter"%} + datacenter_id = ${data.vsphere_datacenter.{{value.name}}.id} +{% endif %}{% endif %}{% endfor %} +} diff --git a/templates/terraform/vsphere/datacenter.tpl b/templates/terraform/vsphere/datacenter.tpl new file mode 100644 index 0000000..7c62ba1 --- /dev/null +++ b/templates/terraform/vsphere/datacenter.tpl @@ -0,0 +1,13 @@ +{##-------- Variables ##} +{%- set preexinsting = preexisting -%} + +{%- if preexinsting %} +data "vsphere_datacenter" "{{datacenter}}" { + name = "{{datacenter}}" +} +{%- else %} +resource "vsphere_datacenter" "{{datacenter}}" { + name = "{{datacenter}}" + folder = "{{folder_path}}" +} +{%-endif %} \ No newline at end of file diff --git a/templates/terraform/vsphere/datastore.tpl b/templates/terraform/vsphere/datastore.tpl new file mode 100644 index 0000000..f35c6b5 --- /dev/null +++ b/templates/terraform/vsphere/datastore.tpl @@ -0,0 +1,8 @@ +{%- if preexisting %} +data "vsphere_datastore" "{{name}}" { + name = "{{vsphere_datastore_name}}" +{%- for key, value in context().items() %}{% if value is mapping%}{% if value.type == "vsphere_datacenter"%} + datacenter_id = ${data.vsphere_datacenter.{{value.name}}.id} +{% endif %}{% endif %}{% endfor %} +} +{%- endif %} \ No newline at end of file diff --git a/templates/terraform/vsphere/init.tpl b/templates/terraform/vsphere/init.tpl new file mode 100644 index 0000000..f161486 --- /dev/null +++ b/templates/terraform/vsphere/init.tpl @@ -0,0 +1,22 @@ +{# Copyright 2022 Hewlett Packard Enterprise Development LP +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#------------------------------------------------------------------------- +#} + +provider "vsphere" { + #user = VSPHERE_USER + #password = VSPHERE_PASSWORD + #vsphere_server = VSPHERE_SERVER + #allow_unverified_ssl = {{allow_unverified_ssl}} +} diff --git a/templates/terraform/vsphere/network.tpl b/templates/terraform/vsphere/network.tpl new file mode 100644 index 0000000..5294d77 --- /dev/null +++ b/templates/terraform/vsphere/network.tpl @@ -0,0 +1,13 @@ +{##-------- Variables ##} +{%- set preexinsting = preexisting -%} + +{%- if preexinsting %} +data "vsphere_network" "{{infra_element_name}}" { + name = "{{vsphere_network_name}}" +{%- for key, value in context().items() %}{% if value is mapping%}{% if value.type == "vsphere_datacenter"%} + datacenter_id = ${data.vsphere_datacenter.{{value.name}}.id} +{% endif %}{% endif %}{% endfor %} +} +{% else %} +TODO +{%-endif %} \ No newline at end of file diff --git a/templates/terraform/vsphere/ssh_key.tpl b/templates/terraform/vsphere/ssh_key.tpl new file mode 100644 index 0000000..f16964d --- /dev/null +++ b/templates/terraform/vsphere/ssh_key.tpl @@ -0,0 +1,19 @@ +{# Copyright 2022 Hewlett Packard Enterprise Development LP +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#------------------------------------------------------------------------- +#} +resource "tls_private_key" "{{infra_element_name}}" { + algorithm = "{{algorithm}}" + rsa_bits = {{bits}} +} \ No newline at end of file diff --git a/templates/terraform/vsphere/virtual_machine.tpl b/templates/terraform/vsphere/virtual_machine.tpl new file mode 100644 index 0000000..a31bafe --- /dev/null +++ b/templates/terraform/vsphere/virtual_machine.tpl @@ -0,0 +1,77 @@ +{# Copyright 2022 Hewlett Packard Enterprise Development LP +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#------------------------------------------------------------------------- +#} + +# Create virtual machine +resource "vsphere_virtual_machine" "{{ infra_element_name }}" { + name = "{{ name }}" + {%- if pool and pool.preexisting %} + resource_pool_id = ${data.{{pool.type}}.{{ pool.name }}.id} + {%- endif %} + {%- if datastore and datastore.preexisting %} + datastore_id = ${data.vsphere_datastore.{{ datastore.name }}.id} + {%- endif %} + num_cpus = {% if 'vm_Virtual_CPU_Cores' in context().keys() %}{{ vm_Virtual_CPU_Cores }}{% else %}{{ cpu_count }}{% endif %} + memory = {% if 'vm_Memory' in context().keys() %}{{ vm_Memory }}{% else %}{{ memory_mb }}{% endif %} + + guest_id = "{{guest_id}}" + + network_interface { + network_id = {%- for key, value in context().items() %}{% if not callable(value)%}{%if key.startswith('NetworkInterface') %} ${data.vsphere_network.{{ value.belongsTo }}.id} + {%- endif %}{% endif %}{% endfor %} + } + + disk { + label = "{{disk}}" //TODO Missing attach option in DOML - datastore and disk are two different resources + size = {% if 'vm_Instance_Storage' in context().keys() %}{{ vm_Instance_Storage }}{% else %}{{ disk_size }}{% endif %} + + } + + clone { + template_uuid = ${data.vsphere_virtual_machine.{{template.name}}.id} + customize { + linux_options { + host_name = "{{host_name}}" + domain = "{{domain}}" + } + network_interface { + ipv4_address = {%- for key, value in context().items() %}{% if not callable(value)%}{%if key.startswith('NetworkInterface') %} "{{ value.endPoint }}"{%- endif %}{% endif %}{% endfor %} + ipv4_netmask = 27 //TODO retrieve in some way the netmask from the network (ICG Parser) + dns_server_list = ["10.81.34.36, 10.81.34.60"] + } + ipv4_gateway = "10.83.18.65" //TODO the DOML v2.2 definition of gateway is still not implemented in IDE + } + } + + connection { + type = "ssh" + user = "${var.username}" + password = "${var.password}" + host = "${self.default_ip_address}" # TCN GBE + } + + provisioner "remote-exec" { + inline = [ + "systemctl stop firewalld", + "echo 'nameserver 10.81.34.36' | sudo tee /etc/resolv.conf", + "mkdir /root/.ssh", + "chmod 700 /root/.ssh", + "touch /root/.ssh/authorized_keys", + "chmod 600 /root/.ssh/authorized_keys", + "echo -e '${tls_private_key.{{ credentials }}.public_key_openssh}' >> /root/.ssh/authorized_keys" # TCN GBE + ] + } + +} \ No newline at end of file diff --git a/templates/terraform/vsphere/virtual_machine_out.tpl b/templates/terraform/vsphere/virtual_machine_out.tpl new file mode 100644 index 0000000..46abc72 --- /dev/null +++ b/templates/terraform/vsphere/virtual_machine_out.tpl @@ -0,0 +1,15 @@ +output "instance_server_public_key_{{ credentials }}_{{ infra_element_name }}" { + value = tls_private_key.{{ credentials }}.public_key_openssh +} + +output "instance_server_private_key_{{ credentials }}_{{ infra_element_name }}" { + value = nonsensitive(tls_private_key.{{ credentials }}.private_key_openssh) +} + +output "instance_user_{{ infra_element_name }}" { + value = var.username +} + +output "instance_ip_{{ infra_element_name }}" { + value = vsphere_virtual_machine.{{ infra_element_name }}.default_ip_address +} \ No newline at end of file diff --git a/templates/terraform/vsphere/vm_image.tpl b/templates/terraform/vsphere/vm_image.tpl new file mode 100644 index 0000000..e6570d9 --- /dev/null +++ b/templates/terraform/vsphere/vm_image.tpl @@ -0,0 +1,8 @@ +{%- if preexisting %} +data "vsphere_virtual_machine" "{{name}}" { + name = "{{vsphere_virtual_machine_name}}" +{%- for key, value in context().items() %}{% if value is mapping%}{% if value.type == "vsphere_datacenter"%} + datacenter_id = ${data.vsphere_datacenter.{{value.name}}.id} +{% endif %}{% endif %}{% endfor %} +} +{%- endif %} \ No newline at end of file diff --git a/utility/PropertiesReaderUtility.py b/utility/PropertiesReaderUtility.py new file mode 100644 index 0000000..483c474 --- /dev/null +++ b/utility/PropertiesReaderUtility.py @@ -0,0 +1,44 @@ +import configparser +import logging +import os + + +def read_properties_file(properties_file_name): + current_folder = os.path.dirname(os.path.realpath(__file__)) + logging.info(f"Reading {properties_file_name} file from folder {current_folder}") + config = configparser.ConfigParser() + config.read(properties_file_name) + return config + +def get_sections(properties_file_name): + logging.info(f"Searching section in file {properties_file_name}") + config_parser = read_properties_file(properties_file_name) + sections =config_parser.sections() + logging.info(f"Founded sections {sections}") + return sections + +def get_items_from_section(properties_file_name, section): + logging.info(f"Searching items in {properties_file_name}.{section}") + config_parser = read_properties_file(properties_file_name) + sections = get_sections(properties_file_name) + for sec in sections: + if sec == section: + items = dict(config_parser.items(section)) + logging.info(f"Founded {items} in {properties_file_name}.{section}") + return items + return logging.info(f"No section {section} found in {properties_file_name}") + +def get_items_from_key(properties_file_name, section, key): + logging.info(f"Searching items in {properties_file_name}.{section}.{key}") + items = get_items_from_section(properties_file_name, section) + values = items.get(key) + return values.split(",") + +def get_key_from_properties(properties_file_name, section): + logging.info(f"Searching items in {properties_file_name}.{section}") + items = get_items_from_section(properties_file_name, section) + return items.keys() + + + + -- GitLab