├── LICENSE ├── Makefile ├── README.md ├── create-config-drive ├── os-create-config-drive ├── virt-addr ├── virt-boot ├── virt-delete ├── virt-disks ├── virt-from ├── virt-hosts ├── virt-interfaces ├── virt-pick ├── virt-pick-vol ├── virt-query ├── virt-vol └── write-mime-multipart /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | prefix=/usr/local 2 | bindir=$(prefix)/bin 3 | 4 | SCRIPTS = \ 5 | virt-query \ 6 | virt-addr \ 7 | virt-delete \ 8 | virt-disks \ 9 | virt-from \ 10 | virt-hosts \ 11 | virt-interfaces \ 12 | virt-pick \ 13 | virt-pick-vol \ 14 | virt-boot \ 15 | virt-vol \ 16 | create-config-drive \ 17 | write-mime-multipart 18 | 19 | all: 20 | 21 | install: all 22 | install -m 755 -d $(DESTDIR)$(bindir)/ 23 | install -m 755 $(SCRIPTS) $(DESTDIR)$(bindir)/ 24 | 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Things that are here: 2 | 3 | - virt-addr -- list the ip addresses assigned to a domain by scanning 4 | the lease file for the `default` libvirt network. 5 | - virt-disks -- list the disk images associated with a domain. 6 | - virt-delete -- delete a domain and the disks images it was using. 7 | - virt-from -- create a new domain from a snapshot of an existing 8 | image. 9 | - virt-hosts -- return an `/etc/hosts` style listing of running 10 | domains and their ip addresses. 11 | - virt-interfaces -- produce a listing of network interfaces 12 | associated with a domain. 13 | - virt-pick -- use xdialog to select a domain. Accepts the same 14 | arguments as "virsh list". 15 | - virt-vol -- quickly create a snapshot from an existing libvirt 16 | volume 17 | - create-config-drive -- create a cloud-init/openstack compatible 18 | config ISO image 19 | - virt-boot -- wrapper for virt-install to create a snapshot, 20 | populate a cloud-init compatible config drive, and boot an instance. 21 | -------------------------------------------------------------------------------- /create-config-drive: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # This will generate a openstack-style config drive image suitable for 4 | # use with cloud-init. You may optionally pass in an ssh public key 5 | # (using the -k/--ssh-key option) and a user-data blob (using the 6 | # -u/--user-data option). 7 | 8 | usage () { 9 | echo "usage: ${0##*/}: [--ssh-key ] [--vendor-data ] [--user-data ] [--hostname ] " 10 | } 11 | 12 | ARGS=$(getopt \ 13 | -o k:u:v:h: \ 14 | --long help,hostname:,ssh-key:,user-data:,vendor-data: -n ${0##*/} \ 15 | -- "$@") 16 | 17 | if [ $? -ne 0 ]; then 18 | usage >&2 19 | exit 2 20 | fi 21 | 22 | eval set -- "$ARGS" 23 | 24 | while :; do 25 | case "$1" in 26 | --help) 27 | usage 28 | exit 0 29 | ;; 30 | -k|--ssh-key) 31 | ssh_key="$2" 32 | shift 2 33 | ;; 34 | -u|--user-data) 35 | user_data="$2" 36 | shift 2 37 | ;; 38 | -v|--vendor-data) 39 | vendor_data="$2" 40 | shift 2 41 | ;; 42 | -h|--hostname) 43 | hostname="$2" 44 | shift 2 45 | ;; 46 | --) shift 47 | break 48 | ;; 49 | esac 50 | done 51 | 52 | config_image=$1 53 | shift 54 | 55 | if [ "$ssh_key" ] && [ -f "$ssh_key" ]; then 56 | echo "adding pubkey from $ssh_key" 57 | ssh_key_data=$(cat "$ssh_key") 58 | fi 59 | 60 | uuid=$(uuidgen) 61 | if ! [ "$hostname" ]; then 62 | hostname="$uuid" 63 | fi 64 | 65 | trap 'rm -rf $config_dir' EXIT 66 | config_dir=$(mktemp -t -d configXXXXXX) 67 | 68 | if [ "$user_data" ] && [ -f "$user_data" ]; then 69 | echo "adding user data from $user_data" 70 | cp $user_data $config_dir/user-data 71 | else 72 | touch $config_dir/user-data 73 | fi 74 | 75 | if [ "$vendor_data" ] && [ -f "$vendor_data" ]; then 76 | echo "adding vendor data from $vendor_data" 77 | cp $vendor_data $config_dir/vendor-data 78 | fi 79 | 80 | cat > $config_dir/meta-data <<-EOF 81 | instance-id: $uuid 82 | hostname: $hostname 83 | local-hostname: $hostname 84 | EOF 85 | 86 | if [ "$ssh_key_data" ]; then 87 | cat >> $config_dir/meta-data <<-EOF 88 | public-keys: 89 | - | 90 | $ssh_key_data 91 | EOF 92 | fi 93 | 94 | #PS1="debug> " bash --norc 95 | 96 | echo "generating configuration image at $config_image" 97 | if ! mkisofs -o $config_image -V cidata -r -J --quiet $config_dir; then 98 | echo "ERROR: failed to create $config_image" >&2 99 | exit 1 100 | fi 101 | chmod a+r $config_image 102 | 103 | -------------------------------------------------------------------------------- /os-create-config-drive: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # This will generate a openstack-style config drive image suitable for 4 | # use with cloud-init. You may optionally pass in an ssh public key 5 | # (using the -k/--ssh-key option) and a user-data blog (using the 6 | # -u/--user-data option). 7 | 8 | md_api_versions=' 9 | 2015-10-15 10 | 2016-06-30 11 | 2016-10-06 12 | 2017-02-22 13 | ' 14 | 15 | 16 | usage () { 17 | echo "usage: ${0##*/}: [--ssh-key ] [--vendor-data ] [--user-data ] [--hostname ] " 18 | } 19 | 20 | ARGS=$(getopt \ 21 | -o k:u:v:h:n: \ 22 | --long help,hostname:,ssh-key:,user-data:,vendor-data:,network-data: -n ${0##*/} \ 23 | -- "$@") 24 | 25 | if [ $? -ne 0 ]; then 26 | usage >&2 27 | exit 2 28 | fi 29 | 30 | eval set -- "$ARGS" 31 | 32 | while :; do 33 | case "$1" in 34 | --help) 35 | usage 36 | exit 0 37 | ;; 38 | -k|--ssh-key) 39 | ssh_key="$2" 40 | shift 2 41 | ;; 42 | -u|--user-data) 43 | user_data="$2" 44 | shift 2 45 | ;; 46 | -v|--vendor-data) 47 | vendor_data="$2" 48 | shift 2 49 | ;; 50 | -n|--network-data) 51 | network_data="$2" 52 | shift 2 53 | ;; 54 | -h|--hostname) 55 | hostname="$2" 56 | shift 2 57 | ;; 58 | --) shift 59 | break 60 | ;; 61 | esac 62 | done 63 | 64 | if ! [ "$#" -eq 1 ]; then 65 | echo "ERROR: missing target filename" >&2 66 | exit 1 67 | fi 68 | 69 | config_image=$1 70 | shift 71 | 72 | if [ "$ssh_key" ] && [ -f "$ssh_key" ]; then 73 | echo "adding pubkey from $ssh_key" 74 | ssh_key_data=$(cat "$ssh_key") 75 | fi 76 | 77 | uuid=$(uuidgen) 78 | if ! [ "$hostname" ]; then 79 | hostname="$uuid" 80 | fi 81 | 82 | trap 'rm -rf $config_dir' EXIT 83 | config_dir=$(mktemp -t -d configXXXXXX) 84 | 85 | mkdir -p "${config_dir}/openstack/latest" 86 | 87 | if [ "$user_data" ] && [ -f "$user_data" ]; then 88 | echo "adding user data from $user_data" 89 | cp $user_data $config_dir/openstack/latest/user-data 90 | fi 91 | 92 | if [ "$vendor_data" ] && [ -f "$vendor_data" ]; then 93 | echo "adding vendor data from $vendor_data" 94 | cp $vendor_data "$config_dir/openstack/latest/vendor_data.json" 95 | else 96 | echo "{}" > "$config_dir/openstack/latest/vendor_data.json" 97 | fi 98 | 99 | if [ "$network_data" ] && [ -f "$network_data" ]; then 100 | echo "adding network data from $network_data" 101 | cp $network_data "$config_dir/openstack/latest/network_data.json" 102 | fi 103 | 104 | cat > $config_dir/openstack/latest/meta_data.json <<-EOF 105 | { 106 | "uuid": "$uuid", 107 | "hostname": "$hostname", 108 | "name": "$hostname", 109 | "launch_index": 0, 110 | "availability_zone": "nova" 111 | EOF 112 | 113 | if [ "$ssh_key_data" ]; then 114 | cat >> $config_dir/meta_data.json <<-EOF 115 | , 116 | "keys": [ 117 | { "name": "default", "type": "ssh", "data": "$ssh_key_data" } 118 | ], 119 | "public_keys": { 120 | "default": "$ssh_key_data" 121 | } 122 | EOF 123 | fi 124 | 125 | echo "}" >> $config_dir/openstack/latest/meta_data.json 126 | 127 | for v in $md_api_versions; do 128 | echo "setting up api version $v" 129 | cp -a "$config_dir/openstack/latest" "$config_dir/openstack/$v" 130 | done 131 | 132 | #PS1="debug> " bash --norc 133 | 134 | echo "generating configuration image at $config_image" 135 | if ! mkisofs -o $config_image -V cidata -r -J --quiet $config_dir; then 136 | echo "ERROR: failed to create $config_image" >&2 137 | exit 1 138 | fi 139 | chmod a+r $config_image 140 | 141 | -------------------------------------------------------------------------------- /virt-addr: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ARGS=$(getopt -o mn:i: --long macaddr,network:,index: -n virt-addr -- "$@") 4 | 5 | if [ $? -ne 0 ]; then 6 | echo "usage: $0: [--network ] [--index ] " >&2 7 | exit 2 8 | fi 9 | 10 | eval set -- "$ARGS" 11 | 12 | while :; do 13 | case "$1" in 14 | -n|--network) 15 | LIBVIRT_NETWORK=$2 16 | shift 2 17 | ;; 18 | 19 | -i|--index) 20 | INTERFACE_INDEX=$2 21 | shift 2 22 | ;; 23 | -m|--macaddr) 24 | ONLY_MACADDR=1 25 | shift 26 | ;; 27 | -l|--lease-file) 28 | LIBVIRT_LEASE_FILE=$2 29 | shift 2 30 | ;; 31 | --) shift 32 | break 33 | ;; 34 | 35 | -*) echo "ERROR: unimplemented option: $1" >&2 36 | exit 2 37 | ;; 38 | esac 39 | done 40 | 41 | : ${LIBVIRT_NETWORK:=default} 42 | : ${LIBVIRT_LEASE_FILE:=/var/lib/libvirt/dnsmasq/${LIBVIRT_NETWORK}.leases} 43 | : ${INTERFACE_INDEX:=1} 44 | 45 | dom_name=$1 46 | shift 47 | 48 | macaddr=$(virsh dumpxml $dom_name | 49 | xmllint --xpath "string((//interface/mac/@address)[${INTERFACE_INDEX}])" -) 50 | 51 | if [ "$ONLY_MACADDR" = 1 ]; then 52 | echo $macaddr 53 | exit 54 | fi 55 | 56 | awk -vmacaddress=$macaddr '$2 == macaddress {print $3}' $LIBVIRT_LEASE_FILE 57 | 58 | -------------------------------------------------------------------------------- /virt-boot: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | boot_vol_bus=virtio 4 | 5 | ARGS=$(getopt -o s:b:c:u:v:ik: --long ssh-key:,size:,import,bus:,base-image:,config:,user-data:,vendor-data: -n virt-boot -- "$@") 6 | 7 | if [ $? -ne 0 ]; then 8 | echo "usage: $myname --base-image " 9 | exit 2 10 | fi 11 | 12 | eval set -- "$ARGS" 13 | 14 | : ${base_image:=$VIRT_BASE_IMAGE} 15 | 16 | while :; do 17 | case "$1" in 18 | --bus) 19 | boot_vol_bus="$2" 20 | shift 2 21 | ;; 22 | -b|--base-image) 23 | base_image="$2" 24 | shift 2 25 | ;; 26 | -s|--image-size) 27 | base_image_size="$2" 28 | shift 2 29 | ;; 30 | -c|--config-drive) 31 | config_image="$2" 32 | shift 2 33 | ;; 34 | -k|--ssh-key) 35 | ssh_key="$2" 36 | shift 2 37 | ;; 38 | -u|--user-data) 39 | user_data="$2" 40 | shift 2 41 | ;; 42 | -v|--vendor-data) 43 | vendor_data="$2" 44 | shift 2 45 | ;; 46 | -i|--import) 47 | no_snapshot=1 48 | shift 49 | ;; 50 | --) shift 51 | break 52 | ;; 53 | esac 54 | done 55 | 56 | dom_name=$1 57 | shift 58 | 59 | if [ -z "$dom_name" ]; then 60 | echo "ERROR: you must specify a domain name." >&2 61 | exit 1 62 | fi 63 | 64 | if [ "$ssh_key" ] && [ -f "$ssh_key" ]; then 65 | ssh_key_data=$(cat "$ssh_key") 66 | fi 67 | 68 | ( 69 | virsh destroy $dom_name 70 | virsh undefine $dom_name 71 | ) > /dev/null 2>&1 72 | 73 | if [ "$no_snapshot" = 1 ]; then 74 | boot_vol=$base_image 75 | else 76 | boot_vol=$(virt-vol ${base_image_size:+-s $base_image_size} \ 77 | $base_image $dom_name.qcow2) 78 | 79 | if [ $? -ne 0 ]; then 80 | echo "ERROR: failed to create boot volume" >&2 81 | exit 1 82 | fi 83 | fi 84 | 85 | if ! [ "$config_image" ]; then 86 | config_image=/tmp/${dom_name}-config.iso 87 | create-config-drive \ 88 | -h $dom_name \ 89 | ${ssh_key:+-k $ssh_key} \ 90 | ${user_data:+-u $user_data} \ 91 | ${vendor_data:+-v $vendor_data} \ 92 | $config_image 93 | fi 94 | 95 | virt-install -n $dom_name \ 96 | --disk vol=$boot_vol,bus=$boot_vol_bus --import \ 97 | --noautoconsole \ 98 | ${config_image:+--disk path=$config_image,device=cdrom} \ 99 | "$@" 100 | 101 | -------------------------------------------------------------------------------- /virt-delete: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "Destroy: $1" 4 | virsh destroy $1 2> /dev/null 5 | 6 | for disk_path in $(virt-query $1 disks | awk '{print $3}'); do 7 | pool=$(virsh vol-pool $disk_path) 8 | name=$(virsh vol-info $disk_path | awk '$1 == "Name:" {print $2}') 9 | 10 | echo "Delete volume: $disk ($name in $pool)" 11 | virsh vol-delete $name $pool 12 | done 13 | 14 | echo "Undefine: $1" 15 | virsh undefine $1 16 | 17 | -------------------------------------------------------------------------------- /virt-disks: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | virsh dumpxml $1 | 4 | xml2 | 5 | awk -F= '$1 == "/domain/devices/disk/source/@file" {print $2}' 6 | 7 | -------------------------------------------------------------------------------- /virt-from: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | base_format=raw 4 | ram=1024 5 | disksize=8G 6 | force= 7 | 8 | usage () { 9 | echo "$0: usage: $0 [ -r ram ] [ -s disksize ] [ -w network-spec ] [ -IQ ] base name [name...]" 10 | } 11 | 12 | while getopts r:s:w:IhfQ ch; do 13 | case $ch in 14 | (r) ram=$OPTARG;; 15 | (s) disksize=$OPTARG;; 16 | (I) no_instance=1;; 17 | (w) VIRT_INSTALL_ARGS="$VIRT_INSTALL_ARGS -w $OPTARG";; 18 | (f) force=1;; 19 | (Q) base_format=qcow2;; 20 | 21 | (h) usage; exit;; 22 | (\?) usage >&2; exit 2;; 23 | esac 24 | done 25 | shift $(( $OPTIND - 1 )) 26 | 27 | base=$1 28 | shift 29 | 30 | base_pool=${base%/*} 31 | base_vol=${base#*/} 32 | 33 | for name in $*; do 34 | echo "--------------------------------------------------" 35 | echo "$name" 36 | echo 37 | 38 | if [ "$force" = 1 ]; then 39 | virsh vol-delete ${name}.img ${base_pool} > /dev/null 2>&1 40 | fi 41 | 42 | echo "Creating volume" 43 | virsh vol-create-as ${base_pool} ${name}.img ${disksize} \ 44 | --format qcow2 \ 45 | --backing-vol ${base_vol} \ 46 | --backing-vol-format ${base_format} || exit 1 47 | 48 | virsh vol-info ${name}.img ${base_pool} 49 | 50 | if ! [ "$no_instance" = 1 ]; then 51 | echo "Creating instance" 52 | virt-install --noautoconsole \ 53 | -n ${name} -r ${ram} \ 54 | -w network=default,model=virtio \ 55 | --cpu host \ 56 | $VIRT_INSTALL_ARGS \ 57 | --disk vol=${base_pool}/${name}.img,format=qcow2,bus=virtio \ 58 | --import || exit 1 59 | fi 60 | done 61 | 62 | -------------------------------------------------------------------------------- /virt-hosts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import os 4 | import sys 5 | import argparse 6 | import libvirt 7 | from lxml import etree 8 | 9 | lease_fields = ('time', 'macaddr', 'ipaddr', 'f1', 'f2') 10 | 11 | def parse_args(): 12 | p = argparse.ArgumentParser() 13 | p.add_argument('--connect', '-c') 14 | p.add_argument('--network', '-N', default='default') 15 | p.add_argument('--domain', '-D') 16 | p.add_argument('--leases', '-L') 17 | p.add_argument('--hosts', '-H') 18 | p.add_argument('--short', '-s', action='store_true') 19 | p.add_argument('--reload-dnsmasq', '-r', action='store_true') 20 | p.add_argument('--update', '-u', action='store_true') 21 | return p.parse_args() 22 | 23 | def read_leases_file(path): 24 | leases = {} 25 | with open(path) as fd: 26 | for leaseEntry in fd.readlines(): 27 | lease = dict(zip(lease_fields, leaseEntry.split())) 28 | leases[lease['macaddr']] = lease 29 | 30 | return leases 31 | 32 | def main(): 33 | args = parse_args() 34 | 35 | if args.domain is None: 36 | args.domain = '%s.virt' % args.network 37 | 38 | if args.leases is None: 39 | args.leases = '/var/lib/libvirt/dnsmasq/%s.leases' % args.network 40 | if args.hosts is None: 41 | args.hosts = '/var/lib/libvirt/dnsmasq/%s.addnhosts' % args.network 42 | 43 | assert os.path.exists(args.leases) 44 | 45 | with open(args.hosts, 'w') if args.update else sys.stdout as fd: 46 | 47 | leases = read_leases_file(args.leases) 48 | 49 | con = libvirt.openReadOnly(args.connect) 50 | assert con is not None 51 | 52 | for domId in con.listDomainsID(): 53 | dom = con.lookupByID(domId) 54 | desc = etree.fromstring(dom.XMLDesc()) 55 | 56 | primary=True 57 | for iface in desc.xpath('/domain/devices/interface[@type="network"]'): 58 | source = iface.find('source') 59 | 60 | if source.get('network') != args.network: 61 | continue 62 | 63 | macaddr = iface.find('mac').get('address') 64 | alias = iface.find('alias').get('name') 65 | 66 | if macaddr in leases: 67 | ipaddr = leases[macaddr]['ipaddr'] 68 | fd.write('%s\t%s' % ( 69 | ipaddr, 70 | '%s-%s.%s' % (dom.name(), alias, args.domain))) 71 | 72 | if primary: 73 | fd.write(' %s.%s' % (dom.name(), args.domain)) 74 | if args.short: 75 | fd.write(' %s' % (dom.name())) 76 | fd.write('\n') 77 | primary=False 78 | 79 | # This is a hack barring a more structured way of getting the 80 | # appropriate dnsmasq pid from NetworkManager. 81 | if args.reload_dnsmasq: 82 | os.system('killall -HUP dnsmasq') 83 | 84 | if __name__ == '__main__': 85 | main() 86 | 87 | 88 | -------------------------------------------------------------------------------- /virt-interfaces: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | virsh dumpxml $1 | 4 | xml2 | 5 | awk -F= ' 6 | $1 == "/domain/devices/interface/@type" { 7 | if (interface["type"]) { 8 | print interface["type"], interface["source"], interface["device"], interface["macaddress"] 9 | } 10 | interface["type"]=$2 11 | } 12 | 13 | $1 == "/domain/devices/interface/mac/@address" {interface["macaddress"]=$2} 14 | $1 == "/domain/devices/interface/target/@dev" {interface["device"]=$2} 15 | $1 == "/domain/devices/interface/source/@network" {interface["source"]=$2} 16 | $1 == "/domain/devices/interface/source/@bridge" {interface["source"]=$2} 17 | 18 | END { 19 | if (interface["type"]) { 20 | print interface["type"], interface["source"], interface["device"], interface["macaddress"] 21 | } 22 | } 23 | ' 24 | 25 | -------------------------------------------------------------------------------- /virt-pick: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | tmpfile=$(mktemp -t .virtpickXXXXXX) 4 | trap "rm -f $tmpfile" EXIT 5 | virsh list --name "$@" | grep -v '^$' > $tmpfile 6 | 7 | Xdialog --menu "Select a domain" 40 60 30 $(sed = < $tmpfile) 2> $tmpfile.selected 8 | 9 | sed -n "$(tail -1 $tmpfile.selected)p" $tmpfile 10 | rm -f $tmpfile $tmpfile.selected 11 | 12 | -------------------------------------------------------------------------------- /virt-pick-vol: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | tmpfile=$(mktemp -t .virtpickXXXXXX) 4 | trap "rm -f $tmpfile $tmpfile.selected" EXIT 5 | 6 | virsh vol-list ${1:-default} | sed 1,2d > $tmpfile 7 | 8 | Xdialog --menu "Select a volume" 40 60 30 $(cat $tmpfile) 2> $tmpfile.selected 9 | 10 | echo ${1:-default}/$(tail -1 $tmpfile.selected) 11 | 12 | -------------------------------------------------------------------------------- /virt-query: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import os 4 | import sys 5 | import argparse 6 | import libvirt 7 | import logging 8 | import re 9 | from lxml import etree 10 | 11 | re_uuid = re.compile('[a-fA-F0-9-]{36}') 12 | default_connect_uri = os.environ.get('LIBVIRT_DEFAULT_URI') 13 | 14 | def is_uuid(s): 15 | return len(s) == 36 and re_uuid.match(s) is not None 16 | 17 | class Domain (object): 18 | def __init__(self, name, connect_uri=None, readonly=True): 19 | if readonly: 20 | self.connection = libvirt.openReadOnly(connect_uri) 21 | else: 22 | self.connection = libvirt.open(connect_uri) 23 | 24 | self.attach(name) 25 | 26 | def attach(self, name): 27 | if name.isdigit(): 28 | self.dom = self.connection.lookupByID(int(name)) 29 | elif is_uuid(name): 30 | self.dom = self.connection.lookupByUUIDString(name) 31 | else: 32 | self.dom = self.connection.lookupByName(name) 33 | 34 | self.domxml = etree.fromstring(self.dom.XMLDesc()) 35 | 36 | def disks(self): 37 | for disk in self.domxml.xpath('/domain/devices/disk'): 38 | if disk.get('type') == 'file': 39 | info = {'type': 'file', 40 | 'source': disk.find('source').get('file'), 41 | 'target': disk.find('target').get('dev')} 42 | yield info 43 | 44 | def interfaces(self): 45 | for iface in self.domxml.xpath('/domain/devices/interface'): 46 | info = {'type': iface.get('type'), 47 | 'hwaddr': iface.find('mac').get('address'), 48 | 'source': iface.find('source').get(iface.get('type')), 49 | } 50 | 51 | if iface.find('target') is not None: 52 | info['target'] = iface.find('target').get('dev') 53 | 54 | yield info 55 | 56 | 57 | def parse_args(): 58 | p = argparse.ArgumentParser() 59 | p.add_argument('--connect', '-c', 60 | default=default_connect_uri) 61 | p.add_argument('domain') 62 | p.add_argument('what') 63 | return p.parse_args() 64 | 65 | def main(): 66 | args = parse_args() 67 | logging.basicConfig() 68 | 69 | dom = Domain(args.domain, connect_uri=args.connect) 70 | 71 | if args.what in ['disk', 'disks']: 72 | for disk in dom.disks(): 73 | print '{type} {target} {source}'.format(**disk) 74 | elif args.what in ['net', 'interfaces', 'iface']: 75 | for iface in dom.interfaces(): 76 | if not 'target' in iface: 77 | iface['target'] = '' 78 | print '{type} {source} {target} {hwaddr}'.format(**iface) 79 | 80 | if __name__ == '__main__': 81 | main() 82 | 83 | 84 | -------------------------------------------------------------------------------- /virt-vol: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ARGS=$(getopt -o s: --long size: -n virt-boot -- "$@") 4 | 5 | if [ $? -ne 0 ]; then 6 | echo "usage: $myname " 7 | exit 2 8 | fi 9 | 10 | eval set -- "$ARGS" 11 | 12 | while :; do 13 | case "$1" in 14 | -s|--size) 15 | image_size="$2" 16 | shift 2 17 | ;; 18 | --) shift 19 | break 20 | ;; 21 | esac 22 | done 23 | 24 | base_image="$1" 25 | vol_name="$2" 26 | shift 2 27 | 28 | case $base_image in 29 | */*) : 30 | ;; 31 | 32 | *) echo "ERROR: base image must be specified as pool/volume" >&2 33 | exit 1 34 | ;; 35 | esac 36 | 37 | base_image_pool=${base_image%/*} 38 | base_image_name=${base_image#*/} 39 | 40 | if [ -z "$image_size" ]; then 41 | image_size=$(virsh vol-info $base_image_name $base_image_pool | 42 | awk '$1 == "Capacity:" {print $2}' | 43 | cut -f1 -d.) 44 | fi 45 | 46 | if ! virsh vol-key $base_image_name --pool $base_image_pool > /dev/null 2>&1; then 47 | echo "ERROR: base image $base_image does not exist" >&2 48 | exit 1 49 | fi 50 | 51 | ( 52 | virsh vol-delete $vol_name --pool $base_image_pool 53 | ) > /dev/null 2>&1 54 | 55 | virsh vol-create-as $base_image_pool $vol_name ${image_size}G \ 56 | --format qcow2 \ 57 | --backing-vol $base_image_name \ 58 | --backing-vol-format qcow2 >&2 59 | 60 | echo $base_image_pool/$vol_name 61 | 62 | -------------------------------------------------------------------------------- /write-mime-multipart: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import sys 5 | import argparse 6 | import mimetypes 7 | 8 | from email import encoders 9 | from email.message import Message 10 | from email.mime.audio import MIMEAudio 11 | from email.mime.base import MIMEBase 12 | from email.mime.image import MIMEImage 13 | from email.mime.multipart import MIMEMultipart 14 | from email.mime.text import MIMEText 15 | 16 | 17 | starts_with_mappings = { 18 | '#include': 'text/x-include-url', 19 | '#!': 'text/x-shellscript', 20 | '#cloud-config': 'text/cloud-config', 21 | '#upstart-job': 'text/upstart-job', 22 | '#part-handler': 'text/part-handler', 23 | '#cloud-boothook': 'text/cloud-boothook', 24 | '#kube-pod': 'text/x-kube-pod', 25 | '#kube-service': 'text/x-kube-service', 26 | '#kube-replica': 'text/x-kube-replica', 27 | } 28 | 29 | mimetypes.add_type('text/x-markdown', '.md') 30 | mimetypes.add_type('text/x-kube-pod', '.pod') 31 | mimetypes.add_type('text/x-kube-service', '.service') 32 | mimetypes.add_type('text/x-kube-replica', '.replica') 33 | 34 | def guess_mimetype(path): 35 | global args 36 | with open(path) as fd: 37 | firstline = fd.readline() 38 | for value, mimetype in starts_with_mappings.items(): 39 | if firstline.startswith(value): 40 | return mimetype, None 41 | 42 | mimetype = mimetypes.guess_type(path) 43 | return mimetype if mimetype else args.default_mimetype 44 | 45 | 46 | def parse_args(): 47 | p = argparse.ArgumentParser() 48 | 49 | p.add_argument('--output', '-o') 50 | p.add_argument('--merge', '-M') 51 | p.add_argument('--default-mimetype', '-T', 52 | default='application/octet-stream') 53 | 54 | p.add_argument('part', nargs='+') 55 | 56 | return p.parse_args() 57 | 58 | 59 | def main(): 60 | global args 61 | args = parse_args() 62 | 63 | container = MIMEMultipart() 64 | 65 | for part in args.part: 66 | if ':' in part: 67 | path, mimetype = part.split(':') 68 | encoding = None 69 | else: 70 | path = part 71 | mimetype, encoding = guess_mimetype(path) 72 | 73 | if mimetype is None: 74 | print >>sys.stderr, 'ERROR: unable to determine mime type ' \ 75 | 'for file $part.' 76 | sys.exit(1) 77 | 78 | maintype, subtype = mimetype.split('/', 1) 79 | with open(path) as fd: 80 | content = fd.read() 81 | 82 | if maintype == 'text': 83 | data = MIMEText(content, _subtype=subtype) 84 | elif maintype == 'image': 85 | data = MIMEImage(content, _subtype=subtype) 86 | elif maintype == 'audio': 87 | data = MIMEAudio(content, _subtype=subtype) 88 | else: 89 | data = MIMEBase(maintype, subtype) 90 | with open(path) as fd: 91 | data.set_payload(fd.read()) 92 | encoders.encode_base64(data) 93 | 94 | if args.merge: 95 | data.add_header('X-Merge-Type', 96 | args.merge) 97 | 98 | container.attach(data) 99 | 100 | with open(args.output, 'w') if args.output else sys.stdout as fd: 101 | fd.write(container.as_string()) 102 | 103 | if __name__ == '__main__': 104 | main() 105 | 106 | --------------------------------------------------------------------------------