diff options
author | Michal Ptacek <m.ptacek@partner.samsung.com> | 2019-09-04 15:39:20 +0000 |
---|---|---|
committer | Gerrit Code Review <gerrit@onap.org> | 2019-09-04 15:39:20 +0000 |
commit | 1da8b7af533aa48450d42219f0615d0bb510da4a (patch) | |
tree | 827fc4fa9da8d685d6d7960978ce99680e8f7d31 /tools | |
parent | 8620b2be770895ffca1385f3a8a57b9422ecf126 (diff) | |
parent | 2e34522351d40edd0e37b4919630736748949f2a (diff) |
Merge changes from topic "OOM-2042"
* changes:
Add ansible configuration file
Add cicdansible playbook
Add onap installation role
Add onap instance configuration role
Add ansible role to deploy onap infrastructure on openstack
Add floating ip fact retrieval module
Add inventory for cicdansible playbook
Add heat template to deploy onap infrastructure
Add the .gitignore for cicdansible
Diffstat (limited to 'tools')
30 files changed, 932 insertions, 0 deletions
diff --git a/tools/cicdansible/.gitignore b/tools/cicdansible/.gitignore new file mode 100644 index 00000000..bb3e4abb --- /dev/null +++ b/tools/cicdansible/.gitignore @@ -0,0 +1,2 @@ +*.retry +resources/ diff --git a/tools/cicdansible/ansible.cfg b/tools/cicdansible/ansible.cfg new file mode 100644 index 00000000..e74dda58 --- /dev/null +++ b/tools/cicdansible/ansible.cfg @@ -0,0 +1,18 @@ +#Ansible configuration used when running the playbook. +[defaults] +#Stdout callback. +stdout_callback=debug +#Default verbosity level, for logging all module outputs. +verbosity=1 + +[inventory] +#Fail when inventory parsing fails. +any_unparsed_is_failed=true + +[connection] +#Enable ansible pipelining. +pipelining=true + +[ssh_connection] +#Increase control persist settings. +ssh_args=-C -o ControlMaster=auto -o ControlPersist=30m diff --git a/tools/cicdansible/group_vars/all.yml b/tools/cicdansible/group_vars/all.yml new file mode 100644 index 00000000..581e7c4a --- /dev/null +++ b/tools/cicdansible/group_vars/all.yml @@ -0,0 +1,63 @@ +--- +#General configuration, can be overridden in cmdline. +#Authentication/keystone url. +os_auth_url: "" +#Openstack username. +os_username: "" +#Password. +os_password: "" +#Domain name. +os_domain_name: "default" +#Project name. +os_project_name: "" +#The name or id of public network used to communicate with instances. +public_network: "" +#Floating ip address for first node instance +first_node_ip: "" +#Floating ip of infra instance. +infra_ip: "" +#Floating ip of installer. +installer_ip: "" +#Openstack flavor name for nodes. +node_flavor_name: "" +#Flavor name for infra instance. +infra_flavor_name: "" +#Flavor name for installer instance. +installer_flavor_name: "" +#Name of the image for instances. +image_name: "" +#Cidr of private subnet where instances are connected. +subnet_cidr: "10.1.0.0/24" +#Start of dhcp allocation range for subnet. +subnet_range_start: "10.1.0.4" +#Subnet allocation range end. +subnet_range_end: "10.1.0.254" +#Ip address of router used as a gateway to external network. +router_addr: "10.1.0.1" +#Cidr of external subnet to allow access to, 0.0.0.0/0 means allow internet access. +# For offline deployment it is recommended to set this to a cidr of intranet. +external_subnet_cidr: "" +#Address of cicd docker registry. +cicd_docker_registry: "" +#Number of nodes to deploy. +num_nodes: "3" +#Stack name to deploy on heat. +stack_name: "installer-test" +#Address of resource server with packages. +resource_host: "" +#Directory with all onap packages (on resource host). +resources_dir: "" +#Filename of software package. +resources_sw_filename: "sw_package.tar" +#Filename of binary resources. +resources_filename: "resources_package.tar" +#Filename of auxiliary resources. +aux_resources_filename: "aux_package.tar" +#Whether to deploy app. +#Setting it to false will skip deployment, but instance preconfiguration +#will still be done and sw resources uploaded to the installer host. +install_app: true +# This is a string containing base64-encoded yaml blob passed to offline installer via -e option. +# You can use it to override any variable in offline installer except those +# supported directly by cicdansible. +application_config: '' diff --git a/tools/cicdansible/group_vars/instances.yml b/tools/cicdansible/group_vars/instances.yml new file mode 100644 index 00000000..0d756a57 --- /dev/null +++ b/tools/cicdansible/group_vars/instances.yml @@ -0,0 +1,11 @@ +#Configuration for all instances. +#User to log in to instances as. +ansible_user: root +#Whether to become root using sudo or such like, no by default. +ansible_become: no +#Private key to use to access instances. +ansible_private_key_file: "{{ lookup('env', 'HOME') }}/.ssh/id_rsa" +#Arguments to skip host key verification for instances, modify only if you know what you are doing. +disable_ssh_host_auth: "-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" +ansible_ssh_common_args: "{{ disable_ssh_host_auth }}" + diff --git a/tools/cicdansible/group_vars/nodes.yml b/tools/cicdansible/group_vars/nodes.yml new file mode 100644 index 00000000..76a222c2 --- /dev/null +++ b/tools/cicdansible/group_vars/nodes.yml @@ -0,0 +1,5 @@ +#Configuration for kubernetes nodes. +#This redirects ssh connections through the installer instance, to allow connecting via internal ip. +#It should work even on openssh versions lacking -j option support. +#The value is based heavily on the default from parent group. +ansible_ssh_common_args: "{{ disable_ssh_host_auth }} -o ProxyCommand='ssh {{ disable_ssh_host_auth }} -i {{ ansible_private_key_file }} -W %h:%p root@{{ installer_ip }}'" diff --git a/tools/cicdansible/group_vars/resources.yml b/tools/cicdansible/group_vars/resources.yml new file mode 100644 index 00000000..e7c0f773 --- /dev/null +++ b/tools/cicdansible/group_vars/resources.yml @@ -0,0 +1,6 @@ +#Resource host configuration. +#Define used private key. +ansible_private_key_file: "{{ lookup('env', 'HOME') }}/.ssh/id_rsa" +#User login data. +ansible_user: root +ansible_become: no diff --git a/tools/cicdansible/heat/config.yaml b/tools/cicdansible/heat/config.yaml new file mode 100644 index 00000000..e1f0309f --- /dev/null +++ b/tools/cicdansible/heat/config.yaml @@ -0,0 +1,10 @@ +#cloud-config +#Enable root login. +disable_root: false +#Output everything to /dev/console... +output: { all: "/dev/console" } +#Initialization. +runcmd: + - | + set -efxu -o pipefail + %{NOTIFY_COMMAND} --data-binary '{"status": "SUCCESS", "reason": "instance started successfully"}' diff --git a/tools/cicdansible/heat/installer.env b/tools/cicdansible/heat/installer.env new file mode 100644 index 00000000..9765ce30 --- /dev/null +++ b/tools/cicdansible/heat/installer.env @@ -0,0 +1 @@ +#Environment file diff --git a/tools/cicdansible/heat/installer.yaml b/tools/cicdansible/heat/installer.yaml new file mode 100644 index 00000000..8fff3a74 --- /dev/null +++ b/tools/cicdansible/heat/installer.yaml @@ -0,0 +1,283 @@ +#This is the environment heat template, compatible with openstack ocata. +heat_template_version: 2017-02-24 +description: "Heat template for deploying onap env" +parameters: + auth_key: + label: "Auth public key" + description: "The public key used to authenticate to instances" + type: string + node_flavor_name: + label: "name of node flavor" + description: "The name of the flavor used to create kubernetes nodes" + type: string + constraints: + - custom_constraint: nova.flavor + description: "need to specify a valid flavor" + infra_flavor_name: + label: "name of infra flavor" + description: "flavor used to create infra instance" + type: string + constraints: + - custom_constraint: nova.flavor + description: "need to specify a valid flavor" + installer_flavor_name: + label: "name of installer flavor" + description: "flavor used to create installer instance" + type: string + constraints: + - custom_constraint: nova.flavor + description: "need to specify a valid flavor" + image_name: + label: "image name" + description: "name of the image from which to create all instances, should be rhel 7.6 or centos image" + type: string + constraints: + - custom_constraint: glance.image + description: "must specify a valid image name" + subnet_cidr: + label: "private subnet cidr" + description: "Cidr of a private subnet instances will be connected to" + type: string + constraints: + - custom_constraint: net_cidr + subnet_range_start: + label: "subnet dhcp allocation range start" + description: "Start of range of dhcp allocatable ips on private subnet" + type: string + constraints: + - custom_constraint: ip_addr + subnet_range_end: + label: "end of subnet dhcp allocation range" + description: "End of private subnet's dhcp allocation range" + type: string + constraints: + - custom_constraint: ip_addr + router_addr: + label: "ip address of router" + description: "IP address of the router allowing access to other networks incl. company network" + type: string + constraints: + - custom_constraint: ip_addr + public_network_name: + label: "name of the public network" + description: "Name of the public, internet facing network, also allowing access to company internal hosts" + type: string + constraints: + - custom_constraint: neutron.network + description: "Must specify a valid network name or id" + external_subnet_cidr: + label: "external subnet cidr" + description: "The CIDR of the external subnet, that should be accessible from instances, even when internet access is cut. Putting 0.0.0.0/0 here means access to internet." + type: string + constraints: + - custom_constraint: net_cidr + installer_ip: + label: "floating ip of the installer" + description: "a pre-allocated floating ip that will be associated with the installer instance" + type: string + infra_ip: + label: "floating ip of the infra" + description: "a pre-allocated floating ip that will be associated with the infrastructure instance" + type: string + node_ip: + label: "floating ip of the first node" + description: "a pre-allocated floating ip that will be associated with the first kubernetes node and allow accessing onap" + type: string + num_nodes: + label: "num nodes" + description: "the number of kubernetes nodes to create, min 1" + type: number + constraints: + - range: { min: 1 } + description: "must be a positive number" +resources: + # Security group used to secure access to instances. + secgroup: + type: OS::Neutron::SecurityGroup + properties: + rules: + # Egress rule allowing access to external_subnet_cidr. + - direction: egress + ethertype: IPv4 + remote_ip_prefix: { get_param: external_subnet_cidr } + # Ingress rule, allowing also inbound access by external network. + - direction: ingress + ethertype: IPv4 + remote_ip_prefix: { get_param: external_subnet_cidr } + # Allow outbound communication with the internal subnet. + - direction: egress + ethertype: IPv4 + remote_ip_prefix: { get_param: subnet_cidr } + # Allow inbound communication from internal network. + - direction: ingress + ethertype: IPv4 + remote_ip_prefix: { get_param: subnet_cidr } + # Allow outbound access to 169.254.0.0/16, mainly for metadata. We do not need inbound. + - direction: egress + ethertype: IPv4 + remote_ip_prefix: 169.254.0.0/16 + #A network that our test environment will be connected to. + privnet: + type: OS::Neutron::Net + #Subnet that instances will live in. + privsubnet: + type: OS::Neutron::Subnet + properties: + network: { get_resource: privnet } + cidr: { get_param: subnet_cidr } + allocation_pools: + - { start: { get_param: subnet_range_start }, end: { get_param: subnet_range_end } } + gateway_ip: { get_param: router_addr } + ip_version: 4 + #A port connected to the private network, taken by router. + routerport: + type: OS::Neutron::Port + properties: + network: { get_resource: privnet } + fixed_ips: + - { subnet: { get_resource: privsubnet }, ip_address: { get_param: router_addr } } + security_groups: [{ get_resource: secgroup }] + #This is a router, routing between us and the internet. + #It has an external gateway to public network. + router: + type: OS::Neutron::Router + properties: + external_gateway_info: + network: { get_param: public_network_name } + #This is a router interface connecting it to our private subnet's router port. + routercon: + type: OS::Neutron::RouterInterface + properties: + router: { get_resource: router } + port: { get_resource: routerport } + + #Key used to authenticate to instances as root. + key: + type: OS::Nova::KeyPair + properties: + name: { get_param: "OS::stack_name" } + public_key: { get_param: auth_key } + #Handle to signal about starting up of instances. + instance_wait_handle: + type: OS::Heat::WaitConditionHandle + #Monitor waiting for all instances to start. + instance_wait: + type: OS::Heat::WaitCondition + properties: + handle: { get_resource: instance_wait_handle } + timeout: 1200 + count: + yaql: + data: { num_nodes: { get_param: num_nodes } } + #This is number of all nodes + 2 (infra instance and installer) + expression: "$.data.num_nodes + 2" + #Resource group to deploy n nodes using node template for each, each node numbered starting from 0. + nodes: + type: OS::Heat::ResourceGroup + properties: + count: { get_param: num_nodes } + resource_def: + type: node.yaml + properties: + nodenum: "%index%" + key_name: { get_resource: key } + image_name: { get_param: image_name } + network: { get_resource: privnet } + subnet: { get_resource: privsubnet } + flavor_name: { get_param: node_flavor_name } + notify_command: { get_attr: ["instance_wait_handle", "curl_cli"] } + security_group: { get_resource: secgroup } + depends_on: [routercon, instance_wait_handle] + #Nfs storage volume for first node. + nfs_storage: + type: OS::Cinder::Volume + properties: + name: nfs_storage + size: 50 + #Attachment of volume to first node. + nfs_storage_attachment: + type: OS::Cinder::VolumeAttachment + properties: + instance_uuid: { get_attr: [nodes, "resource.0"] } + volume_id: { get_resource: nfs_storage } + #Floating ip association for node (first only). + node_fip_assoc: + type: OS::Neutron::FloatingIPAssociation + properties: + floatingip_id: { get_param: node_ip } + port_id: { get_attr: ["nodes", "resource.0.port_id"] } + #Openstack volume used for storing resources. + resources_storage: + type: "OS::Cinder::Volume" + properties: + name: "resources_storage" + size: 120 + #Instance representing infrastructure instance, created using subtemplate. + infra: + type: "instance.yaml" + properties: + instance_name: infra + network: { get_resource: privnet } + subnet: { get_resource: privsubnet } + key_name: { get_resource: key } + flavor_name: { get_param: infra_flavor_name } + image_name: { get_param: image_name } + notify_command: { get_attr: ["instance_wait_handle", "curl_cli"] } + security_group: { get_resource: secgroup } + depends_on: [instance_wait_handle] + #Volume attachment for infra node. + resources_storage_attachment: + type: OS::Cinder::VolumeAttachment + properties: + volume_id: { get_resource: resources_storage } + instance_uuid: { get_resource: infra } + #Floating ip association for infra. + infra_fip_assoc: + type: OS::Neutron::FloatingIPAssociation + properties: + floatingip_id: { get_param: infra_ip } + port_id: { get_attr: ["infra", "port_id"] } + #Small installer vm having access to other instances, used to install onap. + installer: + type: "instance.yaml" + properties: + instance_name: installer + image_name: { get_param: image_name } + flavor_name: { get_param: installer_flavor_name } + key_name: { get_resource: key } + network: { get_resource: privnet } + subnet: { get_resource: privsubnet } + notify_command: { get_attr: ["instance_wait_handle", "curl_cli"] } + security_group: { get_resource: secgroup } + depends_on: instance_wait_handle + #Floating ip for installer. + installer_fip_assoc: + type: OS::Neutron::FloatingIPAssociation + properties: + floatingip_id: { get_param: installer_ip } + port_id: { get_attr: [installer, port_id] } +#Output values +outputs: + installer_ip: + value: { get_attr: [installer, ip] } + description: "Internal ip of installer instance" + infra_ip: + value: { get_attr: [infra, ip] } + description: "Internal ip of infra instance" + node_ips: + value: { get_attr: [nodes, ip] } + description: "Serialized json list of node internal ips starting at node0" + volumes: + description: "map of volumes per each instance" + value: + yaql: + data: + resources_volid: { get_resource: resources_storage } + nfs_volid: { get_resource: nfs_storage } + docker_volids: { get_attr: [nodes, docker_storage_id] } + #This is going to create a map, where keys are instance names, and values are lists of + #pairs of volume ids and their mount points. + #This is done by merging few generated maps together, base map is taken by + #enumerating over docker storage volumes and transforming them into a map like + #{"node0"=>["volid","/var/lib/docker"],...], node1=>...} + expression: 'dict($.data.docker_volids.enumerate().select(["node"+str($[0]), [[$[1], "/var/lib/docker"]]])).mergeWith({"infra" => [[$.data.resources_volid, "/opt/onap"]], "node0" => [[$.data.nfs_volid, "/dockerdata-nfs"]]})' diff --git a/tools/cicdansible/heat/instance.yaml b/tools/cicdansible/heat/instance.yaml new file mode 100644 index 00000000..2734704d --- /dev/null +++ b/tools/cicdansible/heat/instance.yaml @@ -0,0 +1,58 @@ +#Template for instances. +heat_template_version: 2017-02-24 +description: "template instantiating and configuring a single instance (any)" +parameters: + instance_name: + type: string + network: + type: string + subnet: + type: string + image_name: + type: string + flavor_name: + type: string + key_name: + type: string + notify_command: + type: string + security_group: + type: string +#Resources. +resources: + #This is the network port to attach instance to. + port: + type: OS::Neutron::Port + properties: + network: { get_param: network } + security_groups: [ { get_param: security_group } ] + fixed_ips: + - { subnet: { get_param: subnet }} + #cloudinit configuration stuff. + config: + type: OS::Heat::SoftwareConfig + properties: + config: + str_replace_strict: + template: { get_file: config.yaml } + params: + "%{NOTIFY_COMMAND}": { get_param: notify_command } + #Actual instance to create. + instance: + type: OS::Nova::Server + properties: + name: { get_param: instance_name } + image: { get_param: image_name } + flavor: { get_param: flavor_name } + key_name: { get_param: key_name } + networks: + - port: { get_resource: port } + user_data_format: SOFTWARE_CONFIG + user_data: { get_resource: config } +outputs: + OS::stack_id: + value: { get_resource: instance } + port_id: + value: { get_resource: port } + ip: + value: { get_attr: ["port", "fixed_ips", 0, "ip_address"] } diff --git a/tools/cicdansible/heat/node.yaml b/tools/cicdansible/heat/node.yaml new file mode 100644 index 00000000..b6048d8d --- /dev/null +++ b/tools/cicdansible/heat/node.yaml @@ -0,0 +1,59 @@ +#This yaml template instantiates kubernetes nodes (using instance.yaml subtemplate). +#It contains some node specific things, and has been split from main template +#to be able to do some late evaluation tricks. +heat_template_version: 2017-02-24 +description: "This template instantiates a single kubernetes node using the instance.yaml subtemplate" +parameters: + key_name: + type: string + flavor_name: + type: string + nodenum: + type: number + image_name: + type: string + network: + type: string + subnet: + type: string + notify_command: + type: string + security_group: + type: string +resources: + #Volume for storing /var/lib/docker for node. + docker_storage: + type: OS::Cinder::Volume + properties: + name: docker_storage + size: 120 + #Call generic instance template. + instance: + type: instance.yaml + properties: + instance_name: + str_replace_strict: + template: "node%index%" + params: { "%index%": { get_param: nodenum } } + key_name: { get_param: key_name } + image_name: { get_param: image_name } + network: { get_param: network } + subnet: { get_param: subnet } + flavor_name: { get_param: flavor_name } + notify_command: { get_param: notify_command } + security_group: { get_param: security_group } + #Attachment of docker volume to node. + docker_storage_attachment: + type: OS::Cinder::VolumeAttachment + properties: + volume_id: { get_resource: docker_storage } + instance_uuid: { get_resource: instance } +outputs: + OS::stack_id: + value: { get_resource: instance } + port_id: + value: { get_attr: ["instance", "port_id"] } + ip: + value: { get_attr: ["instance", "ip"] } + docker_storage_id: + value: { get_resource: docker_storage } diff --git a/tools/cicdansible/hosts.yml b/tools/cicdansible/hosts.yml new file mode 100644 index 00000000..e4c416cf --- /dev/null +++ b/tools/cicdansible/hosts.yml @@ -0,0 +1,28 @@ +#Default inventory. +#This file should not be modified, instead modify group_vars. +#NOTE +#All kubernetes nodes including the first node are added to inventory dynamically. +#Instances group with children. +instances: + hosts: + +#Installer instance. + installer: + #Do not modify. + ansible_host: "{{ installer_ip }}" + +#Infra instance. + infra: + #Do not modify. + ansible_host: "{{ infra_ip }}" + + children: + #Empty group for nodes, populated dynamically, do not modify please. + nodes: + +#The group for resource host, only first entry is considered. +#This host contains onap installer packages including scripts. +resources: + hosts: + resource_host: + ansible_host: "{{ resource_host }}" diff --git a/tools/cicdansible/install.yml b/tools/cicdansible/install.yml new file mode 100644 index 00000000..13071c31 --- /dev/null +++ b/tools/cicdansible/install.yml @@ -0,0 +1,36 @@ +--- +#Installation of onap on open stack driven by ansible. +#Default parameters are set in group_vars/*.yml. +#Inventory is in hosts.yml, and parameters specific to instances are set there. +#Deploy infrastructure. +- name: "deploy infrastructure" + hosts: localhost + gather_facts: false + roles: + - role: setup_openstack_infrastructure + vars: + mode: deploy +#Play that configures all instances. +- name: "Instance configuration" + hosts: instances + any_errors_fatal: true + roles: + - role: setup_openstack_infrastructure + vars: + mode: configure + - role: configure_instances +#Play that downloads sw resources. +- name: "Download resources" + hosts: resources + gather_facts: false + roles: + - role: install + vars: + mode: download_resources +#Perform installation. +- name: "Perform installation" + hosts: installer + roles: + - role: install + vars: + mode: install diff --git a/tools/cicdansible/library/os_floating_ips_facts.py b/tools/cicdansible/library/os_floating_ips_facts.py new file mode 100644 index 00000000..ad546004 --- /dev/null +++ b/tools/cicdansible/library/os_floating_ips_facts.py @@ -0,0 +1,61 @@ +#!/usr/bin/python +ANSIBLE_METADATA = { + 'METADATA_VERSION': '1.1', + 'supported_by': 'community', + 'status': 'preview' +} + +DOCUMENTATION = ''' +--- +module: "os_floating_ips_facts" +short_description: "Retrieves facts about floating ips" +description: + - "This module retrieves facts about one or more floating ips allocated to project." +version_added: "2.7" +author: + - "Michal Zegan" +requirements: + - "python => 2.7" + - "openstacksdk" +options: + floating_ip: + description: + - "The floating ip to retrieve facts for" + type: "str" + network: + description: + - "Name or id of the floating ip network to query." + required: true + type: "str" +notes: + - "Registers facts starting with openstack_floating_ips" +extends_documentation_fragment: openstack +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.openstack import openstack_full_argument_spec, openstack_module_kwargs, openstack_cloud_from_module + +def run_module(): + args=openstack_module_kwargs() + argspec=openstack_full_argument_spec( + floating_ip=dict(type=str), + network=dict(type=str, required=True)) + module=AnsibleModule(argument_spec=argspec, **args) + sdk, cloud = openstack_cloud_from_module(module) + try: + fip_network=cloud.network.find_network(module.params['network']) + filter=dict( + project_id=cloud.current_project_id, + floating_network_id=fip_network.id) + if not (module.params['floating_ip'] is None): + filter['floating_ip_address'] = module.params['floating_ip'] + ips=[dict(x) for x in cloud.network.ips(**filter)] + module.exit_json( + changed=False, + ansible_facts=dict(openstack_floating_ips=ips) + ) + except sdk.exceptions.OpenStackCloudException as e: + module.fail_json(msg=str(e)) + +if __name__ == '__main__': + run_module() diff --git a/tools/cicdansible/roles/configure_instances/tasks/cicd_registry.yml b/tools/cicdansible/roles/configure_instances/tasks/cicd_registry.yml new file mode 100644 index 00000000..f3c54ca3 --- /dev/null +++ b/tools/cicdansible/roles/configure_instances/tasks/cicd_registry.yml @@ -0,0 +1,10 @@ +#Configure access to cicd docker registry. +- name: "Ensure that docker config directory exists" + file: + path: /etc/docker + mode: 0700 + state: directory +- name: "Allow insecure access to cicd docker registry" + template: + src: daemon.json.j2 + dest: /etc/docker/daemon.json diff --git a/tools/cicdansible/roles/configure_instances/tasks/general.yml b/tools/cicdansible/roles/configure_instances/tasks/general.yml new file mode 100644 index 00000000..6ed9982e --- /dev/null +++ b/tools/cicdansible/roles/configure_instances/tasks/general.yml @@ -0,0 +1,26 @@ +#General instance configuration. +#Modify /etc/hosts on every instance to add every instance there including itself. +- name: "Add hosts to /etc/hosts" + lineinfile: + path: /etc/hosts + insertafter: EOF + regexp: "^[^ ]+ {{ item }}$" + state: present + line: "{{ hostvars[item].ansible_default_ipv4.address }} {{ item }}" + loop: "{{ groups['instances'] }}" +#Copy private ssh key to instances for easy connecting between them. +- name: "Ensure ssh directory exists" + file: + path: /root/.ssh + owner: root + group: root + mode: 0700 + state: directory +- name: "Install ssh private key" + copy: + src: "{{ ansible_private_key_file }}" + dest: /root/.ssh/id_rsa + mode: 0400 +#Add public ssh host keys of all instances to trust them. +- name: "Add host keys of instances to known_hosts" + shell: "ssh-keyscan {{ groups['instances'] | join(' ') }} > /root/.ssh/known_hosts" diff --git a/tools/cicdansible/roles/configure_instances/tasks/main.yml b/tools/cicdansible/roles/configure_instances/tasks/main.yml new file mode 100644 index 00000000..fe5b4b7d --- /dev/null +++ b/tools/cicdansible/roles/configure_instances/tasks/main.yml @@ -0,0 +1,5 @@ +#Initial instance configuration. +- include_tasks: general.yml +#Configure cicd registry access, but skip installer. +- include_tasks: cicd_registry.yml + when: "inventory_hostname != 'installer'" diff --git a/tools/cicdansible/roles/configure_instances/templates/daemon.json.j2 b/tools/cicdansible/roles/configure_instances/templates/daemon.json.j2 new file mode 100644 index 00000000..1c3ca9bb --- /dev/null +++ b/tools/cicdansible/roles/configure_instances/templates/daemon.json.j2 @@ -0,0 +1,3 @@ +{ +"insecure-registries": ["{{ cicd_docker_registry }}"] +} diff --git a/tools/cicdansible/roles/install/defaults/main.yml b/tools/cicdansible/roles/install/defaults/main.yml new file mode 100644 index 00000000..b21e6323 --- /dev/null +++ b/tools/cicdansible/roles/install/defaults/main.yml @@ -0,0 +1,3 @@ +--- +installer_deploy_path: "{{ ansible_user_dir }}/installer" +install_timeout: 10600 diff --git a/tools/cicdansible/roles/install/tasks/download_resources.yml b/tools/cicdansible/roles/install/tasks/download_resources.yml new file mode 100644 index 00000000..7f042596 --- /dev/null +++ b/tools/cicdansible/roles/install/tasks/download_resources.yml @@ -0,0 +1,6 @@ +#Download resources/scripts to controller. +- name: "Download software resources" + fetch: + src: "{{ resources_dir }}/{{ resources_sw_filename }}" + flat: yes + dest: "resources/" diff --git a/tools/cicdansible/roles/install/tasks/install.yml b/tools/cicdansible/roles/install/tasks/install.yml new file mode 100644 index 00000000..35df7976 --- /dev/null +++ b/tools/cicdansible/roles/install/tasks/install.yml @@ -0,0 +1,48 @@ +#Onap installation tasks +#Copy ssh private key used for resource server access +- name: "Copy resource server access key" + copy: + src: "{{ hostvars[groups['resources'][0]].ansible_private_key_file }}" + dest: "{{ ansible_user_dir }}/.ssh/res.pem" + mode: 0600 +#Unarchive resources. +- name: "Ensure {{ installer_deploy_path }} directory exists" + file: + path: "{{ installer_deploy_path }}" + state: directory +- name: "Extract sw resources" + unarchive: + src: "resources/{{ hostvars[groups['resources'][0]].resources_sw_filename }}" + dest: "{{ installer_deploy_path }}" +#Generate ansible inventory and extra vars. +- name: "Generate ansible inventory for installer" + template: + src: inventory.yml.j2 + dest: "{{ installer_deploy_path }}/ansible/inventory/hosts.yml" +- name: "generate application specific config overrides" + copy: + content: "{{ application_config | b64decode }}" + dest: "{{ installer_deploy_path }}/ansible/application/application_overrides.yml" +# This generates a file with locations of resource files in resource host, we +# do it only to allow manually running offline installer without +# typing them by hand. We cannot use +# inventory template because it will be overridden +# by application_configuration.yml. +- name: Generate resource location file + copy: + content: | + resources_dir: {{ resources_dir }} + resources_filename: {{ resources_filename }} + aux_resources_filename: {{ aux_resources_filename }} + app_data_path: /opt/onap/resources + dest: "{{ installer_deploy_path }}/ansible/application/resources.yml" +#Run script. +- name: "Execute installation" + shell: + ./run_playbook.sh + -e @application/application_configuration.yml -e @application/application_overrides.yml + -e @application/resources.yml -i inventory/hosts.yml site.yml + args: + chdir: "{{ installer_deploy_path }}/ansible" + async: "{{ install_timeout }}" + when: install_app diff --git a/tools/cicdansible/roles/install/tasks/main.yml b/tools/cicdansible/roles/install/tasks/main.yml new file mode 100644 index 00000000..04ac4c3d --- /dev/null +++ b/tools/cicdansible/roles/install/tasks/main.yml @@ -0,0 +1 @@ +- include_tasks: "{{ mode }}.yml" diff --git a/tools/cicdansible/roles/install/templates/inventory.yml.j2 b/tools/cicdansible/roles/install/templates/inventory.yml.j2 new file mode 100644 index 00000000..36bf3bd3 --- /dev/null +++ b/tools/cicdansible/roles/install/templates/inventory.yml.j2 @@ -0,0 +1,36 @@ +all: + vars: + ansible_ssh_private_key_file: /root/.ssh/id_rsa + ansible_ssh_common_args: "-o StrictHostKeyChecking=no" + children: + resources: + vars: + ansible_ssh_private_key_file: /root/.ssh/res.pem + ansible_user: "{{ hostvars[groups['resources'][0]].ansible_user }}" + ansible_become: "{{ hostvars[groups['resources'][0]].ansible_become }}" + hosts: + resource_host: + ansible_host: {{ resource_host }} + infrastructure: + hosts: + infra_host: + ansible_host: infra + cluster_ip: {{ hostvars['infra'].ansible_default_ipv4.address }} + kubernetes: + children: + kubernetes-node: + hosts: +{% for h in groups['nodes'] %} + {{ h }}: + ansible_host: "{{ hostvars[h].ansible_default_ipv4.address }}" + cluster_ip: "{{ hostvars[h].ansible_default_ipv4.address }}" +{% endfor %} + kubernetes-control-plane: + hosts: + infra_host + kubernetes-etcd: + hosts: + infra_host + nfs-server: + hosts: + node0 diff --git a/tools/cicdansible/roles/setup_openstack_infrastructure/tasks/configure/main.yml b/tools/cicdansible/roles/setup_openstack_infrastructure/tasks/configure/main.yml new file mode 100644 index 00000000..44de5795 --- /dev/null +++ b/tools/cicdansible/roles/setup_openstack_infrastructure/tasks/configure/main.yml @@ -0,0 +1,11 @@ +#Openstack specific configuration running on instances. +#Get volumes. +- name: "get volume info" + set_fact: + volumes: "{{ (hostvars['localhost'].heat_stack.stack.outputs | selectattr('output_key', 'equalto', 'volumes') | list).0.output_value[inventory_hostname] | default([]) }}" +- name: "Configure volumes" + include_tasks: configure/volume.yml + vars: + volume_id: "{{ item[0] }}" + mountpoint: "{{ item[1] }}" + loop: "{{ volumes }}" diff --git a/tools/cicdansible/roles/setup_openstack_infrastructure/tasks/configure/volume.yml b/tools/cicdansible/roles/setup_openstack_infrastructure/tasks/configure/volume.yml new file mode 100644 index 00000000..8c553850 --- /dev/null +++ b/tools/cicdansible/roles/setup_openstack_infrastructure/tasks/configure/volume.yml @@ -0,0 +1,47 @@ +#Configure a single openstack volume. +- name: "Set volume path" + set_fact: + volume_path: "/dev/disk/by-id/virtio-{{ volume_id | truncate(20, True, '') }}" +- name: "Set partition path" + set_fact: + partition_path: "{{ volume_path }}-part1" +- name: "Wait for volume" + #We do not do it normally, because we want to trigger udev (workaround for some bugs). + shell: "udevadm trigger && udevadm settle && [[ -b {{ volume_path }} ]]" + register: result + retries: 30 + delay: 10 + until: result.rc == 0 +- name: "Partition volume" + parted: + device: "{{ volume_path }}" + number: 1 + label: msdos + flags: boot + part_type: primary + state: present +- name: "Wait for partition to appear" + stat: + path: "{{ partition_path }}" + follow: true + register: part_stat + delay: 1 + retries: 5 + until: part_stat.stat.isblk is defined and part_stat.stat.isblk +- name: "Create xfs filesystem on volume" + filesystem: + dev: "{{ partition_path }}" + type: xfs +- name: "Ensure that the mountpoint exists" + file: + path: "{{ mountpoint }}" + owner: root + group: root + mode: 0755 + state: directory +- name: "Mount filesystem" + mount: + src: "{{ partition_path }}" + path: "{{ mountpoint }}" + fstype: xfs + state: mounted diff --git a/tools/cicdansible/roles/setup_openstack_infrastructure/tasks/deploy/heat.yml b/tools/cicdansible/roles/setup_openstack_infrastructure/tasks/deploy/heat.yml new file mode 100644 index 00000000..2bfeda77 --- /dev/null +++ b/tools/cicdansible/roles/setup_openstack_infrastructure/tasks/deploy/heat.yml @@ -0,0 +1,36 @@ +#Tasks for stack redeployment. +#Delete the heat stack before deployment. +- name: "delete deployment to force redeploy" + os_stack: + auth: "{{ os_auth }}" + auth_type: token + name: "{{ stack_name }}" + state: absent +#Deploy heat stack with infrastructure. +- name: "Deploy the infrastructure via heat" + os_stack: + auth: "{{ os_auth }}" + auth_type: token + name: "{{ stack_name }}" + template: "heat/installer.yaml" + state: present + environment: + - "heat/installer.env" + parameters: + num_nodes: "{{ num_nodes }}" + public_network_name: "{{ public_network }}" + external_subnet_cidr: "{{ external_subnet_cidr }}" + subnet_cidr: "{{ subnet_cidr }}" + subnet_range_start: "{{ subnet_range_start }}" + subnet_range_end: "{{ subnet_range_end }}" + router_addr: "{{ router_addr }}" + auth_key: "{{ auth_public_key }}" + image_name: "{{ image_name }}" + node_flavor_name: "{{ node_flavor_name }}" + infra_flavor_name: "{{ infra_flavor_name }}" + installer_flavor_name: "{{ installer_flavor_name }}" + node_ip: "{{ floating_ips_by_address[first_node_ip].id }}" + infra_ip: "{{ floating_ips_by_address[infra_ip].id }}" + installer_ip: "{{ floating_ips_by_address[installer_ip].id }}" + wait: true + register: heat_stack diff --git a/tools/cicdansible/roles/setup_openstack_infrastructure/tasks/deploy/main.yml b/tools/cicdansible/roles/setup_openstack_infrastructure/tasks/deploy/main.yml new file mode 100644 index 00000000..324f5374 --- /dev/null +++ b/tools/cicdansible/roles/setup_openstack_infrastructure/tasks/deploy/main.yml @@ -0,0 +1,8 @@ +--- +#This mode expects some variables, and deploys infrastructure on open stack. +#Execute prerequisites. +- include_tasks: deploy/prereq.yml +#Deploy stack. +- include_tasks: deploy/heat.yml +#Register instances in inventory. +- include_tasks: deploy/register_instances.yml diff --git a/tools/cicdansible/roles/setup_openstack_infrastructure/tasks/deploy/prereq.yml b/tools/cicdansible/roles/setup_openstack_infrastructure/tasks/deploy/prereq.yml new file mode 100644 index 00000000..2fe8717a --- /dev/null +++ b/tools/cicdansible/roles/setup_openstack_infrastructure/tasks/deploy/prereq.yml @@ -0,0 +1,41 @@ +#Prerequisite tasks before stack deployment. +#Authenticate to cloud. +- name: "authenticate to cloud" + os_auth: + auth: + auth_url: "{{ os_auth_url }}" + username: "{{ os_username }}" + password: "{{ os_password }}" + domain_name: "{{ os_domain_name }}" + project_name: "{{ os_project_name }}" + project_domain_name: "{{ os_domain_name }}" +#Will use the token from this point on. +- name: "set token" + set_fact: + os_auth: + auth_url: "{{ os_auth_url }}" + token: "{{ auth_token }}" + project_name: "{{ os_project_name }}" + project_domain_name: "{{ os_domain_name }}" +#Retrieve floating ip info. +- name: "get floating ip facts" + os_floating_ips_facts: + auth: "{{ os_auth }}" + auth_type: token + network: "{{ public_network }}" +#Group floating ips by ip address to allow looking them up. +- name: "group floating ips by address" + set_fact: + floating_ips_by_address: "{{ floating_ips_by_address | default({}) | combine({item.floating_ip_address: item}) }}" + loop: "{{ query('items', openstack_floating_ips) }}" +- name: "fail if required floating ips do not exist" + fail: msg="The required floating ips do not exist" + when: "(not (first_node_ip in floating_ips_by_address) + or not (infra_ip in floating_ips_by_address) + or not (installer_ip in floating_ips_by_address))" +#Get a ssh public key to be passed to heat, it requires ssh-keygen with -y option. +- name: "Retrieve public key from ssh private key" + command: "ssh-keygen -y -f {{ hostvars['installer'].ansible_private_key_file }}" + register: public_key_generation +- set_fact: + auth_public_key: "{{ public_key_generation.stdout }}" diff --git a/tools/cicdansible/roles/setup_openstack_infrastructure/tasks/deploy/register_instances.yml b/tools/cicdansible/roles/setup_openstack_infrastructure/tasks/deploy/register_instances.yml new file mode 100644 index 00000000..a50ecd22 --- /dev/null +++ b/tools/cicdansible/roles/setup_openstack_infrastructure/tasks/deploy/register_instances.yml @@ -0,0 +1,9 @@ +#Register instances as hosts in inventory. +#Installer and infra are statically registered. +#Register node instances dynamically. +- name: "Register node instances" + add_host: + name: "node{{ item[0] }}" + groups: nodes + ansible_host: "{{ item[1] }}" + loop: "{{ query('indexed_items', (heat_stack.stack.outputs | selectattr('output_key', 'equalto', 'node_ips') | list).0.output_value) }}" diff --git a/tools/cicdansible/roles/setup_openstack_infrastructure/tasks/main.yml b/tools/cicdansible/roles/setup_openstack_infrastructure/tasks/main.yml new file mode 100644 index 00000000..7a00abff --- /dev/null +++ b/tools/cicdansible/roles/setup_openstack_infrastructure/tasks/main.yml @@ -0,0 +1 @@ +- include_tasks: "{{ mode }}/main.yml" |