cloudstack-cli 1.0.0.rc3 → 1.0.0.rc4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +1 -7
- data/lib/cloudstack-cli/base.rb +1 -1
- data/lib/cloudstack-cli/commands/domain.rb +57 -0
- data/lib/cloudstack-cli/commands/infrastructure_stack.rb +9 -10
- data/lib/cloudstack-cli/commands/router.rb +70 -41
- data/lib/cloudstack-cli/commands/snapshot.rb +8 -6
- data/lib/cloudstack-cli/commands/virtual_machine.rb +26 -24
- data/lib/cloudstack-cli/helper.rb +47 -25
- data/lib/cloudstack-cli/version.rb +1 -1
- metadata +2 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: c1fddcccc9c2fe8010aeb200b88b033aa1d97d1e
         | 
| 4 | 
            +
              data.tar.gz: 6e7b9cc1af09643f543266cef7c96c1aec3b9cea
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 0fb2398fdda615a61506c7ab7eb1e5974538f4a9bf8dd160ff18263ef133aa82a46c797e629157bb40d5a900d0a92b94d698a98bb429389a5d67efc580bf7223
         | 
| 7 | 
            +
              data.tar.gz: 54ef72715d16d1042a0a5c78852c8f99fe8e678111d64eceb27cb4a06b49f98ffdd7452ca8896bb55392bbd30389596f4b7fea469eb829a8fa2b8f58072a234c
         | 
    
        data/Gemfile.lock
    CHANGED
    
    
    
        data/README.md
    CHANGED
    
    | @@ -149,14 +149,8 @@ Stop all virtual routers of project named Demo (you could filter by zone too): | |
| 149 149 | 
             
            $ cloudstack-cli router list --project Demo --status running --redundant-state BACKUP --command STOP
         | 
| 150 150 | 
             
            ````
         | 
| 151 151 |  | 
| 152 | 
            -
            **Hint:** You can watch the status of the command with watch.
         | 
| 153 | 
            -
             | 
| 154 | 
            -
            ```bash
         | 
| 155 | 
            -
            $ watch -n cloudstack-cli router list --project Demo
         | 
| 156 | 
            -
            ```
         | 
| 157 | 
            -
             | 
| 158 | 
            -
             | 
| 159 152 | 
             
            ## References
         | 
| 153 | 
            +
             | 
| 160 154 | 
             
            -  [Cloudstack API documentation](http://cloudstack.apache.org/docs/api/)
         | 
| 161 155 | 
             
            -  This tool was inspired by the Knife extension for Cloudstack: [knife-cloudstack](https://github.com/CloudStack-extras/knife-cloudstack)
         | 
| 162 156 |  | 
    
        data/lib/cloudstack-cli/base.rb
    CHANGED
    
    
| @@ -15,4 +15,61 @@ class Domain < CloudstackCli::Base | |
| 15 15 | 
             
                end
         | 
| 16 16 | 
             
              end
         | 
| 17 17 |  | 
| 18 | 
            +
              desc 'create', 'create domain'
         | 
| 19 | 
            +
              option :network_domain, desc: "Network domain for networks in the domain."
         | 
| 20 | 
            +
              option :parent_domain, desc: "Assigns new domain a parent domain by domain name of the parent. If no parent domain is specied, the ROOT domain is assumed."
         | 
| 21 | 
            +
              def create(name)
         | 
| 22 | 
            +
                create_domains([options.merge(name: name)])
         | 
| 23 | 
            +
              end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
              desc 'delete', 'delete domain'
         | 
| 26 | 
            +
              option :parent_domain, desc: "Parent domain by domain name of the parent. If no parent domain is specied, the ROOT domain is assumed."
         | 
| 27 | 
            +
              def delete(name)
         | 
| 28 | 
            +
                delete_domains([options.merge(name: name)])
         | 
| 29 | 
            +
              end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
              no_commands do
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                def create_domains(domains)
         | 
| 34 | 
            +
                  puts domains
         | 
| 35 | 
            +
                  domains.each do |domain|
         | 
| 36 | 
            +
                    say "Creating domain '#{domain['name']}'... "
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                    if dom = client.list_domains(name: domain["name"], listall: true).first
         | 
| 39 | 
            +
                      unless domain["parent_domain"] && dom['parentdomainname'] != domain["parent_domain"]
         | 
| 40 | 
            +
                        say "domain '#{domain["name"]}' already exists.", :yellow
         | 
| 41 | 
            +
                        next
         | 
| 42 | 
            +
                      end
         | 
| 43 | 
            +
                    end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                    if domain["parent_domain"]
         | 
| 46 | 
            +
                      parent = client.list_domains(name: domain["parent_domain"], listall: true).first
         | 
| 47 | 
            +
                      unless parent
         | 
| 48 | 
            +
                        say "parent domain '#{domain["parent_domain"]}' of domain '#{domain["name"]}' not found.", :yellow
         | 
| 49 | 
            +
                        next
         | 
| 50 | 
            +
                      end
         | 
| 51 | 
            +
                      domain['parentdomain_id'] = parent['id']
         | 
| 52 | 
            +
                    end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                    client.create_domain(domain) ? say("OK.", :green) : say("Failed.", :red)
         | 
| 55 | 
            +
                  end
         | 
| 56 | 
            +
                end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                def delete_domains(domains)
         | 
| 59 | 
            +
                  domains.each do |domain|
         | 
| 60 | 
            +
                    print "Deleting domain '#{domain['name']}'..."
         | 
| 61 | 
            +
                    if dom = client.list_domains(name: domain["name"], listall: true).first
         | 
| 62 | 
            +
                      if domain["parent_domain"] && dom['parentdomainname'] =! domain["parent_domain"]
         | 
| 63 | 
            +
                        say "domain '#{domain["name"]}' with same name found, but parent_domain '#{domain["parent_domain"]}' does not match.", :yellow
         | 
| 64 | 
            +
                        next
         | 
| 65 | 
            +
                      end
         | 
| 66 | 
            +
                      client.delete_domain(id: dom['id']) ? say(" OK.", :green) : say(" Failed.", :red)
         | 
| 67 | 
            +
                    else
         | 
| 68 | 
            +
                      say "domain '#{domain["name"]}' not found.", :yellow
         | 
| 69 | 
            +
                    end
         | 
| 70 | 
            +
                  end
         | 
| 71 | 
            +
                end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
              end
         | 
| 74 | 
            +
             | 
| 18 75 | 
             
            end
         | 
| @@ -2,26 +2,25 @@ class InfrastructureStack < CloudstackCli::Base | |
| 2 2 |  | 
| 3 3 | 
             
              desc "create STACKFILE", "create a infrastructure stack"
         | 
| 4 4 | 
             
              def create(stackfile)
         | 
| 5 | 
            -
                 | 
| 5 | 
            +
                stack = parse_file(stackfile)
         | 
| 6 6 | 
             
                say "Create '#{stack["name"]}' infrastructure stack...", :green
         | 
| 7 7 |  | 
| 8 | 
            -
                create_domains(stack['domains'])
         | 
| 8 | 
            +
                Domain.new.create_domains(stack['domains'])
         | 
| 9 9 |  | 
| 10 | 
            -
                say " | 
| 10 | 
            +
                say "Infrastructure stack '#{stack["name"]}' finished.", :green
         | 
| 11 11 | 
             
              end
         | 
| 12 12 |  | 
| 13 13 | 
             
              desc "destroy STACKFILE", "destroy a infrastructure stack"
         | 
| 14 14 | 
             
              def destroy(stackfile)
         | 
| 15 | 
            +
                stack = parse_file(stackfile)
         | 
| 16 | 
            +
                say "Destroy '#{stack["name"]}' infrastructure stack...", :green
         | 
| 15 17 |  | 
| 16 | 
            -
              end
         | 
| 17 18 |  | 
| 18 | 
            -
              no_commands do
         | 
| 19 | 
            -
                def create_domains(domains)
         | 
| 20 | 
            -
                  domains.each do |domain|
         | 
| 21 | 
            -
                    puts domain
         | 
| 22 | 
            -
                  end
         | 
| 23 | 
            -
                end
         | 
| 24 19 |  | 
| 20 | 
            +
                Domain.new.delete_domains(stack['domains'].reverse)
         | 
| 21 | 
            +
                say "Infrastructure stack '#{stack["name"]}' finished.", :green
         | 
| 25 22 | 
             
              end
         | 
| 26 23 |  | 
| 24 | 
            +
             | 
| 25 | 
            +
             | 
| 27 26 | 
             
            end
         | 
| @@ -4,7 +4,7 @@ class Router < CloudstackCli::Base | |
| 4 4 | 
             
              option :project, desc: "name of the project"
         | 
| 5 5 | 
             
              option :account, desc: "name of the account"
         | 
| 6 6 | 
             
              option :zone, desc: "name of the zone"
         | 
| 7 | 
            -
              option : | 
| 7 | 
            +
              option :state, desc: "the status of the router"
         | 
| 8 8 | 
             
              option :redundant_state, desc: "the state of redundant virtual router",
         | 
| 9 9 | 
             
                enum: %w(master backup fault unknown)
         | 
| 10 10 | 
             
              option :listall, type: :boolean, desc: "list all routers", default: true
         | 
| @@ -15,6 +15,8 @@ class Router < CloudstackCli::Base | |
| 15 15 | 
             
                enum: %w(START STOP REBOOT)
         | 
| 16 16 | 
             
              option :concurrency, type: :numeric, default: 10, aliases: '-C',
         | 
| 17 17 | 
             
                desc: "number of concurrent command to execute"
         | 
| 18 | 
            +
              option :format, default: "table",
         | 
| 19 | 
            +
                enum: %w(table json yaml)
         | 
| 18 20 | 
             
              def list
         | 
| 19 21 | 
             
                resolve_project
         | 
| 20 22 | 
             
                resolve_zone
         | 
| @@ -23,8 +25,7 @@ class Router < CloudstackCli::Base | |
| 23 25 | 
             
                routers = client.list_routers(options)
         | 
| 24 26 |  | 
| 25 27 | 
             
                if options[:listall]
         | 
| 26 | 
            -
                   | 
| 27 | 
            -
                  projects.each do |project|
         | 
| 28 | 
            +
                  client.list_projects(listall: true).each do |project|
         | 
| 28 29 | 
             
                    routers = routers + client.list_routers(
         | 
| 29 30 | 
             
                      options.merge(projectid: project['id'])
         | 
| 30 31 | 
             
                    )
         | 
| @@ -37,22 +38,24 @@ class Router < CloudstackCli::Base | |
| 37 38 |  | 
| 38 39 | 
             
                routers.reverse! if options[:reverse]
         | 
| 39 40 | 
             
                print_routers(routers, options)
         | 
| 41 | 
            +
                execute_router_commands(options[:command].downcase, routers) if options[:command]
         | 
| 42 | 
            +
              end
         | 
| 40 43 |  | 
| 41 | 
            -
             | 
| 42 | 
            -
             | 
| 43 | 
            -
             | 
| 44 | 
            -
             | 
| 45 | 
            -
             | 
| 46 | 
            -
             | 
| 47 | 
            -
             | 
| 48 | 
            -
             | 
| 49 | 
            -
             | 
| 50 | 
            -
             | 
| 51 | 
            -
             | 
| 52 | 
            -
             | 
| 53 | 
            -
             | 
| 54 | 
            -
             | 
| 55 | 
            -
                 | 
| 44 | 
            +
              desc "list_from_file FILE", "list virtual routers from file"
         | 
| 45 | 
            +
              option :reverse, type: :boolean, default: false, desc: "reverse listing of routers"
         | 
| 46 | 
            +
              option :command,
         | 
| 47 | 
            +
                desc: "command to execute for each router",
         | 
| 48 | 
            +
                enum: %w(START STOP REBOOT)
         | 
| 49 | 
            +
              option :concurrency, type: :numeric, default: 10, aliases: '-C',
         | 
| 50 | 
            +
                desc: "number of concurrent command to execute"
         | 
| 51 | 
            +
              option :format, default: "table",
         | 
| 52 | 
            +
                enum: %w(table json yaml)
         | 
| 53 | 
            +
              def list_from_file(file)
         | 
| 54 | 
            +
                routers = parse_file(file)["routers"]
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                routers.reverse! if options[:reverse]
         | 
| 57 | 
            +
                print_routers(routers, options)
         | 
| 58 | 
            +
                execute_router_commands(options[:command].downcase, routers) if options[:command]
         | 
| 56 59 | 
             
              end
         | 
| 57 60 |  | 
| 58 61 | 
             
              desc "stop NAME [NAME2 ..]", "stop virtual router(s)"
         | 
| @@ -144,32 +147,58 @@ class Router < CloudstackCli::Base | |
| 144 147 | 
             
                  if routers.size < 1
         | 
| 145 148 | 
             
                    say "No routers found."
         | 
| 146 149 | 
             
                  else
         | 
| 147 | 
            -
                     | 
| 148 | 
            -
             | 
| 149 | 
            -
             | 
| 150 | 
            -
                     | 
| 151 | 
            -
             | 
| 152 | 
            -
             | 
| 153 | 
            -
             | 
| 154 | 
            -
                         | 
| 155 | 
            -
             | 
| 156 | 
            -
             | 
| 157 | 
            -
             | 
| 158 | 
            -
                         | 
| 159 | 
            -
             | 
| 160 | 
            -
             | 
| 161 | 
            -
             | 
| 162 | 
            -
             | 
| 163 | 
            -
             | 
| 164 | 
            -
             | 
| 165 | 
            -
             | 
| 166 | 
            -
             | 
| 150 | 
            +
                    case options[:format].to_sym
         | 
| 151 | 
            +
                    when :yaml
         | 
| 152 | 
            +
                      puts({'routers' => routers}.to_yaml)
         | 
| 153 | 
            +
                    when :json
         | 
| 154 | 
            +
                      say JSON.pretty_generate(routers: routers)
         | 
| 155 | 
            +
                    else
         | 
| 156 | 
            +
                      table = [%w(
         | 
| 157 | 
            +
                        ID Name Zone Account Project Redundant-State IP Linklocal-IP Status Redundant Offering
         | 
| 158 | 
            +
                      )]
         | 
| 159 | 
            +
                      table[0].delete('ID') unless options[:showid]
         | 
| 160 | 
            +
                      routers.each do |router|
         | 
| 161 | 
            +
                        table << [
         | 
| 162 | 
            +
                          router["id"],
         | 
| 163 | 
            +
                          router["name"],
         | 
| 164 | 
            +
                          router["zonename"],
         | 
| 165 | 
            +
                          router["account"],
         | 
| 166 | 
            +
                          router["project"],
         | 
| 167 | 
            +
                          router["redundantstate"],
         | 
| 168 | 
            +
                          router["nic"] && router["nic"].first ? router["nic"].first['ipaddress'] : "",
         | 
| 169 | 
            +
                          router["linklocalip"],
         | 
| 170 | 
            +
                          router["state"],
         | 
| 171 | 
            +
                          router["isredundantrouter"],
         | 
| 172 | 
            +
                          router["serviceofferingname"]
         | 
| 173 | 
            +
                        ]
         | 
| 174 | 
            +
                        table[-1].delete_at(0) unless table[0].index "ID"
         | 
| 175 | 
            +
                      end
         | 
| 176 | 
            +
                      print_table table
         | 
| 177 | 
            +
                      puts
         | 
| 178 | 
            +
                      say "Total number of routers: #{routers.size}"
         | 
| 167 179 | 
             
                    end
         | 
| 168 | 
            -
                    print_table table
         | 
| 169 | 
            -
                    puts
         | 
| 170 | 
            -
                    say "Total number of routers: #{routers.size}"
         | 
| 171 180 | 
             
                  end
         | 
| 172 181 | 
             
                end
         | 
| 182 | 
            +
             | 
| 183 | 
            +
                def execute_router_commands(command, routers)
         | 
| 184 | 
            +
                  unless %w(start stop reboot).include?(command)
         | 
| 185 | 
            +
                    say "\nCommand #{options[:command]} not supported.", :red
         | 
| 186 | 
            +
                    exit 1
         | 
| 187 | 
            +
                  end
         | 
| 188 | 
            +
                  exit unless yes?("\n#{command.capitalize} the router(s) above? [y/N]:", :magenta)
         | 
| 189 | 
            +
             | 
| 190 | 
            +
                  jobs = routers.map do |router|
         | 
| 191 | 
            +
                    {
         | 
| 192 | 
            +
                      job_id: nil,
         | 
| 193 | 
            +
                      object_id: router["id"],
         | 
| 194 | 
            +
                      name: "#{command.capitalize} router #{router['name']}",
         | 
| 195 | 
            +
                      status: -1
         | 
| 196 | 
            +
                    }
         | 
| 197 | 
            +
                  end
         | 
| 198 | 
            +
             | 
| 199 | 
            +
                  run_background_jobs(jobs, "#{command}_router")
         | 
| 200 | 
            +
                end
         | 
| 201 | 
            +
             | 
| 173 202 | 
             
              end
         | 
| 174 203 |  | 
| 175 204 | 
             
            end
         | 
| @@ -1,10 +1,11 @@ | |
| 1 1 | 
             
            class Snapshot < CloudstackCli::Base
         | 
| 2 2 |  | 
| 3 3 | 
             
              desc 'list', 'list snapshots'
         | 
| 4 | 
            -
              option :account
         | 
| 5 | 
            -
              option :project
         | 
| 6 | 
            -
              option :domain
         | 
| 7 | 
            -
              option :listall, default: true
         | 
| 4 | 
            +
              option :account, desc: "the account associated with the snapshot"
         | 
| 5 | 
            +
              option :project, desc: "the project associated with the snapshot"
         | 
| 6 | 
            +
              option :domain, desc: "the domain name of the snapshot's account"
         | 
| 7 | 
            +
              option :listall, default: true, desc: "list all resources the caller has rights on"
         | 
| 8 | 
            +
              option :state, desc: "filter snapshots by state"
         | 
| 8 9 | 
             
              def list
         | 
| 9 10 | 
             
                resolve_account
         | 
| 10 11 | 
             
                resolve_project
         | 
| @@ -13,11 +14,12 @@ class Snapshot < CloudstackCli::Base | |
| 13 14 | 
             
                if snapshots.size < 1
         | 
| 14 15 | 
             
                  say "No snapshots found."
         | 
| 15 16 | 
             
                else
         | 
| 16 | 
            -
                  table = [ | 
| 17 | 
            +
                  table = [%w(Account Name Volume Created Type State)]
         | 
| 18 | 
            +
                  snapshots = filter_by(snapshots, :state, options[:state]) if options[:state]
         | 
| 17 19 | 
             
                  snapshots.each do |snapshot|
         | 
| 18 20 | 
             
                    table << [
         | 
| 19 21 | 
             
                    	snapshot['account'], snapshot['name'], snapshot['volumename'],
         | 
| 20 | 
            -
                    	snapshot['created'], snapshot['snapshottype']
         | 
| 22 | 
            +
                    	snapshot['created'], snapshot['snapshottype'], snapshot['state']
         | 
| 21 23 | 
             
                    ]
         | 
| 22 24 | 
             
                  end
         | 
| 23 25 | 
             
                  print_table table
         | 
| @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            require 'thread'
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            class VirtualMachine < CloudstackCli::Base
         | 
| 2 4 |  | 
| 3 5 | 
             
              desc "list", "list virtual machines"
         | 
| @@ -43,7 +45,10 @@ class VirtualMachine < CloudstackCli::Base | |
| 43 45 | 
             
                  puts "No virtual machines found."
         | 
| 44 46 | 
             
                else
         | 
| 45 47 | 
             
                  print_virtual_machines(virtual_machines)
         | 
| 46 | 
            -
                  execute_virtual_machines_commands( | 
| 48 | 
            +
                  execute_virtual_machines_commands(
         | 
| 49 | 
            +
                    options[:command].downcase,
         | 
| 50 | 
            +
                    virtual_machines
         | 
| 51 | 
            +
                  ) if options[:command]
         | 
| 47 52 | 
             
                end
         | 
| 48 53 | 
             
              end
         | 
| 49 54 |  | 
| @@ -84,7 +89,7 @@ class VirtualMachine < CloudstackCli::Base | |
| 84 89 | 
             
                say "Start deploying virtual machine#{ "s" if names.size > 1 }...", :green
         | 
| 85 90 | 
             
                jobs = names.map do |name|
         | 
| 86 91 | 
             
                  if virtual_machine = client.list_virtual_machines(name: name, project_id: options[:project_id]).first
         | 
| 87 | 
            -
                    say " | 
| 92 | 
            +
                    say "virtual machine #{name} (#{virtual_machine["state"]}) already exists.", :yellow
         | 
| 88 93 | 
             
                    job = {
         | 
| 89 94 | 
             
                      id: 0,
         | 
| 90 95 | 
             
                      name: "Create virtual machine #{name}",
         | 
| @@ -93,7 +98,7 @@ class VirtualMachine < CloudstackCli::Base | |
| 93 98 | 
             
                  else
         | 
| 94 99 | 
             
                    job = {
         | 
| 95 100 | 
             
                      id: client.deploy_virtual_machine(options, {sync: true})['jobid'],
         | 
| 96 | 
            -
                      name: "Create  | 
| 101 | 
            +
                      name: "Create virtual machine #{name}"
         | 
| 97 102 | 
             
                    }
         | 
| 98 103 | 
             
                  end
         | 
| 99 104 | 
             
                  job
         | 
| @@ -116,17 +121,17 @@ class VirtualMachine < CloudstackCli::Base | |
| 116 121 | 
             
                say "Finished.", :green
         | 
| 117 122 | 
             
              end
         | 
| 118 123 |  | 
| 119 | 
            -
              desc "destroy NAME [NAME2 ..]", "destroy  | 
| 124 | 
            +
              desc "destroy NAME [NAME2 ..]", "destroy virtual machine(s)"
         | 
| 120 125 | 
             
              option :project
         | 
| 121 126 | 
             
              option :force, desc: "destroy without asking", type: :boolean, aliases: '-f'
         | 
| 122 | 
            -
              option :expunge, desc: "expunge  | 
| 127 | 
            +
              option :expunge, desc: "expunge virtual machine immediately", type: :boolean, default: false, aliases: '-E'
         | 
| 123 128 | 
             
              def destroy(*names)
         | 
| 124 129 | 
             
                resolve_project
         | 
| 125 130 | 
             
                names.each do |name|
         | 
| 126 131 | 
             
                  unless virtual_machine = client.list_virtual_machines(options.merge(name: name, listall: true)).first
         | 
| 127 132 | 
             
                    say "Virtual machine #{name} not found.", :red
         | 
| 128 133 | 
             
                  else
         | 
| 129 | 
            -
                    ask = "Destroy #{name} (#{ | 
| 134 | 
            +
                    ask = "Destroy #{name} (#{virtual machine['state']})? [y/N]:"
         | 
| 130 135 | 
             
                    if options[:force] || yes?(ask, :yellow)
         | 
| 131 136 | 
             
                      say "destroying #{name} "
         | 
| 132 137 | 
             
                      client.destroy_virtual_machine(
         | 
| @@ -139,12 +144,12 @@ class VirtualMachine < CloudstackCli::Base | |
| 139 144 | 
             
                end
         | 
| 140 145 | 
             
              end
         | 
| 141 146 |  | 
| 142 | 
            -
              desc "create_interactive", "interactive creation of a  | 
| 147 | 
            +
              desc "create_interactive", "interactive creation of a virtual machine with network access"
         | 
| 143 148 | 
             
              def create_interactive
         | 
| 144 149 | 
             
                bootstrap_server_interactive
         | 
| 145 150 | 
             
              end
         | 
| 146 151 |  | 
| 147 | 
            -
              desc "stop NAME", "stop a  | 
| 152 | 
            +
              desc "stop NAME", "stop a virtual machine"
         | 
| 148 153 | 
             
              option :project
         | 
| 149 154 | 
             
              option :force
         | 
| 150 155 | 
             
              def stop(name)
         | 
| @@ -170,12 +175,12 @@ class VirtualMachine < CloudstackCli::Base | |
| 170 175 | 
             
                  say "Virtual machine #{name} not found.", :red
         | 
| 171 176 | 
             
                  exit 1
         | 
| 172 177 | 
             
                end
         | 
| 173 | 
            -
                say("Starting  | 
| 178 | 
            +
                say("Starting virtual machine #{name}", :magenta)
         | 
| 174 179 | 
             
                client.start_virtual_machine(id: virtual_machine['id'])
         | 
| 175 180 | 
             
                puts
         | 
| 176 181 | 
             
              end
         | 
| 177 182 |  | 
| 178 | 
            -
              desc "reboot NAME", "reboot a  | 
| 183 | 
            +
              desc "reboot NAME", "reboot a virtual machine"
         | 
| 179 184 | 
             
              option :project
         | 
| 180 185 | 
             
              option :force
         | 
| 181 186 | 
             
              def reboot(name)
         | 
| @@ -212,7 +217,7 @@ class VirtualMachine < CloudstackCli::Base | |
| 212 217 | 
             
                      ]
         | 
| 213 218 | 
             
                    end
         | 
| 214 219 | 
             
                    print_table table
         | 
| 215 | 
            -
                    say "Total number of  | 
| 220 | 
            +
                    say "Total number of virtual machines: #{virtual_machines.count}"
         | 
| 216 221 | 
             
                  end
         | 
| 217 222 | 
             
                end
         | 
| 218 223 |  | 
| @@ -222,20 +227,17 @@ class VirtualMachine < CloudstackCli::Base | |
| 222 227 | 
             
                    exit 1
         | 
| 223 228 | 
             
                  end
         | 
| 224 229 | 
             
                  exit unless yes?("\n#{command.capitalize} the virtual_machine(s) above? [y/N]:", :magenta)
         | 
| 225 | 
            -
             | 
| 226 | 
            -
             | 
| 227 | 
            -
             | 
| 228 | 
            -
             | 
| 229 | 
            -
             | 
| 230 | 
            -
             | 
| 231 | 
            -
             | 
| 232 | 
            -
             | 
| 233 | 
            -
                        name: "#{command.capitalize} virtual_machine #{virtual_machine['name']}"
         | 
| 234 | 
            -
                      }
         | 
| 235 | 
            -
                    end
         | 
| 236 | 
            -
                    puts
         | 
| 237 | 
            -
                    watch_jobs(jobs)
         | 
| 230 | 
            +
             | 
| 231 | 
            +
                  jobs = virtual_machines.map do |vm|
         | 
| 232 | 
            +
                    {
         | 
| 233 | 
            +
                      job_id: nil,
         | 
| 234 | 
            +
                      object_id: vm["id"],
         | 
| 235 | 
            +
                      name: "#{command.capitalize} virtual machine #{vm['name']}",
         | 
| 236 | 
            +
                      status: -1
         | 
| 237 | 
            +
                    }
         | 
| 238 238 | 
             
                  end
         | 
| 239 | 
            +
             | 
| 240 | 
            +
                  run_background_jobs(jobs, "#{command}_virtual_machine")
         | 
| 239 241 | 
             
                end
         | 
| 240 242 |  | 
| 241 243 | 
             
              end # no_commands
         | 
| @@ -12,44 +12,66 @@ module CloudstackCli | |
| 12 12 | 
             
                end
         | 
| 13 13 |  | 
| 14 14 | 
             
                ASYNC_STATES = {
         | 
| 15 | 
            -
                   | 
| 16 | 
            -
                   | 
| 17 | 
            -
                   | 
| 15 | 
            +
                  -1 => "waiting",
         | 
| 16 | 
            +
                  0  => "running",
         | 
| 17 | 
            +
                  1  => "completed",
         | 
| 18 | 
            +
                  2  => "error"
         | 
| 18 19 | 
             
                }
         | 
| 19 20 |  | 
| 20 | 
            -
                def  | 
| 21 | 
            -
                   | 
| 22 | 
            -
             | 
| 23 | 
            -
             | 
| 24 | 
            -
             | 
| 25 | 
            -
             | 
| 26 | 
            -
                     | 
| 27 | 
            -
                       | 
| 28 | 
            -
             | 
| 21 | 
            +
                def run_background_jobs(jobs, command)
         | 
| 22 | 
            +
                  view_thread = Thread.new do
         | 
| 23 | 
            +
                    chars = %w(| / - \\)
         | 
| 24 | 
            +
                    call = 0
         | 
| 25 | 
            +
                    opts = {t_start: Time.now}
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                    while jobs.select{|job| job[:status] < 1 }.size > 0 do
         | 
| 28 | 
            +
                      if call.modulo(40) == 0
         | 
| 29 | 
            +
                        t = Thread.new { jobs = update_jobs(jobs, command) }
         | 
| 30 | 
            +
                        while t.alive?
         | 
| 31 | 
            +
                          chars = print_job_status(jobs, chars,
         | 
| 32 | 
            +
                            call == 0 ? opts.merge(no_clear: true) : opts
         | 
| 33 | 
            +
                          )
         | 
| 34 | 
            +
                          call += 1
         | 
| 35 | 
            +
                        end
         | 
| 36 | 
            +
                        t.join
         | 
| 37 | 
            +
                      else
         | 
| 29 38 | 
             
                        chars = print_job_status(jobs, chars,
         | 
| 30 39 | 
             
                          call == 0 ? opts.merge(no_clear: true) : opts
         | 
| 31 40 | 
             
                        )
         | 
| 32 41 | 
             
                        call += 1
         | 
| 33 42 | 
             
                      end
         | 
| 34 | 
            -
                      t.join
         | 
| 35 | 
            -
                    else
         | 
| 36 | 
            -
                      chars = print_job_status(jobs, chars,
         | 
| 37 | 
            -
                        call == 0 ? opts.merge(no_clear: true) : opts
         | 
| 38 | 
            -
                      )
         | 
| 39 | 
            -
                      call += 1
         | 
| 40 43 | 
             
                    end
         | 
| 44 | 
            +
                    print_job_status(jobs, chars,
         | 
| 45 | 
            +
                      call == 0 ? opts.merge(no_clear: true) : opts
         | 
| 46 | 
            +
                    )
         | 
| 41 47 | 
             
                  end
         | 
| 42 | 
            -
                   | 
| 43 | 
            -
                    call == 0 ? opts.merge(no_clear: true) : opts
         | 
| 44 | 
            -
                  )
         | 
| 48 | 
            +
                  view_thread.join
         | 
| 45 49 | 
             
                end
         | 
| 46 50 |  | 
| 47 | 
            -
                def  | 
| 48 | 
            -
                   | 
| 49 | 
            -
             | 
| 50 | 
            -
             | 
| 51 | 
            +
                def update_jobs(jobs, command)
         | 
| 52 | 
            +
                  # update running job status
         | 
| 53 | 
            +
                  threads = jobs.select{|job| job[:status] == 0 }.map do |job|
         | 
| 54 | 
            +
                    Thread.new do
         | 
| 55 | 
            +
                      job[:status] = client.query_async_job_result(job_id: job[:job_id])['jobstatus']
         | 
| 56 | 
            +
                    end
         | 
| 57 | 
            +
                  end
         | 
| 58 | 
            +
                  threads.each(&:join)
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                  # launch new jobs if required and possible
         | 
| 61 | 
            +
                  launch_capacity = options[:concurrency] - jobs.select{|job| job[:status] == 0 }.count
         | 
| 62 | 
            +
                  threads = []
         | 
| 63 | 
            +
                  jobs.select{|job| job[:status] == -1 }.each do |job|
         | 
| 64 | 
            +
                    if launch_capacity > 0
         | 
| 65 | 
            +
                      threads << Thread.new do
         | 
| 66 | 
            +
                        job[:job_id] = client.send(
         | 
| 67 | 
            +
                          command, { id: job[:object_id] }, { sync: true }
         | 
| 68 | 
            +
                        )['jobid']
         | 
| 69 | 
            +
                        job[:status] = 0
         | 
| 70 | 
            +
                      end
         | 
| 71 | 
            +
                      launch_capacity -= 1
         | 
| 51 72 | 
             
                    end
         | 
| 52 73 | 
             
                  end
         | 
| 74 | 
            +
                  threads.each(&:join)
         | 
| 53 75 | 
             
                  jobs
         | 
| 54 76 | 
             
                end
         | 
| 55 77 |  | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: cloudstack-cli
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 1.0.0. | 
| 4 | 
            +
              version: 1.0.0.rc4
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Nik Wolfgramm
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2015- | 
| 11 | 
            +
            date: 2015-06-18 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: rdoc
         |