summaryrefslogtreecommitdiffstats
path: root/genome-bootstrap/bin/genome-bootstrap
blob: b364cbf7e145eb8f8b6d24acb64e20f74bd73e5d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
#!/usr/bin/env ruby
require 'ostruct'
require 'genome-bootstrap'
require 'main'
require 'net/http'
require 'uri'
require "highline/import"
#http://www.mail-archive.com/capistrano@googlegroups.com/msg01822.html
HighLine.track_eof = false

include GenomeBootstrap

Main {

  def handle_exception e
    puts e.message
  end unless $DEBUG

  def say_red(text)
    say("<%= color(\"#{text}\", :red) %>") 
  end

  # It's a little silly to have this much indirection.  Smells like things need
  # to be refactored a bit. 
  def get_system_ip(repo, system_name)
    sleep_time = 5
    tries = 20
    
    say("Trying to determine the ip address.")
    say("We'll try and maximum of #{tries} times.")
    repo.get_system_ip(system_name, tries) do
      # It's really silly that we have to poll for the ip like this.  
      say("Polling for cobbler status...")
      say("Retrying in #{sleep_time} seconds")
      sleep sleep_time
    end
  end

  def get_input
    @facts = Hash.new

    say_red("\nYour genome repo server is the system that serves as the")
    say_red("puppet master, git server, and cobbler server all in one.")
    @repo = ask("Enter your Genome repo fully qualified domain name (fqdn):  ")
    
    # Default the "repoappliance" parameter to the given repo value
    @facts["repoappliance"] = @repo

    say_red("\nYour cloud master is the server that determines where the")
    say_red("machine you're trying to create will be provisioned.  Typically")
    say_red("cloud masters control several machines in a cloud.  However,")
    say_red("cloud masters can be configured to simply manage their own disk space.")
    @cloudmaster = ask("Enter your cloud master fqdn:  ")
    @email = ask("Enter your email address for cloud notifications:  ")

    # Allows genomed to know which cloudmaster is controlling this new machine
    @facts["cloudmaster"] = @cloudmaster

    say_red("\nYour system prefix is added to your machine name to help identify")
    say_red("it as your own.  It must be a single word and should be short (e.g. username).")
    @system_prefix = ask("Enter your system prefix:  ")

    # Once we know the repo we can fetch the supported machines
    @genome_repo = GenomeRepo.new(@repo)
    say_red("\nNow, you can select your machine type.")
    say_red("To see a description of these machine types visit: " +
        "#{@genome_repo.machine_types_url}")

    @machine_type = choose do |menu|
      menu.prompt = "Select your machine type:  "
      menu.choices(*@genome_repo.machines.map{|m| m.name})
    end

    # Nice to keep around with the machine's specification
    @facts["genome_machine_type"] = @machine_type
    @system_name = "#{@system_prefix}-#{@machine_type}"
    @facts["genome_system_name"] = @system_name

    say_red("\nYour cobbler profile determines the OS, the disk space, and")
    say_red("the amount of memory that should be allocated to the system")
    say_red("that you are provisioning.")
    @facts["cobbler_profile"] = choose do |menu|
      menu.prompt = "Select your cobbler profile (This determines the OS):  "
      menu.choices(*@genome_repo.bootstrapping_cobbler_profiles)
    end

    facts = @genome_repo.facts_for(@machine_type)
    if not facts.empty? 
      say_red("\nNow you need to enter some parameters what will be used to configure this machine.")
      say_red("Most of the defaults will work out fine for development.")

      facts.each do |f|
        say_red("Description: #{f.desc}") unless f.desc.empty?
        @facts[f.name] = ask("Enter value for #{f.name}: ") do |q|
          q.default = f.default
        end
      end
    end
  end

  def _run(config_only=false)
    # Ask all the silly questions
    get_input
    
    classes = @genome_repo.classes_for(@machine_type)
    config = {"classes" => classes, "parameters" => @facts}

    say("\nYour system is now being provisioned")

    @genome_repo.register_machine(@system_name, config)
    unless config_only
      cloud_master = CloudController.new(@cloudmaster, @genome_repo)
      id, host  = cloud_master.create_machine(@system_name, @email)
     
      if ip = get_system_ip(@genome_repo, @system_name)
        cloud_master.update_hostname(id, ip) 
        say("Your new system is being provisioned on #{host}.")
        say("The IP address of the new system is #{ip}.")
      else
        say("Your new system is being provisioned on #{host}.")
        say("You can visit #{@cloudmaster.cloud} to see the status of the new system.")
      end
    end
  end

  def run
    _run
  end

  # Allow user to simply create the cobbler system
  # but not actually create the VM
  mode 'config-only' do
    def run
      _run(true)
    end
  end

  # Allow user to supply a yaml file with the machine
  # configuration instead of answering all the questions
  mode 'advanced' do
    option('fqdn', 'f'){
      description "Fully qualified domain name of machine to be provisioned"
      argument_required
    }

    option('system', 's'){
      description "Cobbler system name of the machine to be provisioned"
      argument_required
    }

    option('repo', '-r'){
      required
      description "Fully qualified domain name for the Genome repo machine to use for provisioning"
      argument_required
    }

    option('cloudmaster', 'c'){
      required
      description "Fully qualified domain name for the machine controlling the cloud"
      argument_required
    }
    
    option('email', 'e'){
      required
      description "Your email address to use to help identify the instance owner"
      argument_required
    }

    option('yaml', 'y'){
      description "YAML configuration for this machine"
      argument_required
      validate {|f| File.exist?(f)}
    }

    def run 
      unless params['fqdn'].given? || params['system'].given?
        say("You must provide either an 'fqdn' parameter or a 'system' parameter")
        exit(1)
      end

      # FQDN beats system parameter
      if params['fqdn'].given?
        name = params['fqdn'].value
      else
        name = params['system'].value
      end

      config = if params['yaml'].given? 
        YAML.load(File.read(params['yaml'].value))
      else
        YAML.load($stdin.read)
      end

      genome_repo = GenomeRepo.new(params['repo'].value)
      genome_repo.register_machine(name, config)
      cloud_master = CloudController.new(params['cloudmaster'].value, genome_repo)
      host = cloud_master.create_machine(name, params['email'].value)

      if ip = get_system_ip(genome_repo, name)
        say("Your new system is being provisioned on #{host}.")
        say("The IP address of the new system is #{ip}.")
      else
        say("Your new system is being provisioned on #{host}.")
        say("You can visit #{cloud_master.cloud} to see the status of the new system.")
      end
    end
  end
}