vagrant-cloudcenter 0.2.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2d4c66986cae8f7c4095b70094c408b8da09ad0b
4
+ data.tar.gz: 887a35f63bdd429521a72c9bdcd48815030e3e1b
5
+ SHA512:
6
+ metadata.gz: 88b0511d3d2cb3febb94e58d10c6034e1678e823c4358b02025d39f8ab050b8c6829f8f0bcccb99ecafad85d23f66c8ce74d1d412868a3f87f7c7753020013b5
7
+ data.tar.gz: ef1e21106b538189f6e81328f55aed0d0d48e4c09d624c534216b5ed917fc117e5a693077b140ed7b3fbc4c5de1147e7e1395a7445406eaad20f0f4ce7f87cb1
data/README.md ADDED
@@ -0,0 +1,143 @@
1
+ # Cisco CloudCenter Vagrant Plugin - Proof of Concept
2
+
3
+ This is a Vagrant plugin that adds a Cisco CloudCenter provider to Vagrant. It allows Vagrant to communicate with CloudCenter and have it control and provision machines in a number of public and private clouds.
4
+
5
+ This plugin is currently a Proof of Concept and has been developed and tested against Cisco CloudCenter 4.8.0 and Vagrant 1.2+
6
+
7
+ ![alt tag](https://github.com/conmurphy/vagrant-cloudcenter/blob/master/images/overview.png)
8
+
9
+ Table of Contents
10
+ =================
11
+
12
+ * [Cisco CloudCenter Vagrant Plugin - Proof of Concept](#cisco-cloudcenter-vagrant-plugin---proof-of-concept)
13
+ * [Table of Contents](#table-of-contents)
14
+ * [Features](#features)
15
+ * [Usage](#usage)
16
+ * [Vagrantfile structure](#vagrantfile-structure)
17
+ * [Installation](#installation)
18
+ * [Box Format](#box-format)
19
+ * [Configuration](#configuration)
20
+ * [Deployment Config](#deployment-config)
21
+ * [Synced Folders](#synced-folders)
22
+ * [Guidelines and Limitations](#guidelines-and-limitations)
23
+ * [Development](#development)
24
+
25
+ Created by [gh-md-toc](https://github.com/ekalinin/github-markdown-toc)
26
+
27
+ ## Features
28
+
29
+ * Boot instances through CloudCenter
30
+ * SSH into the instances
31
+ * Provision the instances with any built-in Vagrant provisioner
32
+ * Minimal synced folder support via `rsync`
33
+
34
+ ## Usage
35
+
36
+ After installing the plugin use the `vagrant up` command an specify the `cloudcenter` provider.
37
+
38
+ ```
39
+ $ vagrant up --provider=cloudcenter
40
+ ...
41
+ ```
42
+
43
+ The following additional plugin commands have been provided:
44
+
45
+ * `vagrant cloudcenter init` - Create a template Vagrantfile and populate with your own configuration
46
+ * `vagrant cloudcenter catalog` - Return a list of the current available catalog
47
+ * `vagrant cloudcenter jobs` - Return a list of service requests and their current status
48
+
49
+ ## Vagrantfile structure
50
+
51
+ You can either manually create a Vagrantfile that looks like the following, filling in
52
+ your information where necessary, or run the `vagrant cloudcenter init` command to have an empty Vagrantfile created for you.
53
+
54
+ ```
55
+ # -*- mode: ruby -*-
56
+ # vi: set ft=ruby :
57
+
58
+ Vagrant.configure(2) do |config|
59
+
60
+ config.vm.box = 'cloudcenter'
61
+
62
+ config.ssh.private_key_path = ['/Users/MYUSERNAME/.ssh/id_rsa','/Users/MYUSERNAME/.vagrant.d/insecure_private_key']
63
+ config.ssh.insert_key = false
64
+
65
+ config.vm.provider :cloudcenter do |cloudcenter|
66
+ cloudcenter.username = 'my_username'
67
+ cloudcenter.access_key = 'my_access_key'
68
+ cloudcenter.host_ip = 'cloudcenter_host_ip_address'
69
+ cloudcenter.deployment_config = 'sample_deployment_config.json'
70
+ end
71
+
72
+ config.vm.synced_folder '.', '/opt/my_files/', type: 'rsync'
73
+
74
+ end
75
+ ```
76
+
77
+ ## Installation
78
+
79
+ ## Box Format
80
+
81
+ The Vagrant CloudCenter plugin requires a box with configuration as outlined in this document.
82
+
83
+ [Vagrant Box Format]( https://www.vagrantup.com/docs/boxes/base.html )
84
+
85
+ * "vagrant" User
86
+ * Root Password: "vagrant"
87
+ * Password-less Sudo
88
+ * SSH Tweaks
89
+
90
+ ## Configuration
91
+
92
+ This provider exposes quite a few provider-specific configuration options:
93
+
94
+ * `access_key` - The access key for accessing the Cisco CloudCenter API
95
+ * `username` - The username for accessing the CloudCenter API
96
+ * `host_ip` - The host IP address of the CloudCenter Manager
97
+ * `deployment_config` - A JSON file used by CloudCenter to deploy the desired infrastructure
98
+
99
+ ## Deployment Config
100
+
101
+ This is a JSON file used by Cisco CloudCenter to deploy a new application into the environment of your choosing. It can be created by following these steps:
102
+
103
+ 1. Access the application from the CCM UI and click Applications
104
+ 2. Search for the required application in the Applications page
105
+ 3. Select `Deploy`
106
+
107
+ ![alt tag](https://github.com/conmurphy/vagrant-cloudcenter/blob/master/images/AppProfiles.png)
108
+
109
+ 4. Complete the required fields
110
+ 5. Select `Restful JSON`
111
+
112
+ ![alt tag](https://github.com/conmurphy/vagrant-cloudcenter/blob/master/images/AppDeployment.png)
113
+
114
+ 6. Save the JSON output into a new file on your local machine - if sharing a single file amongst multiple people or for multiple projects be sure to change the deployment job name so there is no overlap.
115
+ 7. Use this file in the `cloudcenter.deployment_config` setting
116
+
117
+ ## Synced Folders
118
+
119
+ There is minimal support for synced folders. Upon `vagrant up`,
120
+ `vagrant reload`, and `vagrant provision`, the CloudCenter provider will use
121
+ `rsync` (if available) to uni-directionally sync the folder to
122
+ the remote machine over SSH.
123
+
124
+ See [Vagrant Synced folders: rsync](https://docs.vagrantup.com/v2/synced-folders/rsync.html)
125
+
126
+ ## Guidelines and Limitations
127
+
128
+ * Currently tested with a single tier VM
129
+
130
+ ## Development
131
+
132
+ To work on the CloudCenter plugin, clone this repository then run the following commands to build and install the plugin.
133
+
134
+ ```
135
+ $ gem build vagrant-cloudcenter.gemspec
136
+ $ vagrant plugin install ./vagrant-cloudcenter-0.1.0.gem
137
+ ```
138
+
139
+ To uninstall the plugin run `vagrant plugin uninstall vagrant-cloudcenter`
140
+
141
+ WARNING:
142
+
143
+ These scripts are meant for educational/proof of concept purposes only. Any use of these scripts and tools is at your own risk. There is no guarantee that they have been through thorough testing in a comparable environment and we are not responsible for any damage or data loss incurred with their use.
@@ -0,0 +1,146 @@
1
+
2
+ require "log4r"
3
+ require 'rest-client';
4
+ require 'json';
5
+ require 'base64'
6
+
7
+ require 'vagrant/util/retryable'
8
+
9
+ require 'vagrant-cloudcenter/util/timer'
10
+
11
+ module VagrantPlugins
12
+ module Cloudcenter
13
+ module Action
14
+
15
+ class Deploy
16
+ def initialize(app, env)
17
+ @app = app
18
+ @logger = Log4r::Logger.new("cloudcenter::action::connect")
19
+ end
20
+
21
+ def call(env)
22
+
23
+ # Get the rest API key for authentication
24
+ access_key = env[:machine].provider_config.access_key
25
+ host_ip = env[:machine].provider_config.host_ip
26
+ username = env[:machine].provider_config.username
27
+
28
+ countdown = 24
29
+
30
+ #@logger.info("Deploying VM to Cloudcenter...")
31
+
32
+ begin
33
+
34
+ if !File.exists?(env[:machine].provider_config.deployment_config)
35
+ puts "\nMissing deployment_config file\n\n"
36
+ exit
37
+ end
38
+
39
+ deployment_config = File.read(env[:machine].provider_config.deployment_config)
40
+ tmp = JSON.parse(deployment_config)
41
+ env[:machine_name] = tmp["name"]
42
+
43
+ encoded = URI.encode("https://#{username}:#{access_key}@#{host_ip}/v2/jobs");
44
+
45
+ env[:cloudcenter_connect] = JSON.parse(RestClient::Request.execute(
46
+ :method => :post,
47
+ :url => encoded,
48
+ :verify_ssl => false,
49
+ :accept => "json",
50
+ :payload => deployment_config,
51
+ :headers => {"Content-Type" => "application/json"}
52
+ ));
53
+
54
+ response = env[:cloudcenter_connect]
55
+
56
+ jobID = response["id"]
57
+
58
+ encoded = URI.encode("https://#{username}:#{access_key}@#{host_ip}/v2/jobs/#{jobID}");
59
+
60
+ response = JSON.parse(RestClient::Request.execute(
61
+ :method => :get,
62
+ :url => encoded,
63
+ :verify_ssl => false,
64
+ :accept => "json"
65
+
66
+ ))
67
+
68
+ rescue => e
69
+
70
+ error = JSON.parse(e.response)
71
+ code = error["errors"][0]["code"]
72
+
73
+ puts "\n Error code: #{error['errors'][0]['code']}\n"
74
+ puts "\n #{error['errors'][0]['message']}\n\n"
75
+
76
+ exit
77
+
78
+ end
79
+
80
+ status = response["status"]
81
+
82
+ # Wait for SSH to be ready.
83
+ env[:ui].info(I18n.t("cloudcenter.waiting_for_ready"))
84
+
85
+ while countdown > 0
86
+
87
+ countdown -= 1
88
+
89
+ # When an instance comes up, it's networking may not be ready
90
+ # by the time we connect.
91
+ begin
92
+
93
+ jobID = response["id"]
94
+
95
+ encoded = URI.encode("https://#{username}:#{access_key}@#{host_ip}/v2/jobs/#{jobID}");
96
+
97
+ response = JSON.parse(RestClient::Request.execute(
98
+ :method => :get,
99
+ :url => encoded,
100
+ :verify_ssl => false,
101
+ :accept => "json"
102
+
103
+ ))
104
+
105
+ status = response["status"]
106
+
107
+
108
+ if status == "JobRunning" then
109
+ env[:machine_state_id]= :created
110
+ break
111
+ elsif status == "JobStarting" || status == "JobSubmitted" || status == "JobInProgress" || status == "JobResuming"
112
+ env[:ui].info(I18n.t("cloudcenter.waiting_for_ssh"))
113
+ elsif status == "JobError"
114
+ puts "\nError deploying VM...\n"
115
+ puts "\n#{response['jobStatusMessage']}\n\n"
116
+ exit
117
+ end
118
+
119
+ rescue => e
120
+ error = JSON.parse(e.response)
121
+ code = error["errors"][0]["code"]
122
+
123
+ puts "\n Error code: #{error['errors'][0]['code']}\n"
124
+ puts "\n #{error['errors'][0]['message']}\n\n"
125
+
126
+ exit
127
+ end
128
+
129
+ sleep 20
130
+ end
131
+
132
+ env[:machine_public_ip] = response["accessLink"][7,response.length]
133
+
134
+ # Ready and booted!
135
+ env[:ui].info(I18n.t("cloudcenter.ready"))
136
+
137
+
138
+ @app.call(env)
139
+
140
+
141
+
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,88 @@
1
+ module VagrantPlugins
2
+ module Cloudcenter
3
+ module Action
4
+ # This can be used with "Call" built-in to check if the machine
5
+ # is created and branch in the middleware.
6
+ class IsCreated
7
+ def initialize(app, env)
8
+ @app = app
9
+ end
10
+
11
+ def call(env)
12
+
13
+ if !File.exists?(env[:machine].provider_config.deployment_config)
14
+ puts "Missing deployment_config file"
15
+ exit
16
+ end
17
+
18
+ if !env[:machine_name]
19
+ deployment_config = JSON.parse(File.read(env[:machine].provider_config.deployment_config))
20
+ env[:machine_name] = deployment_config["name"]
21
+ end
22
+
23
+ access_key = env[:machine].provider_config.access_key
24
+ host_ip = env[:machine].provider_config.host_ip
25
+ username = env[:machine].provider_config.username
26
+
27
+ begin
28
+ encoded = URI.encode("https://#{username}:#{access_key}@#{host_ip}/v2/jobs?search=[deploymentEntity.name,fle,#{env[:machine_name]}]");
29
+
30
+ response = JSON.parse(RestClient::Request.execute(
31
+ :method => :get,
32
+ :url => encoded,
33
+ :verify_ssl => false,
34
+ :accept => "json",
35
+ :headers => {"Content-Type" => "application/json"}
36
+ ));
37
+
38
+ if !response["jobs"].empty?
39
+ jobID = response["jobs"][0]["id"]
40
+ end
41
+
42
+ rescue => e
43
+ error = JSON.parse(e.response)
44
+ code = error["errors"][0]["code"]
45
+
46
+ puts "\n Error code: #{error['errors'][0]['code']}\n"
47
+ puts "\n #{error['errors'][0]['message']}\n\n"
48
+
49
+ exit
50
+ end
51
+
52
+ if !jobID.nil?
53
+ begin
54
+ encoded = URI.encode("https://#{username}:#{access_key}@#{host_ip}/v2/jobs/#{jobID}");
55
+
56
+ response = JSON.parse(RestClient::Request.execute(
57
+ :method => :get,
58
+ :url => encoded,
59
+ :verify_ssl => false,
60
+ :accept => "json"
61
+ ));
62
+
63
+ rescue => e
64
+ error = JSON.parse(e.response)
65
+ code = error["errors"][0]["code"]
66
+
67
+ puts "\n Error code: #{error['errors'][0]['code']}\n"
68
+ puts "\n #{error['errors'][0]['message']}\n\n"
69
+
70
+ exit
71
+ end
72
+
73
+ env[:machine_public_ip] = response["accessLink"][7,response.length]
74
+ env[:machine_ssh_info] = { :host => env[:machine_public_ip], :port => 22, :username => "vagrant"}
75
+ env[:ssh_info] = { :host => env[:machine_public_ip], :port => 22, :username => "vagrant"}
76
+ env[:result] = :created
77
+
78
+ else
79
+ env[:result] = :not_created
80
+ end
81
+
82
+
83
+ @app.call(env)
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,97 @@
1
+ module VagrantPlugins
2
+ module Cloudcenter
3
+ module Action
4
+ # This can be used with "Call" built-in to check if the machine
5
+ # is stopped and branch in the middleware.
6
+ class IsStopped
7
+ def initialize(app, env)
8
+ @app = app
9
+ end
10
+
11
+ def call(env)
12
+
13
+ if !File.exists?(env[:machine].provider_config.deployment_config)
14
+ puts "Missing deployment_config file"
15
+ exit
16
+ end
17
+
18
+ if !env[:machine_name]
19
+ deployment_config = JSON.parse(File.read(env[:machine].provider_config.deployment_config))
20
+ env[:machine_name] = deployment_config["name"]
21
+ end
22
+
23
+ access_key = env[:machine].provider_config.access_key
24
+ host_ip = env[:machine].provider_config.host_ip
25
+ username = env[:machine].provider_config.username
26
+
27
+ begin
28
+ encoded = URI.encode("https://#{username}:#{access_key}@#{host_ip}/v2/jobs?search=[deploymentEntity.name,fle,#{env[:machine_name]}]");
29
+ response = JSON.parse(RestClient::Request.execute(
30
+ :method => :get,
31
+ :url => encoded,
32
+ :verify_ssl => false,
33
+ :accept => "json",
34
+ :headers => {"Content-Type" => "application/json"}
35
+ ));
36
+ if !response["jobs"].empty?
37
+ jobID = response["jobs"][0]["id"]
38
+ end
39
+
40
+ rescue => e
41
+ error = JSON.parse(e.response)
42
+ code = error["errors"][0]["code"]
43
+
44
+ if code == "DEPLOYMENT_STATUS_NOT_VALID_FOR_OPERATION"
45
+ puts "\n Error code: #{error['errors'][0]['code']}\n"
46
+ puts "\n #{error['errors'][0]['message']}\n\n"
47
+ exit
48
+ else
49
+ puts "\n Error code: #{error['errors'][0]['code']}\n"
50
+ puts "\n #{error['errors'][0]['message']}\n\n"
51
+ end
52
+
53
+ exit
54
+ end
55
+
56
+ if !jobID.nil?
57
+ begin
58
+ encoded = URI.encode("https://#{username}:#{access_key}@#{host_ip}/v2/jobs/#{jobID}");
59
+ response = JSON.parse(RestClient::Request.execute(
60
+ :method => :get,
61
+ :url => encoded,
62
+ :verify_ssl => false,
63
+ :accept => "json",
64
+ ));
65
+
66
+ rescue => e
67
+ error = JSON.parse(e.response)
68
+ code = error["errors"][0]["code"]
69
+
70
+ if code == "DEPLOYMENT_STATUS_NOT_VALID_FOR_OPERATION"
71
+ puts "\n Error code: #{error['errors'][0]['code']}\n"
72
+ puts "\n #{error['errors'][0]['message']}\n\n"
73
+ exit
74
+ else
75
+ puts "\n Error code: #{error['errors'][0]['code']}\n"
76
+ puts "\n #{error['errors'][0]['message']}\n\n"
77
+ end
78
+
79
+ exit
80
+ end
81
+
82
+ if response["deploymentEntity"]["attributes"]["health"] == "Healthy"
83
+ env[:result] = :running
84
+ else
85
+ env[:result] = :stopped
86
+ end
87
+
88
+ else
89
+ env[:result] = :stopped
90
+ end
91
+
92
+ @app.call(env)
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,90 @@
1
+ require "log4r"
2
+ require "json"
3
+
4
+ module VagrantPlugins
5
+ module Cloudcenter
6
+ module Action
7
+ # This action reads the SSH info for the machine and puts it into the
8
+ # `:machine_ssh_info` key in the environment.
9
+ class ReadSSHInfo
10
+ def initialize(app, env)
11
+ @app = app
12
+ end
13
+
14
+ def call(env)
15
+
16
+ if !File.exists?(env[:machine].provider_config.deployment_config)
17
+ puts "Missing deployment_config file"
18
+ exit
19
+ end
20
+
21
+ if !env[:machine_public_ip]
22
+
23
+ begin
24
+
25
+ if !env[:machine_name]
26
+ deployment_config = JSON.parse(File.read(env[:machine].provider_config.deployment_config))
27
+ env[:machine_name] = deployment_config["name"]
28
+ end
29
+
30
+ access_key = env[:machine].provider_config.access_key
31
+ host_ip = env[:machine].provider_config.host_ip
32
+ username = env[:machine].provider_config.username
33
+
34
+ encoded = URI.encode("https://#{username}:#{access_key}@#{host_ip}/v2/jobs?search=[deploymentEntity.name,fle,#{env[:machine_name]}]");
35
+
36
+ response = JSON.parse(RestClient::Request.execute(
37
+ :method => :get,
38
+ :url => encoded,
39
+ :verify_ssl => false,
40
+ :accept => "json",
41
+ :headers => {"Content-Type" => "application/json"}
42
+ ));
43
+
44
+ jobID = response["jobs"][0]["id"]
45
+
46
+ rescue => e
47
+ error = JSON.parse(e.response)
48
+ code = error["errors"][0]["code"]
49
+
50
+ puts "\n Error code: #{error['errors'][0]['code']}\n"
51
+ puts "\n #{error['errors'][0]['message']}\n\n"
52
+
53
+ exit
54
+ end
55
+
56
+ begin
57
+ encoded = URI.encode("https://#{username}:#{access_key}@#{host_ip}/v2/jobs/#{jobID}");
58
+
59
+ response = JSON.parse(RestClient::Request.execute(
60
+ :method => :get,
61
+ :url => encoded,
62
+ :verify_ssl => false,
63
+ :accept => "json"
64
+ ));
65
+ rescue => e
66
+ error = JSON.parse(e.response)
67
+ code = error["errors"][0]["code"]
68
+
69
+ puts "\n Error code: #{error['errors'][0]['code']}\n"
70
+ puts "\n #{error['errors'][0]['message']}\n\n"
71
+
72
+ exit
73
+ end
74
+
75
+ env[:machine_public_ip] = response["accessLink"][7,response.length]
76
+
77
+ end
78
+
79
+
80
+ env[:machine_ssh_info] = { :host => env[:machine_public_ip], :port => 22, :username => "vagrant",:private_key_path => env[:machine].config.ssh.private_key_path}
81
+
82
+ env[:ssh_info] = { :host => env[:machine_public_ip], :port => 22, :username => "vagrant",:private_key_path => env[:machine].config.ssh.private_key_path}
83
+
84
+ @app.call(env)
85
+ end
86
+
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,28 @@
1
+ require "log4r"
2
+
3
+ module VagrantPlugins
4
+ module Cloudcenter
5
+ module Action
6
+ # This action reads the state of the machine and puts it in the
7
+ # `:machine_state_id` key in the environment.
8
+ class ReadState
9
+ def initialize(app, env)
10
+ @app = app
11
+ @logger = Log4r::Logger.new("cloudcenter::action::read_state")
12
+ end
13
+
14
+ def call(env)
15
+ env[:machine_state_id] = read_state( env[:machine])
16
+
17
+ @app.call(env)
18
+ end
19
+
20
+ def read_state(machine)
21
+ return :not_created if machine.id.nil?
22
+
23
+ return env[:machine_state_id]
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end