Allow boot from volume

With environment variable ATMOSPHERE_BOOT_FROM_VOLUME,

you can change instance to boot from volume, and allow image not
specify local disk.

Sem-Ver: feature
Change-Id: If65ddb82b3da348750a4c5d8600469a57ff171b9
diff --git a/molecule/default/create.yml b/molecule/default/create.yml
index 54c5267..d599c54 100644
--- a/molecule/default/create.yml
+++ b/molecule/default/create.yml
@@ -30,6 +30,7 @@
     image: "{{ lookup('env', 'ATMOSPHERE_IMAGE') | default('Ubuntu 20.04.3 LTS (x86_64) [2021-10-04]', True) }}"
     instance_type: "{{ lookup('env', 'ATMOSPHERE_INSTANCE_TYPE') | default('v3-standard-4', True) }}"
     nameservers: "{{ lookup('env', 'ATMOSPHERE_NAMESERVERS') | default('1.1.1.1', True) }}"
+    boot_from_volume: "{{ lookup('env', 'ATMOSPHERE_BOOT_FROM_VOLUME') | bool }}"
   tasks:
     - name: create stack
       openstack.cloud.stack:
@@ -40,6 +41,7 @@
           image: "{{ image }}"
           instance_type: "{{ instance_type }}"
           nameservers: "{{ nameservers }}"
+          boot_from_volume: "{{  boot_from_volume }}"
       register: _os_stack
     - debug:
         msg: "{{ _os_stack.stack }}"
@@ -105,4 +107,4 @@
             address: "{{ compute_ips[0] }}"
           - <<: *instance_config
             instance: "kvm2"
-            address: "{{ compute_ips[1] }}"
\ No newline at end of file
+            address: "{{ compute_ips[1] }}"
diff --git a/molecule/default/heat/server.yaml b/molecule/default/heat/server.yaml
index 930868f..afe659d 100644
--- a/molecule/default/heat/server.yaml
+++ b/molecule/default/heat/server.yaml
@@ -67,6 +67,14 @@
     type: number
     default: 0
 
+  boot_volumes_size:
+    type: number
+    default: 40
+
+  boot_from_volume:
+    type: boolean
+    default: false
+
 conditions:
   has_extra_volumes:
     not:
@@ -74,6 +82,16 @@
         - get_param: extra_volumes_count
         - 0
 
+  is_boot_from_image:
+    equals:
+      - get_param: boot_from_volume
+      - false
+
+  is_boot_from_volume:
+    equals:
+      - get_param: boot_from_volume
+      - true
+
 resources:
   internal_port:
     type: OS::Neutron::Port
@@ -94,8 +112,9 @@
       security_groups:
         - { get_param: security_group }
 
-  server:
+  server_boot_from_image:
     type: OS::Nova::Server
+    condition: is_boot_from_image
     properties:
       name:
         yaql:
@@ -111,6 +130,34 @@
         - port: { get_resource: internal_port }
         - port: { get_resource: external_port }
 
+  server_boot_from_volume:
+    type: OS::Nova::Server
+    condition: is_boot_from_volume
+    properties:
+      name:
+        yaql:
+          expression: concat($.data.name, str($.data.index + 1))
+          data:
+            name: { get_param: name }
+            index: { get_param: index }
+      flavor: { get_param: instance_type }
+      key_name: { get_param: key_name }
+      config_drive: true
+      networks:
+        - port: { get_resource: internal_port }
+        - port: { get_resource: external_port }
+      block_device_mapping_v2:
+        - boot_index: 0
+          volume_id: {get_resource: volume}
+          delete_on_termination: true
+
+  volume:
+    type: OS::Cinder::Volume
+    condition: is_boot_from_volume
+    properties:
+      size: { get_param: boot_volumes_size }
+      image: { get_param: image }
+
   volumes:
     type: OS::Heat::ResourceGroup
     condition: has_extra_volumes
@@ -119,7 +166,7 @@
       resource_def:
         type: volume.yaml
         properties:
-          instance_uuid: { get_resource: server }
+          instance_uuid: {if: ["is_boot_from_volume", { get_resource: server_boot_from_volume }, { get_resource: server_boot_from_image } ]}
           volume_size: { get_param: extra_volumes_size }
 
 outputs:
diff --git a/molecule/default/heat/stack.yaml b/molecule/default/heat/stack.yaml
index 113ddb8..54373a4 100644
--- a/molecule/default/heat/stack.yaml
+++ b/molecule/default/heat/stack.yaml
@@ -40,6 +40,10 @@
     constraints:
       - custom_constraint: glance.image
 
+  boot_from_volume:
+    type: boolean
+    default: false
+
   instance_type:
     type: string
     constraints:
@@ -132,6 +136,8 @@
           internal_network: { get_resource: internal_network }
           public_network: { get_param: public_network }
           external_network: { get_resource: external_network }
+          boot_volumes_size: 40
+          boot_from_volume: { get_param: boot_from_volume  }
 
   storage:
     type: OS::Heat::ResourceGroup
@@ -153,6 +159,8 @@
           external_network: { get_resource: external_network }
           extra_volumes_count: 3
           extra_volumes_size: 40
+          boot_volumes_size: 40
+          boot_from_volume: { get_param: boot_from_volume  }
 
   compute:
     type: OS::Heat::ResourceGroup
@@ -172,6 +180,8 @@
           internal_network: { get_resource: internal_network }
           public_network: { get_param: public_network }
           external_network: { get_resource: external_network }
+          boot_volumes_size: 40
+          boot_from_volume: { get_param: boot_from_volume  }
 
 outputs:
   controller_floating_ip_addresses:
diff --git a/releasenotes/notes/allow_boot_from_volume-d85a6fef6ec2eced.yaml b/releasenotes/notes/allow_boot_from_volume-d85a6fef6ec2eced.yaml
new file mode 100644
index 0000000..2ca7211
--- /dev/null
+++ b/releasenotes/notes/allow_boot_from_volume-d85a6fef6ec2eced.yaml
@@ -0,0 +1,8 @@
+---
+features:
+  - |
+    Support new environment variable ``ATMOSPHERE_BOOT_FROM_VOLUME``,
+    When this boolean variable is set (like ``true``, ``yes``,
+    or anytihng can be accpeted by Ansible ``bool`` filter),
+    you can change OpenStack instances to boot from volume, and allow image not
+    specify local disk.