├── requirements.txt ├── app ├── static │ ├── config.txt │ ├── roles │ │ └── compute │ └── clr-installer │ │ └── cloud.yaml ├── settings.py └── app.py ├── AUTHORS ├── parameters.conf ├── .gitignore ├── README.rst ├── install.sh ├── configure-ipxe.sh └── LICENSE /requirements.txt: -------------------------------------------------------------------------------- 1 | flask>=0.12.3 2 | -------------------------------------------------------------------------------- /app/static/config.txt: -------------------------------------------------------------------------------- 1 | #MAC,role 2 | default,compute 3 | ff:ff:ff:ff:ff:ff,compute 4 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Robert Nesius 2 | George T Kramer 3 | -------------------------------------------------------------------------------- /app/settings.py: -------------------------------------------------------------------------------- 1 | import os 2 | APP_ROOT = os.path.dirname(os.path.abspath(__file__)) 3 | APP_STATIC = os.path.join(APP_ROOT, 'static') 4 | SUBDIR = os.path.join('/', os.path.basename(APP_ROOT)) 5 | -------------------------------------------------------------------------------- /app/static/roles/compute: -------------------------------------------------------------------------------- 1 | #cloud-config 2 | packages: 3 | - python-basic 4 | 5 | groups: 6 | - cloud-init 7 | 8 | users: 9 | - name: cloudinit 10 | groups: cloud-init 11 | homedir: /home/cloudinit 12 | no-log-init: true 13 | sudo: 14 | - ALL ALL=(ALL) NOPASSWD:ALL 15 | 16 | -------------------------------------------------------------------------------- /parameters.conf: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | uwsgi_app_dir=/usr/share/uwsgi 3 | uwsgi_socket_dir=/run/uwsgi 4 | ccis_app_name=ccis 5 | 6 | web_root=/var/www 7 | ipxe_root=$web_root/ipxe 8 | ccis_root=$web_root/$ccis_app_name 9 | tftp_root=/srv/tftp 10 | 11 | external_iface=eno1 12 | internal_iface=eno2 13 | pxe_subnet=192.168.1 14 | pxe_internal_ip=$pxe_subnet.1 15 | pxe_subnet_mask_ip=255.255.255.0 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Virtualenv 2 | # http://iamzed.com/2009/05/07/a-primer-on-virtualenv/ 3 | .Python 4 | [Bb]in 5 | [Ii]nclude 6 | [Ll]ib 7 | [Ll]ib64 8 | [Ll]ocal 9 | [Ss]cripts 10 | pyvenv.cfg 11 | .venv 12 | pip-selfcheck.json 13 | # Vim 14 | [._]*.s[a-w][a-z] 15 | [._]s[a-w][a-z] 16 | *.un~ 17 | Session.vim 18 | .netrwhist 19 | *~ 20 | ##flask 21 | # 22 | .DS_Store 23 | *.pyc 24 | *.pyo 25 | env 26 | env* 27 | dist 28 | build 29 | *.egg 30 | *.egg-info 31 | _mailinglist 32 | .tox 33 | .cache/ 34 | .idea/ 35 | __pycache* 36 | -------------------------------------------------------------------------------- /app/static/clr-installer/cloud.yaml: -------------------------------------------------------------------------------- 1 | #clear-linux-config 2 | 3 | # switch between aliases if you want to install to an actuall block device 4 | # i.e /dev/sda 5 | block-devices: [ 6 | {name: "bdevice", file: "cloud.img"} 7 | ] 8 | 9 | targetMedia: 10 | - name: ${bdevice} 11 | size: "864M" 12 | type: disk 13 | children: 14 | - name: ${bdevice}1 15 | fstype: vfat 16 | mountpoint: /boot 17 | size: "64M" 18 | type: part 19 | - name: ${bdevice}2 20 | fstype: ext4 21 | mountpoint: / 22 | size: "800M" 23 | type: part 24 | 25 | bundles: [ 26 | bootloader, 27 | cloud-network, 28 | os-cloudguest, 29 | os-core, 30 | os-core-update, 31 | ] 32 | 33 | autoUpdate: false 34 | postArchive: false 35 | postReboot: false 36 | telemetry: false 37 | 38 | keyboard: us 39 | language: en_US.UTF-8 40 | kernel: kernel-kvm 41 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | CCIS: Clear Cloud Initialization Service 2 | ####################################### 3 | 4 | CCIS is a service that `clr-installer`_ uses to automatically install an instance of 5 | Clear Linux. It applies role-based cloud-init configurations during the 6 | installation. It can be used to stand up a cluster of Clear Linux instances 7 | that are ready to be managed with Ansible. 8 | 9 | Getting Started 10 | =============== 11 | 12 | To get started, simply ``git clone https://github.com/clearlinux/clr-cloud-init-svc.git``, 13 | configure parameters.conf to suit your system, and run ``install.sh``. 14 | This will provision a PXE server, install CCIS with default configurations, 15 | and disable NetworkManager for the internal/external interfaces set in 16 | parameters.conf which allows the systemd-networkd settings to take effect. 17 | 18 | The default configuration for provisioning a PXE server creates a router that 19 | performs network address translation for PXE clients. Additional requirements 20 | that must be met to use the default configuration out of the box are outlined in 21 | the preparations section of the `network booting`_ documentation for Clear 22 | Linux. 23 | 24 | CCIS relies on cloud-init configurations to perform an automated installation of 25 | Clear Linux. These need to be changed to apply user-specific configurations. 26 | Instructions on how to change these are outlined in the `bulk provisioning`_ 27 | documentation for Clear Linux. 28 | 29 | 30 | .. _clr-installer: https://github.com/clearlinux/clr-installer 31 | .. _network booting: https://clearlinux.org/documentation/clear-linux/guides/network/ipxe-install 32 | .. _bulk provisioning: https://clearlinux.org/documentation/clear-linux/guides/maintenance/bulk-provision 33 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | source $(dirname $0)/parameters.conf 3 | 4 | main() { 5 | install_dependencies 6 | stop_web_services 7 | $(dirname $0)/configure-ipxe.sh 8 | if [ $? -eq 0 ]; then 9 | populate_ccis_content 10 | generate_web_configuration 11 | start_web_services 12 | return 0 13 | else 14 | echo 'CCIS not installed!!' 15 | return 1 16 | fi 17 | } 18 | 19 | install_dependencies() { 20 | swupd bundle-add pxe-server python-basic-dev python3-basic-static 21 | pip install uwsgi 22 | } 23 | 24 | stop_web_services() { 25 | systemctl stop nginx 26 | systemctl stop uwsgi@$ccis_app_name.socket 27 | systemctl disable uwsgi@$ccis_app_name.service 28 | } 29 | 30 | populate_ccis_content() { 31 | # Reference: http://uwsgi-docs.readthedocs.io/en/latest/Systemd.html#one-service-per-app-in-systemd 32 | # Reference: https://www.dabapps.com/blog/introduction-to-pip-and-virtualenv-python/ 33 | rm -rf $ccis_root 34 | mkdir -p $ccis_root 35 | cp -rf $(dirname ${0})/app/* $ccis_root 36 | local ccis_venv_dir=$ccis_root/env 37 | virtualenv $ccis_venv_dir 38 | $ccis_venv_dir/bin/pip install -r $(dirname ${0})/requirements.txt 39 | 40 | mkdir -p $uwsgi_app_dir 41 | cat > $uwsgi_app_dir/$ccis_app_name.ini << EOF 42 | [uwsgi] 43 | # App configurations 44 | module = app 45 | callable = app 46 | chdir = $ccis_root 47 | home = $ccis_venv_dir 48 | 49 | # Init system configurations 50 | master = true 51 | cheap = true 52 | idle = 600 53 | die-on-idle = true 54 | manage-script-name = true 55 | EOF 56 | } 57 | 58 | generate_web_configuration() { 59 | local nginx_dir=/etc/nginx 60 | mkdir -p $nginx_dir/conf.d 61 | cp -f /usr/share/nginx/conf/nginx.conf.example $nginx_dir/nginx.conf 62 | cat > /etc/nginx/conf.d/pxe.conf << EOF 63 | server { 64 | listen 80; 65 | server_name localhost; 66 | location / { 67 | root $ipxe_root; 68 | autoindex on; 69 | } 70 | location /$ccis_app_name/static/ { 71 | root $ccis_root/static; 72 | rewrite ^/$ccis_app_name/static(/.*)$ \$1 break; 73 | } 74 | location /$ccis_app_name/ { 75 | uwsgi_pass unix://$uwsgi_socket_dir/$ccis_app_name.sock; 76 | include uwsgi_params; 77 | } 78 | } 79 | EOF 80 | } 81 | 82 | start_web_services() { 83 | systemctl enable uwsgi@$ccis_app_name.service 84 | systemctl enable uwsgi@$ccis_app_name.socket 85 | systemctl restart uwsgi@$ccis_app_name.socket 86 | systemctl enable nginx 87 | systemctl restart nginx 88 | } 89 | 90 | main 91 | -------------------------------------------------------------------------------- /app/app.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2016 Intel Corporation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | from flask import Flask, abort, request, jsonify 18 | from settings import APP_STATIC, SUBDIR 19 | import os 20 | import re 21 | 22 | app = Flask(__name__) 23 | 24 | 25 | def get_config_data(mac_addr): 26 | data = dict() 27 | ddata = dict() 28 | pattern = '^%s' % mac_addr 29 | expr = re.compile(pattern) 30 | dpattern = '^default' 31 | dexpr = re.compile(dpattern) 32 | found_default = False 33 | 34 | with open(os.path.join(APP_STATIC, 'config.txt')) as f: 35 | for line in f: 36 | if expr.match(line): 37 | items = line.rstrip().split(',') 38 | data['mac_addr'] = items[0] 39 | data['role'] = items[1] 40 | return jsonify(data) 41 | if dexpr.match(line): 42 | items = line.rstrip().split(',') 43 | ddata['mac_addr'] = items[0] 44 | ddata['role'] = items[1] 45 | found_default = True 46 | 47 | if found_default: 48 | return jsonify(ddata) 49 | 50 | return None 51 | 52 | 53 | @app.route(SUBDIR + '/') 54 | def index(): 55 | return 'Clear Cloud Init Service... is alive\n' 56 | 57 | 58 | @app.route(SUBDIR + '/get_config/') 59 | @app.route(SUBDIR + '/get_config/') 60 | def get_config(mac_addr=None): 61 | reply = None 62 | if mac_addr is None: 63 | mac_addr = request.args.get('MAC') 64 | if mac_addr: 65 | reply = get_config_data(mac_addr) 66 | if not reply: 67 | return 'No config info for %s' % mac_addr 68 | else: 69 | return reply 70 | else: 71 | abort(404) 72 | 73 | 74 | @app.route(SUBDIR + '/get_role/') 75 | @app.route(SUBDIR + '/get_role/') 76 | def get_role(role=None): 77 | reply = None 78 | if role is None: 79 | role = request.args.get('role') 80 | if role: 81 | role_file = os.path.join(APP_STATIC, "roles", role) 82 | if os.path.isfile(role_file): 83 | with open(role_file) as f: 84 | return f.read() 85 | else: 86 | abort(404) 87 | else: 88 | abort(404) 89 | 90 | 91 | if __name__ == '__main__': 92 | app.run() 93 | -------------------------------------------------------------------------------- /configure-ipxe.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | source $(dirname $0)/parameters.conf 3 | 4 | main() { 5 | if [ -d /sys/class/net/$external_iface ] && [ -d /sys/class/net/$internal_iface ] && [[ $(grep '^up$' /sys/class/net/$external_iface/operstate) ]] && [[ $(grep '^up$' /sys/class/net/$internal_iface/operstate) ]]; then 6 | populate_ipxe_content 7 | configure_tftp_server 8 | configure_dns_server 9 | configure_network 10 | configure_dhcp_server 11 | configure_nat 12 | return 0 13 | else 14 | echo 'ERROR: External interface or internal interface does not exist!! Alternatively, external interface or internal interface is not up!!' 15 | echo 'PXE not configured!!' 16 | return 1 17 | fi 18 | } 19 | 20 | populate_ipxe_content() { 21 | rm -rf $ipxe_root 22 | mkdir -p $ipxe_root 23 | curl -o /tmp/clear-pxe.tar.xz https://download.clearlinux.org/current/clear-$(curl https://download.clearlinux.org/latest)-pxe.tar.xz 24 | tar -xJf /tmp/clear-pxe.tar.xz -C $ipxe_root 25 | ln -sf $(ls $ipxe_root | grep 'org.clearlinux.*') $ipxe_root/linux 26 | cat > $ipxe_root/ipxe_boot_script.txt << EOF 27 | #!ipxe 28 | kernel linux quiet init=/usr/lib/systemd/systemd-bootchart initcall_debug tsc=reliable no_timer_check noreplace-smp rw initrd=initrd clri.descriptor=http://$pxe_internal_ip/$ccis_app_name/static/clr-installer/cloud.yaml 29 | initrd initrd 30 | boot 31 | EOF 32 | } 33 | 34 | configure_tftp_server() { 35 | rm -rf $tftp_root 36 | mkdir -p $tftp_root 37 | ln -sf /usr/share/ipxe/ipxe-x86_64.efi $tftp_root/ipxe-x86_64.efi 38 | ln -sf /usr/share/ipxe/undionly.kpxe $tftp_root/undionly.kpxe 39 | cat > /etc/dnsmasq.conf << EOF 40 | enable-tftp 41 | tftp-root=$tftp_root 42 | EOF 43 | 44 | systemctl enable dnsmasq 45 | 46 | } 47 | 48 | configure_dns_server() { 49 | mkdir -p /etc/systemd 50 | cat > /etc/systemd/resolved.conf << EOF 51 | [Resolve] 52 | DNSStubListener=no 53 | EOF 54 | cat >> /etc/dnsmasq.conf << EOF 55 | listen-address=$pxe_internal_ip 56 | EOF 57 | 58 | systemctl stop systemd-resolved 59 | systemctl restart dnsmasq 60 | systemctl start systemd-resolved 61 | } 62 | 63 | configure_network() { 64 | mkdir -p /etc/systemd/network 65 | ln -sf /dev/null /etc/systemd/network/80-dhcp.network 66 | cat > /etc/systemd/network/80-external-dynamic.network << EOF 67 | [Match] 68 | Name=$external_iface 69 | [Network] 70 | DHCP=yes 71 | EOF 72 | local pxe_subnet_bitmask 73 | convert_ip_address_to_bitmask $pxe_subnet_mask_ip pxe_subnet_bitmask 74 | cat > /etc/systemd/network/80-internal-static.network << EOF 75 | [Match] 76 | Name=$internal_iface 77 | [Network] 78 | DHCP=no 79 | Address=$pxe_internal_ip/$pxe_subnet_bitmask 80 | EOF 81 | if systemctl is-active --quiet NetworkManager; then 82 | echo "Disabling NetworkManager for $external_iface and $internal_iface" 83 | nmcli device set $external_iface managed false 84 | nmcli device set $internal_iface managed false 85 | fi 86 | 87 | systemctl restart systemd-networkd 88 | } 89 | 90 | convert_ip_address_to_bitmask() { 91 | local binary='' 92 | local D2B=({0..1}{0..1}{0..1}{0..1}{0..1}{0..1}{0..1}{0..1}) 93 | local decimals=($(tr '.' ' ' <<< $1)) 94 | local decimal 95 | for decimal in "${decimals[@]}"; do 96 | binary=$binary${D2B[$decimal]} 97 | done 98 | eval "$2=$(grep -o 1 <<< $binary | wc -l)" 99 | } 100 | 101 | configure_dhcp_server() { 102 | cat > /etc/dhcpd.conf << EOF 103 | # iPXE-specific options 104 | # Source: http://www.ipxe.org/howto/dhcpd 105 | option space ipxe; 106 | option ipxe-encap-opts code 175 = encapsulate ipxe; 107 | option ipxe.priority code 1 = signed integer 8; 108 | option ipxe.keep-san code 8 = unsigned integer 8; 109 | option ipxe.skip-san-boot code 9 = unsigned integer 8; 110 | option ipxe.syslogs code 85 = string; 111 | option ipxe.cert code 91 = string; 112 | option ipxe.privkey code 92 = string; 113 | option ipxe.crosscert code 93 = string; 114 | option ipxe.no-pxedhcp code 176 = unsigned integer 8; 115 | option ipxe.bus-id code 177 = string; 116 | option ipxe.bios-drive code 189 = unsigned integer 8; 117 | option ipxe.username code 190 = string; 118 | option ipxe.password code 191 = string; 119 | option ipxe.reverse-username code 192 = string; 120 | option ipxe.reverse-password code 193 = string; 121 | option ipxe.version code 235 = string; 122 | option iscsi-initiator-iqn code 203 = string; 123 | option ipxe.pxeext code 16 = unsigned integer 8; 124 | option ipxe.iscsi code 17 = unsigned integer 8; 125 | option ipxe.aoe code 18 = unsigned integer 8; 126 | option ipxe.http code 19 = unsigned integer 8; 127 | option ipxe.https code 20 = unsigned integer 8; 128 | option ipxe.tftp code 21 = unsigned integer 8; 129 | option ipxe.ftp code 22 = unsigned integer 8; 130 | option ipxe.dns code 23 = unsigned integer 8; 131 | option ipxe.bzimage code 24 = unsigned integer 8; 132 | option ipxe.multiboot code 25 = unsigned integer 8; 133 | option ipxe.slam code 26 = unsigned integer 8; 134 | option ipxe.srp code 27 = unsigned integer 8; 135 | option ipxe.nbi code 32 = unsigned integer 8; 136 | option ipxe.pxe code 33 = unsigned integer 8; 137 | option ipxe.elf code 34 = unsigned integer 8; 138 | option ipxe.comboot code 35 = unsigned integer 8; 139 | option ipxe.efi code 36 = unsigned integer 8; 140 | option ipxe.fcoe code 37 = unsigned integer 8; 141 | option ipxe.vlan code 38 = unsigned integer 8; 142 | option ipxe.menu code 39 = unsigned integer 8; 143 | option ipxe.sdi code 40 = unsigned integer 8; 144 | option ipxe.nfs code 41 = unsigned integer 8; 145 | 146 | class "PXE-Chainload" { 147 | match if substring(option vendor-class-identifier, 0, 9) = "PXEClient"; 148 | 149 | next-server $pxe_internal_ip; 150 | if exists user-class and option user-class = "iPXE" { 151 | filename "http://$pxe_internal_ip/ipxe_boot_script.txt"; 152 | } 153 | elsif substring(option vendor-class-identifier, 0, 20) = "PXEClient:Arch:00007" or substring(option vendor-class-identifier, 0, 20) = "PXEClient:Arch:00008" or substring(option vendor-class-identifier, 0, 20) = "PXEClient:Arch:00009" { 154 | filename "ipxe-x86_64.efi"; 155 | } 156 | elsif substring(option vendor-class-identifier, 0, 20) = "PXEClient:Arch:00000" { 157 | filename "undionly.kpxe"; 158 | } 159 | } 160 | 161 | subnet $pxe_subnet.0 netmask $pxe_subnet_mask_ip { 162 | authoritative; 163 | option routers $pxe_internal_ip; 164 | option domain-name-servers $pxe_internal_ip; 165 | 166 | pool { 167 | allow members of "PXE-Chainload"; 168 | range $pxe_subnet.128 $pxe_subnet.253; 169 | default-lease-time 600; 170 | max-lease-time 3600; 171 | } 172 | 173 | pool { 174 | deny members of "PXE-Chainload"; 175 | range $pxe_subnet.2 $pxe_subnet.127; 176 | default-lease-time 3600; 177 | max-lease-time 21600; 178 | } 179 | } 180 | EOF 181 | 182 | mkdir -p /var/db 183 | touch /var/db/dhcpd.leases 184 | 185 | systemctl enable dhcp4 186 | systemctl restart dhcp4 187 | } 188 | 189 | configure_nat() { 190 | iptables -t nat -F POSTROUTING 191 | iptables -t nat -A POSTROUTING -o $external_iface -j MASQUERADE 192 | systemctl enable iptables-save.service 193 | systemctl restart iptables-save.service 194 | systemctl enable iptables-restore.service 195 | systemctl restart iptables-restore.service 196 | 197 | mkdir -p /etc/sysctl.d 198 | echo net.ipv4.ip_forward=1 > /etc/sysctl.d/80-nat-forwarding.conf 199 | echo 1 > /proc/sys/net/ipv4/ip_forward 200 | } 201 | 202 | main 203 | 204 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | --------------------------------------------------------------------------------