vagrant-cloudcenter 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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