|
| 1 | +# |
| 2 | +# Author:: Doug MacEachern <dougm@vmware.com> |
| 3 | +# Copyright:: Copyright (c) 2010 VMware, Inc. |
| 4 | +# License:: Apache License, Version 2.0 |
| 5 | +# |
| 6 | +# Licensed under the Apache License, Version 2.0 (the "License"); |
| 7 | +# you may not use this file except in compliance with the License. |
| 8 | +# You may obtain a copy of the License at |
| 9 | +# |
| 10 | +# http://www.apache.org/licenses/LICENSE-2.0 |
| 11 | +# |
| 12 | +# Unless required by applicable law or agreed to in writing, software |
| 13 | +# distributed under the License is distributed on an "AS IS" BASIS, |
| 14 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 15 | +# See the License for the specific language governing permissions and |
| 16 | +# limitations under the License. |
| 17 | +# |
| 18 | + |
| 19 | +require 'fileutils' |
| 20 | +require 'chef/knife/bootstrap.rb' |
| 21 | + |
| 22 | +class Chef |
| 23 | + class Knife |
| 24 | + class WindowsBootstrap < Chef::Knife::Bootstrap |
| 25 | + |
| 26 | + banner "knife windows bootstrap FQDN [RUN LIST...] (options)" |
| 27 | + |
| 28 | + option :user, |
| 29 | + :short => "-x USERNAME", |
| 30 | + :long => "--user USERNAME", |
| 31 | + :description => "The Windows username" |
| 32 | + |
| 33 | + option :password, |
| 34 | + :short => "-P PASSWORD", |
| 35 | + :long => "--password PASSWORD", |
| 36 | + :description => "The Windows password" |
| 37 | + |
| 38 | + option :chef_node_name, |
| 39 | + :short => "-N NAME", |
| 40 | + :long => "--node-name NAME", |
| 41 | + :description => "The Chef node name for your new node" |
| 42 | + |
| 43 | + option :distro, |
| 44 | + :short => "-d DISTRO", |
| 45 | + :long => "--distro DISTRO", |
| 46 | + :description => "Bootstrap a distro using a template", |
| 47 | + :default => "windows-gems" |
| 48 | + |
| 49 | + option :template_file, |
| 50 | + :long => "--template-file TEMPLATE", |
| 51 | + :description => "Full path to location of template to use", |
| 52 | + :default => false |
| 53 | + |
| 54 | + option :run_list, |
| 55 | + :short => "-r RUN_LIST", |
| 56 | + :long => "--run-list RUN_LIST", |
| 57 | + :description => "Comma separated list of roles/recipes to apply", |
| 58 | + :proc => lambda { |o| o.split(",") }, |
| 59 | + :default => [] |
| 60 | + |
| 61 | + def is_mounted |
| 62 | + @net_use ||= Chef::Util::Windows::NetUse.new(@admin_share) |
| 63 | + begin |
| 64 | + @net_use.get_info |
| 65 | + return true |
| 66 | + rescue |
| 67 | + return false |
| 68 | + end |
| 69 | + end |
| 70 | + |
| 71 | + def mount_admin_share |
| 72 | + if @add_mount && !is_mounted |
| 73 | + use = { |
| 74 | + :remote => @admin_share, :local => '', |
| 75 | + :username => config[:user], :password => config[:password] |
| 76 | + } |
| 77 | + @net_use.add(use) |
| 78 | + if is_mounted |
| 79 | + Chef::Log.info("Mounted #{@admin_share} for copying files") |
| 80 | + else |
| 81 | + Chef::Log.fatal("Failed to mount #{@admin_share}") |
| 82 | + exit 1 |
| 83 | + end |
| 84 | + end |
| 85 | + end |
| 86 | + |
| 87 | + def unmount_admin_share |
| 88 | + if @add_mount && is_mounted |
| 89 | + Chef::Log.debug("Unmounting #{@admin_share}") |
| 90 | + @net_use.delete |
| 91 | + end |
| 92 | + end |
| 93 | + |
| 94 | + def psexec(args) |
| 95 | + cmd = ['psexec', @unc_path, "-w", 'c:\chef'] |
| 96 | + if config[:user] |
| 97 | + cmd << "-u" << config[:user] |
| 98 | + end |
| 99 | + if config[:password] |
| 100 | + cmd << "-p" << config[:password] |
| 101 | + end |
| 102 | + cmd << args |
| 103 | + cmd = cmd.join(' ') |
| 104 | + Chef::Log.debug("system #{cmd}") |
| 105 | + system(cmd) |
| 106 | + end |
| 107 | + |
| 108 | + def run |
| 109 | + require 'chef/util/windows/net_use' |
| 110 | + |
| 111 | + if @name_args.first == nil |
| 112 | + Chef::Log.error("Must pass a node name/ip to windows bootstrap") |
| 113 | + exit 1 |
| 114 | + end |
| 115 | + |
| 116 | + config[:server_name] = @name_args.first |
| 117 | + if Chef::Config[:http_proxy] |
| 118 | + uri = URI.parse(Chef::Config[:http_proxy]) |
| 119 | + config[:proxy] = "#{uri.host}:#{uri.port}" |
| 120 | + end |
| 121 | + |
| 122 | + @unc_path = "\\\\#{config[:server_name]}" |
| 123 | + @admin_share = "#{@unc_path}\\c$" |
| 124 | + path = "#{@admin_share}\\chef" |
| 125 | + |
| 126 | + $stdout.sync = true |
| 127 | + |
| 128 | + command = render_template(load_template(config[:bootstrap_template])) |
| 129 | + |
| 130 | + Chef::Log.info("Bootstrapping Chef on #{config[:server_name]}") |
| 131 | + |
| 132 | + @add_mount = config[:user] != nil && !is_mounted |
| 133 | + mount_admin_share |
| 134 | + |
| 135 | + begin |
| 136 | + unless File.exists?(path) |
| 137 | + Chef::Log.debug("mkdir #{path}") |
| 138 | + FileUtils.mkdir(path) |
| 139 | + end |
| 140 | + File.open("#{path}\\bootstrap.bat", 'w') {|f| f.write(command) } |
| 141 | + FileUtils.cp(File.join(File.dirname(__FILE__), 'bootstrap', 'client-install.vbs'), path) |
| 142 | + FileUtils.cp(Chef::Config[:validation_key], path) |
| 143 | + psexec("cmd.exe /c bootstrap.bat") |
| 144 | + ensure |
| 145 | + unmount_admin_share |
| 146 | + end |
| 147 | + end |
| 148 | + end |
| 149 | + end |
| 150 | +end |
0 commit comments