Add ability to pass hardware metadata

Related-Prod: PRODX-00000
Change-Id: I4c377558ffe13e28f8ac5ced45262ff67556f46c
diff --git a/de/heat-templates/scripts/instance_boot.sh b/de/heat-templates/scripts/instance_boot.sh
index a1928b0..0e7cde0 100644
--- a/de/heat-templates/scripts/instance_boot.sh
+++ b/de/heat-templates/scripts/instance_boot.sh
@@ -80,8 +80,9 @@
 
 function wait_condition_send {
     local status=${1:-SUCCESS}
-    local reason=${2:-empty}
-    local data_binary="{\"status\": \"$status\", \"reason\": \"$reason\"}"
+    local reason=${2:-\"empty\"}
+    local data=${3:-empty}
+    local data_binary="{\"status\": \"$status\", \"reason\": \"$reason\", \"data\": $data}"
     echo "Trying to send signal to wait condition 5 times: $data_binary"
     WAIT_CONDITION_NOTIFY_EXIT_CODE=2
     i=0
@@ -315,8 +316,21 @@
     retry 10 "Labeling node failed" set_node_labels_retry
 }
 
+HW_METADATA=''
+# Place files specified in metadata to system.
+# For example netplan.io metadata, the restart of services
+# is not covered by script.
+function prepare_metadata_files {
+    /usr/sbin/prepare-metadata.py  --metadata-file /usr/share/metadata/lab-metadata.yaml
+    if [[ -f /usr/share/metadata/ceph.yaml ]]; then
+        HW_METADATA="{\"ceph\": {\"$(hostname)\": \"$(base64 -w 0 /usr/share/metadata/ceph.yaml)\"}}"
+    fi
+}
+
+
 case "$NODE_TYPE" in
     ucp)
+        prepare_metadata_files
         prepare_network
         update_docker_network
         install_required_packages
@@ -333,6 +347,7 @@
         set_node_labels
         ;;
     master)
+        prepare_metadata_files
         prepare_network
         update_docker_network
         install_required_packages
@@ -346,6 +361,7 @@
         set_node_labels
         ;;
     worker)
+        prepare_metadata_files
         prepare_network
         update_docker_network
         install_required_packages
@@ -364,4 +380,4 @@
 esac
 
 
-wait_condition_send "SUCCESS" "Instance successfuly started."
+wait_condition_send "SUCCESS" "Instance successfuly started." "${HW_METADATA}"
diff --git a/de/heat-templates/scripts/prepare-metadata.py b/de/heat-templates/scripts/prepare-metadata.py
new file mode 100644
index 0000000..93c4cf5
--- /dev/null
+++ b/de/heat-templates/scripts/prepare-metadata.py
@@ -0,0 +1,109 @@
+#!/usr/bin/python3
+#
+# Copyright 2018 Mirantis, Inc.
+#
+#    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.
+
+
+"""Prepare metadata python module
+
+The module is aimed to prepare system files files (networking configs etc)
+based on lab metadata.
+
+Example:
+  python prepare-metadata --metadata-file '/etc/lab-metadata.yaml'
+
+Example of lab-metadata.yaml
+
+'52:54:00:10:94:78':
+  write_files:
+  - path: '/tmp/123.yaml'
+    content: |
+      foo: bar
+
+Attributes:
+  metadata-file - The file with metadata
+"""
+
+
+__version__ = '1.0'
+
+import argparse
+import os
+import yaml
+import logging
+import netifaces
+import sys
+
+LOG = logging.getLogger(__name__)
+
+
+def main():
+    logging.basicConfig(
+        format='%(asctime)s - %(levelname)s - %(message)s'
+    )
+    LOG.setLevel(logging.INFO)
+
+    parser = argparse.ArgumentParser(
+        description=('Render system files based on metadata')
+    )
+
+    group = parser.add_argument_group()
+    group.add_argument(
+        '--metadata-file',
+        help='The path to metadata file.',
+        required=True
+    )
+    args = parser.parse_args()
+
+    with open(args.metadata_file) as f:
+        metadata = yaml.safe_load(f)
+    node_meta = get_node_metadata(metadata)
+    if node_meta is not None:
+        LOG.info(f"Processing node_metadata: {node_meta}")
+        create_files(node_meta.get('write_files', []))
+    else:
+        LOG.error("No matches to MACs for node_metadata found")
+
+def get_interface_mac(iface_name):
+    mac = None
+    ifaddresses = netifaces.ifaddresses(iface_name)
+    link = ifaddresses.get(netifaces.AF_LINK, [])
+    if link:
+        return link[0]['addr']
+
+def get_node_macs():
+    ifaces = netifaces.interfaces()
+    macs = [get_interface_mac(iface_name) for iface_name in ifaces]
+    return [mac for mac in macs if mac is not None]
+
+def get_node_metadata(metadata):
+    for mac in get_node_macs():
+        if mac in metadata:
+             return metadata[mac]
+
+def create_files(files_meta):
+    for file_meta in files_meta:
+        path = file_meta['path']
+        content = file_meta['content']
+        permissions = int(str(file_meta.get('permissions', '644')), base=8)
+        with open(path, "w") as f:
+            f.write(content)
+        os.chmod(path, permissions)
+
+if __name__ == '__main__':
+    try:
+        main()
+    except Exception as e:
+        LOG.exception(f"Failed to apply image layout: {e}")
+        sys.exit(1)