initial commit
This commit is contained in:
commit
02bf8b1bba
22
LICENSE
Normal file
22
LICENSE
Normal file
@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 GGA Software Services LLC
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
48
README.md
Normal file
48
README.md
Normal file
@ -0,0 +1,48 @@
|
||||
# Devops services#
|
||||
|
||||
Copyright (c) 2009-2014 GGA Software Services LLC
|
||||
|
||||
Authors: Anton Martynov, Mikhail Mirolyubov, Alexey Lukashin
|
||||
|
||||
##Introduction##
|
||||
|
||||
This software was developed for supporting development and operations activities. This service allows managing servers and deployments in hybrid computing environment such as combination of Amazon EC2, VPC, and OpenStack clouds as well as bare metal servers. Deployment is performed by using Opscode Chef Server. The general idea is to put all software dependencies and deployment scripts into chef recipes and apply these procedures to the server.
|
||||
|
||||
##Devops-service installation##
|
||||
|
||||
Devops service is a REST web service which incapsulates all apllication logic.
|
||||
|
||||
Setup server:
|
||||
yum install ruby
|
||||
yum install ruby-devel
|
||||
yum install libxml2-devel
|
||||
yum install libxslt-devel
|
||||
yum install gcc make
|
||||
yum install wget
|
||||
wget http://production.cf.rubygems.org/rubygems/rubygems-1.8.24.tgz
|
||||
tar xvf rubygems-1.8.24.tgz
|
||||
cd rubygems-1.8.24
|
||||
ruby setup.rb
|
||||
gem install knife-openstack -sinatra thin --no-ri --no-rdoc
|
||||
|
||||
Run server:
|
||||
ruby -rubygems devops-service.rb
|
||||
|
||||
The deep configuration of Devops Service is performed by Chef cookbook.
|
||||
|
||||
##Devops-client installation##
|
||||
|
||||
Devops client is a ruby gem, which provides CLI application for interaction with Devops Service.
|
||||
|
||||
Dependencies:
|
||||
gems:
|
||||
httpclient >= 2.3
|
||||
json
|
||||
terminal-table
|
||||
|
||||
gem install devops-client.gem
|
||||
|
||||
|
||||
## License
|
||||
|
||||
Devops-service software is released under the [MIT License](http://www.opensource.org/licenses/MIT).
|
||||
17
devops-client/.gitignore
vendored
Normal file
17
devops-client/.gitignore
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
*.gem
|
||||
*.rbc
|
||||
.bundle
|
||||
.config
|
||||
.yardoc
|
||||
Gemfile.lock
|
||||
InstalledFiles
|
||||
_yardoc
|
||||
coverage
|
||||
doc/
|
||||
lib/bundler/man
|
||||
pkg
|
||||
rdoc
|
||||
spec/reports
|
||||
test/tmp
|
||||
test/version_tmp
|
||||
tmp
|
||||
22
devops-client/LICENSE.txt
Normal file
22
devops-client/LICENSE.txt
Normal file
@ -0,0 +1,22 @@
|
||||
Copyright (c) 2013 amartynov
|
||||
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
987
devops-client/README.md
Normal file
987
devops-client/README.md
Normal file
@ -0,0 +1,987 @@
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="author" content="Anton Martynov">
|
||||
<meta name="author" content="Mike Miroliubov">
|
||||
<meta name="author" content="Alexey Lukashin">
|
||||
<title>Devops client</title>
|
||||
</head>
|
||||
|
||||
<style>
|
||||
h1 {
|
||||
text-align: center;
|
||||
}
|
||||
h2 {
|
||||
border-bottom: 1px solid black;
|
||||
}
|
||||
h3 {
|
||||
border-bottom: 1px solid #c6c6c6;
|
||||
}
|
||||
table {
|
||||
border-collapse:collapse;
|
||||
}
|
||||
table, th, td {
|
||||
border: 1px solid #cccccc;
|
||||
}
|
||||
th, td {
|
||||
padding: 2px 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
Devops client
|
||||
=============
|
||||
|
||||
Devops client is a ruby gem.
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Installation](#install)
|
||||
* [First run](#first_run)
|
||||
* [Client commands](#commands)
|
||||
* [Templates](#templates)
|
||||
* [Deploy](#deploy)
|
||||
* [Filters](#filters)
|
||||
* [Flavor](#flavor)
|
||||
* [Group](#group)
|
||||
* [Image](#image)
|
||||
* [Key](#key)
|
||||
* [Network](#network)
|
||||
* [Project](#project)
|
||||
* [Provider](#provider)
|
||||
* [Script](#script)
|
||||
* [Server](#server)
|
||||
* [Tag](#tag)
|
||||
* [User](#user)
|
||||
* [HOWTO](#howto)
|
||||
* [Create user](#howto_user)
|
||||
* [Create image](#howto_image)
|
||||
* [Create project](#howto_project)
|
||||
* [Launch new server](#howto_server)
|
||||
|
||||
<h2 id="install">Installation</h2>
|
||||
|
||||
Devops client requirements:
|
||||
|
||||
* ruby v1.9.3 or higher
|
||||
|
||||
Client can be installed by following command
|
||||
|
||||
$ sudo gem install devops-client.gem --no-ri --no-rdoc
|
||||
|
||||
After gem installation new command will be available in your system
|
||||
|
||||
$ devops
|
||||
|
||||
If command wasn't found then necessary to check ruby environment
|
||||
|
||||
$ gem environment
|
||||
|
||||
And add "EXECUTABLE DIRECTORY" into $PATH
|
||||
|
||||
Devops shows help if invoked without parameters:
|
||||
|
||||
$ devops
|
||||
|
||||
Usage: /usr/bin/devops command [options]
|
||||
|
||||
Commands:
|
||||
Bootsrap templates:
|
||||
templates list
|
||||
|
||||
Deploy:
|
||||
deploy NODE_NAME [NODE_NAME ...]
|
||||
|
||||
Filters:
|
||||
filter image add ec2|openstack IMAGE [IMAGE ...]
|
||||
filter image delete ec2|openstack IMAGE [IMAGE ...]
|
||||
filter image list ec2|openstack
|
||||
|
||||
Flavor:
|
||||
flavor list PROVIDER
|
||||
|
||||
Group:
|
||||
group list PROVIDER
|
||||
|
||||
Image:
|
||||
image create
|
||||
image delete IMAGE
|
||||
image list [provider] [ec2|openstack]
|
||||
image show IMAGE
|
||||
image update IMAGE FILE
|
||||
|
||||
Key:
|
||||
key add KEY_NAME FILE
|
||||
key delete KEY_NAME
|
||||
key list
|
||||
|
||||
Network:
|
||||
network list PROVIDER
|
||||
|
||||
Project:
|
||||
project create PROJECT_ID
|
||||
project delete PROJECT_ID [DEPLOY_ENV]
|
||||
project deploy PROJECT_ID [DEPLOY_ENV]
|
||||
project list
|
||||
project multi create PROJECT_ID
|
||||
project servers PROJECT_ID [DEPLOY_ENV]
|
||||
project set run_list PROJECT_ID DEPLOY_ENV [(recipe[mycookbook::myrecipe])|(role[myrole]) ...]
|
||||
project show PROJECT_ID
|
||||
project update PROJECT_ID FILE
|
||||
project user add PROJECT_ID USER_NAME [USER_NAME ...]
|
||||
project user delete PROJECT_ID USER_NAME [USER_NAME ...]
|
||||
|
||||
Provider:
|
||||
provider list
|
||||
|
||||
Script:
|
||||
script list
|
||||
script add SCRIPT_NAME FILE
|
||||
script delete SCRIPT_NAME
|
||||
script run SCRIPT_NAME NODE_NAME [NODE_NAME ... ]
|
||||
script command NODE_NAME 'sh command'
|
||||
|
||||
Server:
|
||||
server add PROJECT_ID DEPLOY_ENV IP SSH_USER KEY_ID
|
||||
server bootstrap INSTANCE_ID
|
||||
server create PROJECT_ID DEPLOY_ENV
|
||||
server delete NODE_NAME [NODE_NAME ...]
|
||||
server list [chef|ec2|openstack]
|
||||
server pause NODE_NAME
|
||||
server show NODE_NAME
|
||||
server unpause NODE_NAME
|
||||
|
||||
Tag:
|
||||
tag create NODE_NAME TAG_NAME [TAG_NAME ...]
|
||||
tag delete NODE_NAME TAG_NAME [TAG_NAME ...]
|
||||
tag list NODE_NAME
|
||||
|
||||
User:
|
||||
user create USER_NAME
|
||||
user delete USER_NAME
|
||||
user grant USER_NAME [COMMAND] [PRIVILEGES]
|
||||
user list
|
||||
user password USER_NAME
|
||||
|
||||
Detailed help for each command can be shown by passing --help to command line.
|
||||
|
||||
<h2 id="first_run">First run</h2>
|
||||
|
||||
During first, run devops will detect that its configuration file is absent and will show warning and ask for required parameters:
|
||||
First step is to enter server's host and port:
|
||||
|
||||
WARN: File '~/.devops/devops-client.conf' does not exist
|
||||
Language: ru
|
||||
Devops service host: <host>:7070
|
||||
Default API version (v2.0):
|
||||
Username: my_user
|
||||
Password: my_password
|
||||
Configuration file '~/.devops/devops-client.conf' is created
|
||||
|
||||
Also necessary to enter API version (current is v2.0) and credentials.
|
||||
After these questions configuration file will be created.
|
||||
|
||||
<h2 id="commands">Commands</h2>
|
||||
|
||||
After running some commands, devops client might show information in JSON format and ask for confirmation. User can approve or decline operation.
|
||||
|
||||
Any command has additional options:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Option</th>
|
||||
<th>Desciption</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>-h, --help</td>
|
||||
<td>Show help</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>-c, --config FILE</td>
|
||||
<td>Specify devops client config file (/home/my_user/.devops/devops-client.conf)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>-v, --version</td>
|
||||
<td>devops client version</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--host HOST</td>
|
||||
<td>devops service host address (devops-server-host:devops-server-port)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--api VER</td>
|
||||
<td>devops service API version (v2.0)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--user USERNAME</td>
|
||||
<td>use USERNAME for authentication</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--format FORMAT</td>
|
||||
<td>Output format: 'table', 'json' (table)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--completion</td>
|
||||
<td>Initialize bash completion script</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h3 id="templates">Templates</h3>
|
||||
|
||||
$ devops templates
|
||||
|
||||
Usage: /usr/bin/devops command [options]
|
||||
|
||||
Commands:
|
||||
Bootsrap templates:
|
||||
templates list
|
||||
|
||||
**devops templates list** - command will list available templates for bootstrapping virtual machines by Chef
|
||||
|
||||
<h3 id="deploy">Deploy</h3>
|
||||
|
||||
Command performs deployment operation by running Chef client on remote server
|
||||
|
||||
$ devops deploy
|
||||
|
||||
Usage: /usr/bin/devops command [options]
|
||||
|
||||
Commands:
|
||||
Deploy:
|
||||
deploy NODE_NAME [NODE_NAME ...]
|
||||
|
||||
**devops deploy** - deploys everything on server
|
||||
|
||||
Options:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Option</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--tag TAG1,TAG2...</td>
|
||||
<td>Chef tag names, comma separated list of a tags which will be temporary applied to servers.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h3 id="filters">Filters</h3>
|
||||
|
||||
Filters allows to specify cloud VM images and restrict devops to use only them. It is helpful in case of EC2 which has hungreds of images.
|
||||
|
||||
$ devops filter
|
||||
|
||||
Usage: /usr/bin/devops command [options]
|
||||
|
||||
Commands:
|
||||
Filters:
|
||||
filter image add ec2|openstack IMAGE [IMAGE ...]
|
||||
filter image delete ec2|openstack IMAGE [IMAGE ...]
|
||||
filter image list ec2|openstack
|
||||
|
||||
**devops filter image add** - adds image id to filters
|
||||
**devops filter image delete** - removes image id (ids) from filters
|
||||
**devops filter image list** - shows list of available images
|
||||
|
||||
<h3 id="flavor">Flavor</h3>
|
||||
|
||||
$ devops flavor
|
||||
|
||||
Usage: /usr/bin/devops command [options]
|
||||
|
||||
Commands:
|
||||
Flavor:
|
||||
flavor list PROVIDER
|
||||
|
||||
**devops flavor list** - lists available virtual machine configurations
|
||||
|
||||
<h3 id="group">Group</h3>
|
||||
|
||||
$ devops group
|
||||
|
||||
Usage: /usr/bin/devops command [options]
|
||||
|
||||
Commands:
|
||||
Group:
|
||||
group list PROVIDER
|
||||
|
||||
**devops group list** - displays list of security groups
|
||||
|
||||
<h3 id="image">Image</h3>
|
||||
|
||||
Command allows managing virtual machine images.
|
||||
|
||||
$ devops image
|
||||
|
||||
Usage: /usr/bin/devops command [options]
|
||||
|
||||
Commands:
|
||||
Image:
|
||||
image create
|
||||
image delete IMAGE
|
||||
image list [provider] [ec2|openstack]
|
||||
image show IMAGE
|
||||
image update IMAGE FILE
|
||||
|
||||
**devops image create** - creates image. Client will ask several questions:
|
||||
|
||||
Provider: # select cloud provider (e.g., openstack, ec2)
|
||||
Choose image: # enter image number from a list
|
||||
The ssh username: # give ssh username for logging in
|
||||
Bootstrap template (optional): # select bootstrap template
|
||||
|
||||
Options:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Option</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--provider PROVIDER</td>
|
||||
<td>Image provider</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--image IMAGE_ID</td>
|
||||
<td>Image identifier</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--ssh_user USER</td>
|
||||
<td>SSH user name</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--bootstrap_template TEMPLATE</td>
|
||||
<td>Bootstrap template</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--no_bootstrap_template</td>
|
||||
<td>Do not specify bootstrap template</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
**devops delete** - delete image by ID
|
||||
|
||||
**devops image list** - list available images
|
||||
|
||||
**devops image list provider ec2|openstack** - list available cloud images (filtered by devops)
|
||||
|
||||
**devops image list ec2|openstack** - list available images
|
||||
|
||||
**devops image show** - show image information
|
||||
|
||||
**devops image update** - update image from provided JSON file
|
||||
|
||||
<h3 id="key">Key</h3>
|
||||
|
||||
Manage keys (SSH certificates) servers.
|
||||
|
||||
Key:
|
||||
key add KEY_NAME FILE
|
||||
key delete KEY_NAME
|
||||
key list
|
||||
|
||||
**devops key add** - adds new key with given name KEY_NAME from file FILE
|
||||
|
||||
**devops key delete** - remove key with name KEY_NAME
|
||||
|
||||
**devops key list** - lists available keys
|
||||
|
||||
There is at least one system key which cannot be deleted by user. System keys are registered during devops server configuration and not manageable by user)
|
||||
|
||||
<h3 id="network">Network</h3>
|
||||
|
||||
$ devops network
|
||||
|
||||
Usage: /usr/bin/devops command [options]
|
||||
|
||||
Commands:
|
||||
Network:
|
||||
network list PROVIDER
|
||||
|
||||
**devops network list PROVIDER** - list available cloud networks for given PROVIDER
|
||||
|
||||
<h3 id="project">Project</h3>
|
||||
|
||||
Command allows to manage projects
|
||||
|
||||
$ devops project
|
||||
|
||||
Usage: /usr/bin/devops command [options]
|
||||
|
||||
Commands:
|
||||
Project:
|
||||
project create PROJECT_ID
|
||||
project delete PROJECT_ID [DEPLOY_ENV]
|
||||
project deploy PROJECT_ID [DEPLOY_ENV]
|
||||
project list
|
||||
project servers PROJECT_ID [DEPLOY_ENV]
|
||||
project set run_list PROJECT_ID DEPLOY_ENV [(recipe[mycookbook::myrecipe])|(role[myrole]) ...]
|
||||
project show PROJECT_ID
|
||||
project update PROJECT_ID FILE
|
||||
project user add PROJECT_ID USER_NAME [USER_NAME ...]
|
||||
project user delete PROJECT_ID USER_NAME [USER_NAME ...]
|
||||
|
||||
**devops project create** - create a new project
|
||||
|
||||
Client will ask several questions:
|
||||
Deploy environment identifier: # which environment will be created (dev, test, my_env...) At least one environment required for project.
|
||||
Provider: # Cloud provider (openstack, amazon ec2)
|
||||
Security groups (comma separated), like 1,2,3, or empty for 'default': # List of security groups which will be assigned to new VMs in given environment/
|
||||
Users, you will be added automatically (comma separated), like 1,2,3, or empty: # list of users
|
||||
Flavor: # server configuration
|
||||
Image: # image for virtual machine
|
||||
Subnets (comma separated), like 1,2,3, or empty: # cloud subnets (openstack or Amazon VPC requires at least one)
|
||||
Run list (comma separated), like recipe[mycookbook::myrecipe], role[myrole]: role[test_dev], # roles and cookbooks which will be assigned to virtual machines
|
||||
Enter expires time if necessary (5m, 3h, 2d, 1w, etc): # virtual machine life time (by default forever)
|
||||
|
||||
*If project already exists then new environment will be added to it*
|
||||
|
||||
Options:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Option</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--groups GROUP_1,GROUP_2...</td>
|
||||
<td>Security groups (comma separated list)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--deploy_env DEPLOY_ID</td>
|
||||
<td>Deploy enviroment identifier</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--subnets SUBNET,SUBNET...</td>
|
||||
<td>Subnets identifier for deploy enviroment (ec2 - only one sybnet, openstack - comma separated list)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--flavor FLAVOR</td>
|
||||
<td>Specify flavor for the project</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--image IMAGE_ID</td>
|
||||
<td>Specify image identifier for the project</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--run_list RUN_LIST</td>
|
||||
<td>Run list (comma separated), like recipe[mycookbook::myrecipe], role[myrole]:</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--users USER,USER...</td>
|
||||
<td>Users for deploy environment control</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--provider PROVIDER</td>
|
||||
<td>Provider identifier 'ec2' or 'openstack'</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--no_expires</td>
|
||||
<td>Without expires time</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--expires EXPIRES</td>
|
||||
<td>Expires time (5m, 3h, 2d, 1w, etc)</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
**devops project delete** - removes project or its environment
|
||||
|
||||
**devops project deploy** - deploys to all servers in a project or in given environment
|
||||
|
||||
Options:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Option</th>
|
||||
<th>Desciption</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--servers SERVERS</td>
|
||||
<td>Servers list (comma separated)</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
**devops project list** - list all available projects
|
||||
|
||||
**devops project servers** - list all running servers in a project
|
||||
|
||||
**devops project set run_list** - update run-list for a project's environment
|
||||
|
||||
**devops project show** - display project info
|
||||
|
||||
**devops project update** - update project from JSON file
|
||||
|
||||
**devops project user delete** - add user to project
|
||||
|
||||
Options:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Option</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--deploy_env ENV</td>
|
||||
<td>Add user to deploy enviroment</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
**devops project user delete** - remove user(s) from a project
|
||||
|
||||
Options:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Option</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--deploy_env ENV</td>
|
||||
<td>Add user to deploy enviroment</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h3 id="provider">Provider</h3>
|
||||
|
||||
$ devops provider
|
||||
|
||||
Usage: /usr/bin/devops command [options]
|
||||
|
||||
Commands:
|
||||
Provider:
|
||||
provider list
|
||||
|
||||
**devops provider list** - Lists available cloud providers registered on devops server
|
||||
|
||||
<h3 id="script">Script</h3>
|
||||
|
||||
Manages shell scrips for running on servers
|
||||
|
||||
$ devops script
|
||||
|
||||
Usage: /usr/bin/devops command [options]
|
||||
|
||||
Commands:
|
||||
Script:
|
||||
script list
|
||||
script add SCRIPT_NAME FILE
|
||||
script delete SCRIPT_NAME
|
||||
script run SCRIPT_NAME NODE_NAME [NODE_NAME ...]
|
||||
script command NODE_NAME 'sh command'
|
||||
|
||||
**devops script list** - lists available scripts
|
||||
|
||||
**devops script add** - adds new script with name SCRIPT_NAME from file FILE
|
||||
|
||||
**devops script delete** - removes script SCRIPT_NAME
|
||||
|
||||
**devops script run** - runs script with name SCRIPT_NAME on server with node name (on Chef server) NODE_NAME
|
||||
|
||||
Options:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Option</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--params PARAMS</td>
|
||||
<td>Comma separated scipt parameters</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
**devops script command** - run shell command on remote server (bash interpreter is used)
|
||||
|
||||
<h3 id="server">Server</h3>
|
||||
|
||||
$ devops server
|
||||
|
||||
Usage: /usr/bin/devops command [options]
|
||||
|
||||
Commands:
|
||||
Server:
|
||||
server add PROJECT_ID DEPLOY_ENV IP SSH_USER KEY_ID
|
||||
server bootstrap INSTANCE_ID
|
||||
server create PROJECT_ID DEPLOY_ENV
|
||||
server delete NODE_NAME [NODE_NAME ...]
|
||||
server list [chef|ec2|openstack]
|
||||
server pause NODE_NAME
|
||||
server show NODE_NAME
|
||||
server unpause NODE_NAME
|
||||
|
||||
**devops server add** - adds new server (bare metal, existing,...) to a project with name PROJECT_ID
|
||||
|
||||
**devops server bootstrap** - bootstraps chef on server and runs Chef client with project run list
|
||||
|
||||
Options:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Option</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>-N, --name NAME</td>
|
||||
<td>Set chef name</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--bootstrap_template [TEMPLATE]</td>
|
||||
<td>Bootstrap template (optional)</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
**devops server create** - launches new server in a cloud with project PROJECT_ID and environment DEPLOY_ENV
|
||||
|
||||
Options:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Option</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>-N, --name NAME</td>
|
||||
<td>Set chef name</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
**devops server delete** - terminates server
|
||||
|
||||
Options:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Option</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--instance</td>
|
||||
<td>Delete node by instance id</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
**devops server list** - list servers
|
||||
|
||||
**devops server pause** - put server on pause (only if cloud provider supports it)
|
||||
|
||||
**devops server show** - show detailed information
|
||||
|
||||
**server unpause** - unpause server
|
||||
|
||||
<h3 id="tag">Tag</h3>
|
||||
|
||||
Manages tags on Chef servers. This functionality can be used for changing deploy behavior according to given tags.
|
||||
|
||||
$ devops tag
|
||||
|
||||
Usage: /usr/bin/devops command [options]
|
||||
|
||||
Commands:
|
||||
Tag:
|
||||
tag create NODE_NAME TAG_NAME [TAG_NAME ...]
|
||||
tag delete NODE_NAME TAG_NAME [TAG_NAME ...]
|
||||
tag list NODE_NAME
|
||||
|
||||
**devops tag create** - create new tag on chef node with name NODE_NAME
|
||||
|
||||
**devops tag delete** - removes tag from chef node with name NODE_NAME
|
||||
|
||||
**devops tag list** - lists all tags on a chef node with name NODE_NAME
|
||||
|
||||
<h3 id="user">User</h3>
|
||||
|
||||
User management
|
||||
|
||||
$ devops user
|
||||
|
||||
Usage: /usr/bin/devops command [options]
|
||||
|
||||
Commands:
|
||||
User:
|
||||
user create USER_NAME
|
||||
user delete USER_NAME
|
||||
user grant USER_NAME [COMMAND] [PRIVILEGES]
|
||||
user list
|
||||
user password USER_NAME
|
||||
|
||||
**devops user create** - create user with name USER_NAME
|
||||
|
||||
Options:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Option</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--password PASSWORD</td>
|
||||
<td>New user password</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
**devops user delete** - remove user with name USER_NAME
|
||||
|
||||
**devops user grant** - grants permissions for user
|
||||
|
||||
Available subcommands:
|
||||
|
||||
* all
|
||||
* flavor
|
||||
* group
|
||||
* image
|
||||
* project
|
||||
* server
|
||||
* key
|
||||
* user
|
||||
* filter
|
||||
* network
|
||||
* provider
|
||||
* script
|
||||
|
||||
Available privileges:
|
||||
|
||||
* r
|
||||
* w
|
||||
* rw
|
||||
|
||||
If privileges are not specified then user is not allowed to run command.
|
||||
|
||||
If command and privileges are not specified then user's permissions are set to default values.
|
||||
|
||||
**devops user list** - list all users
|
||||
|
||||
**devops user password** - change user's password
|
||||
|
||||
<h1 id="howto">Mini HOWTO</h1>
|
||||
|
||||
Mostly used scenarios described below.
|
||||
|
||||
|
||||
<h2 id="howto_user">User management</h2>
|
||||
|
||||
After clean install root user has empty password, lets set it:
|
||||
|
||||
$ devops user password root -u root
|
||||
Enter password for 'root':
|
||||
Updated
|
||||
|
||||
Let's create user test and grant some permissions for working with filters, images, projects and servers:
|
||||
|
||||
If system doesn't have users then let's use root user:
|
||||
|
||||
$ devops user create test -u root
|
||||
Password for root:
|
||||
Enter password for 'test':
|
||||
Created
|
||||
|
||||
By default user has read permissions for filter, image, project, and server operations. Lets give him write permissions:
|
||||
|
||||
$ devops user grant test filter rw -u root
|
||||
Password for root:
|
||||
Updated
|
||||
|
||||
$ devops user grant test image rw -u root
|
||||
Password for root:
|
||||
Updated
|
||||
|
||||
$ devops user grant test project rw -u root
|
||||
Password for root:
|
||||
Updated
|
||||
|
||||
$ devops user grant test server rw -u root
|
||||
Password for root:
|
||||
Updated
|
||||
|
||||
$ devops user grant test user r -u root
|
||||
Password for root:
|
||||
Updated
|
||||
|
||||
|
||||
<h2 id="howto_image">Image management</h2>
|
||||
|
||||
First step is to add required images to filter. For OpenStack it is OpenStack image id, for EC2 it is AMI.
|
||||
|
||||
devops filter image add openstack 78665e7b-5123-4fa8-b39b-d7643ecd8ed7
|
||||
|
||||
Next step is to create image and specify required metadata:
|
||||
|
||||
$ devops image create
|
||||
+--------+-----------+
|
||||
| API version: v2.0 |
|
||||
| Provider |
|
||||
+--------+-----------+
|
||||
| Number | Provider |
|
||||
+--------+-----------+
|
||||
| 1 | ec2 |
|
||||
| 2 | openstack |
|
||||
+--------+-----------+
|
||||
Provider: 2
|
||||
+--------+---------------------------+--------------------------------------+--------+
|
||||
| API version: v2.0 |
|
||||
| Images |
|
||||
+--------+---------------------------+--------------------------------------+--------+
|
||||
| Number | Name | ID | Status |
|
||||
+--------+---------------------------+--------------------------------------+--------+
|
||||
| 1 | centos-6.4-amd64-20130707 | 78665e7b-5123-4fa8-b39b-d7643ecd8ed7 | ACTIVE |
|
||||
+--------+---------------------------+--------------------------------------+--------+
|
||||
Image: 1
|
||||
The ssh username: root
|
||||
Bootstrap template (optional):
|
||||
{
|
||||
"provider": "openstack",
|
||||
"name": "centos-6.4-amd64-20130707",
|
||||
"id": "78665e7b-5123-4fa8-b39b-d7643ecd8ed7",
|
||||
"remote_user": "root"
|
||||
}
|
||||
Create image? (y/n):
|
||||
|
||||
<h2 id="howto_project">Project management</h2>
|
||||
|
||||
Let's create new project 'my_project' with environment 'test'
|
||||
|
||||
$ devops project create my_project
|
||||
Deploy environment identifier: test
|
||||
+--------+-----------+
|
||||
| API version: v2.0 |
|
||||
| Provider |
|
||||
+--------+-----------+
|
||||
| Number | Provider |
|
||||
+--------+-----------+
|
||||
| 1 | ec2 |
|
||||
| 2 | openstack |
|
||||
+--------+-----------+
|
||||
Provider: 2
|
||||
|
||||
System will show security groups. We are selecting what is needed:
|
||||
|
||||
+--------+-------------------------------------+----------+------+-------+-----------+-----------------------------+
|
||||
| API version: v2.0 |
|
||||
| Groups |
|
||||
+--------+-------------------------------------+----------+------+-------+-----------+-----------------------------+
|
||||
| Number | Name | Protocol | From | To | CIDR | Description |
|
||||
+--------+-------------------------------------+----------+------+-------+-----------+-----------------------------+
|
||||
| 1 | default | udp | 1 | 65535 | 0.0.0.0/0 | default |
|
||||
| | | tcp | 1 | 65535 | 0.0.0.0/0 | |
|
||||
| | | icmp | -1 | -1 | 0.0.0.0/0 | |
|
||||
+--------+-------------------------------------+----------+------+-------+-----------+-----------------------------+
|
||||
| 2 | webports | tcp | 8080 | 8080 | 0.0.0.0/0 | web ports |
|
||||
| | | tcp | 80 | 80 | 0.0.0.0/0 | |
|
||||
| | | tcp | 8089 | 8089 | 0.0.0.0/0 | |
|
||||
| | | tcp | 8443 | 8443 | 0.0.0.0/0 | |
|
||||
| | | tcp | 443 | 443 | 0.0.0.0/0 | |
|
||||
+--------+-------------------------------------+----------+------+-------+-----------+-----------------------------+
|
||||
Security groups (comma separated), like 1,2,3, or empty for 'default':
|
||||
|
||||
Next step is to users which can work with a project:
|
||||
|
||||
+--------+------------------+-------+-----+---------+--------+------+--------+--------+--------+-------+---------+----------+
|
||||
| API version: v2.0 |
|
||||
| Users |
|
||||
+--------+------------------+-------+-----+---------+--------+------+--------+--------+--------+-------+---------+----------+
|
||||
| | | Privileges |
|
||||
+--------+------------------+-------+-----+---------+--------+------+--------+--------+--------+-------+---------+----------+
|
||||
| Number | User ID | Image | Key | Project | Server | User | Script | Filter | Flavor | Group | Network | Provider |
|
||||
+--------+------------------+-------+-----+---------+--------+------+--------+--------+--------+-------+---------+----------+
|
||||
| 1 | test | rw | r | rw | rw | r | r | rw | r | r | r | r |
|
||||
+--------+------------------+-------+-----+---------+--------+------+--------+--------+--------+-------+---------+----------+
|
||||
| 2 | root | rw | rw | rw | rw | rw | rw | rw | rw | rw | rw | rw |
|
||||
+--------+------------------+-------+-----+---------+--------+------+--------+--------+--------+-------+---------+----------+
|
||||
Users, you will be added automatically (comma separated), like 1,2,3, or empty:
|
||||
|
||||
Flavor for environment:
|
||||
|
||||
+--------+-----------+--------------+------+-------+
|
||||
| API version: v2.0 |
|
||||
| Flavors |
|
||||
+--------+-----------+--------------+------+-------+
|
||||
| Number | ID | Virtual CPUs | Disk | RAM |
|
||||
+--------+-----------+--------------+------+-------+
|
||||
| 1 | c1.large | 8 | 50 | 8192 |
|
||||
| 2 | c1.medium | 2 | 50 | 2048 |
|
||||
| 3 | c1.small | 2 | 20 | 1024 |
|
||||
| 4 | c2.long | 2 | 120 | 4096 |
|
||||
| 5 | m1.large | 4 | 80 | 8192 |
|
||||
| 6 | m1.medium | 2 | 40 | 4096 |
|
||||
| 7 | m1.small | 1 | 20 | 2048 |
|
||||
| 8 | m1.tiny | 1 | 3 | 512 |
|
||||
| 9 | m1.xlarge | 8 | 160 | 16384 |
|
||||
| 10 | m2.long | 2 | 60 | 2048 |
|
||||
| 11 | snapshot | 2 | 42 | 2048 |
|
||||
+--------+-----------+--------------+------+-------+
|
||||
Flavor: 7
|
||||
|
||||
Image for virtual machines:
|
||||
|
||||
+--------+--------------------------------------+---------------------------+--------------------+-------------+-----------+
|
||||
| API version: v2.0 |
|
||||
| Images |
|
||||
+--------+--------------------------------------+---------------------------+--------------------+-------------+-----------+
|
||||
| Number | ID | Name | Bootstrap template | Remote user | Provider |
|
||||
+--------+--------------------------------------+---------------------------+--------------------+-------------+-----------+
|
||||
| 1 | 78665e7b-5123-4fa8-b39b-d7643ecd8ed7 | centos-6.4-amd64-20130707 | | root | openstack |
|
||||
+--------+--------------------------------------+---------------------------+--------------------+-------------+-----------+
|
||||
Image: 1
|
||||
|
||||
Network for a virtual machine:
|
||||
|
||||
+--------+--------------+-----------------+
|
||||
| API version: v2.0 |
|
||||
| Subnets |
|
||||
+--------+--------------+-----------------+
|
||||
| Number | Name | CIDR |
|
||||
+--------+--------------+-----------------+
|
||||
| 1 | 172.16.223.0 | 172.16.223.0/24 |
|
||||
| 2 | 172.16.227.0 | 172.16.227.0/24 |
|
||||
| 3 | LocalNetwork | 172.16.37.0/24 |
|
||||
| 4 | LocalNetwork | 10.1.98.0/24 |
|
||||
| 5 | private | 10.0.0.0/24 |
|
||||
+--------+--------------+-----------------+
|
||||
Subnets (comma separated), like 1,2,3, or empty: 5
|
||||
|
||||
Chef roles for project and environment. By default will be created new role with name PROJECT-ENV and added to runlist. Additional roles and recipes can be specified here.
|
||||
|
||||
Run list (comma separated), like recipe[mycookbook::myrecipe], role[myrole]: role[my_project_test],
|
||||
|
||||
Just press enter if server lifetime should be infinite.
|
||||
|
||||
Enter expires time if necessary (5m, 3h, 2d, 1w, etc):
|
||||
|
||||
Assume that we do not need second environment. Just press 'n' here.
|
||||
|
||||
Add deploy environment? (y/n): n
|
||||
{
|
||||
"deploy_envs": [
|
||||
{
|
||||
"identifier": "test",
|
||||
"provider": "openstack",
|
||||
"groups": [
|
||||
"default"
|
||||
],
|
||||
"users": [
|
||||
"test"
|
||||
],
|
||||
"flavor": "m1.small",
|
||||
"image": "78665e7b-5123-4fa8-b39b-d7643ecd8ed7",
|
||||
"subnets": [
|
||||
"private"
|
||||
],
|
||||
"run_list": [
|
||||
"role[my_project_test]"
|
||||
],
|
||||
"expires": null
|
||||
}
|
||||
],
|
||||
"name": "my_project"
|
||||
}
|
||||
Create project? (y/n):
|
||||
|
||||
|
||||
Last question allows reviewing details and confirming for project creation.
|
||||
|
||||
<h2 id="howto_server">Starting new instance</h2>
|
||||
|
||||
After that we can create servers and apply chef roles:
|
||||
|
||||
devops server create my_project test -N my_server_1
|
||||
|
||||
'-N' parameter allows to specify chef node name. By default node name will be generated automatically.
|
||||
1038
devops-client/README_ru.md
Normal file
1038
devops-client/README_ru.md
Normal file
File diff suppressed because it is too large
Load Diff
1
devops-client/Rakefile
Normal file
1
devops-client/Rakefile
Normal file
@ -0,0 +1 @@
|
||||
require "bundler/gem_tasks"
|
||||
6
devops-client/bin/devops
Normal file
6
devops-client/bin/devops
Normal file
@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require 'rubygems'
|
||||
require 'devops-client'
|
||||
|
||||
DevopsClient.run
|
||||
256
devops-client/completion/devops_complete.sh
Normal file
256
devops-client/completion/devops_complete.sh
Normal file
@ -0,0 +1,256 @@
|
||||
_devops()
|
||||
{
|
||||
|
||||
local cur prev cmds cmd
|
||||
COMPREPLY=()
|
||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
||||
|
||||
|
||||
PROVIDERS="ec2 openstack"
|
||||
|
||||
grant=""
|
||||
project="create delete list servers set show update add_user remove_user"
|
||||
server="bootstrap create delete list pause show unpause add"
|
||||
|
||||
declare -A commands=( [flavor]=1 [group]=1 [image]=1 [project]=0 [server]=0 [deploy]=1 [key]=1 [user]=1 [grant]=0 [tag]=1 [provider]=1 [network]=1 [script]=1 )
|
||||
|
||||
case "${COMP_CWORD}" in
|
||||
1)
|
||||
#cmds="${!commands[@]}"
|
||||
#cmds="--help --version --completion"
|
||||
cmds=""
|
||||
if [[ "$cur" =~ ^-.* ]]; then
|
||||
_devops_options
|
||||
else
|
||||
for i in "${!commands[@]}"
|
||||
do
|
||||
if [ ${commands[$i]} -eq 1 ]; then
|
||||
cmds="$cmds $i"
|
||||
fi
|
||||
done
|
||||
_set_devops_params
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
if [ ${commands[${COMP_WORDS[1]}]} -ne 1 ]; then
|
||||
# invalid command
|
||||
return
|
||||
fi
|
||||
eval _devops_${COMP_WORDS[1]} ${COMP_WORDS[@]:2}
|
||||
;;
|
||||
esac
|
||||
|
||||
# case "$cmds" in
|
||||
# PROVIDERS)
|
||||
# cmds=$PROVIDERS
|
||||
# ;;
|
||||
# FILE)
|
||||
# COMPREPLY=($(compgen -f "${COMP_WORDS[${COMP_CWORD}]}" ))
|
||||
# return 0
|
||||
# ;;
|
||||
# esac
|
||||
|
||||
# COMPREPLY=( $(compgen -W "${cmds}" -- ${cur}) )
|
||||
return 0
|
||||
}
|
||||
|
||||
_devops_flavor()
|
||||
{
|
||||
case "$1" in
|
||||
list)
|
||||
case "$2" in
|
||||
ec2|openstack)
|
||||
_devops_options ""
|
||||
;;
|
||||
*)
|
||||
_set_devops_params_providers
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
cmds="list"
|
||||
_set_devops_params
|
||||
;;
|
||||
esac
|
||||
}
|
||||
alias _devops_group=_devops_flavor
|
||||
alias _devops_network=_devops_flavor
|
||||
|
||||
_devops_provider()
|
||||
{
|
||||
case "$1" in
|
||||
list)
|
||||
case "$2" in
|
||||
*)
|
||||
_devops_options ""
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
cmds="list"
|
||||
_set_devops_params
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_devops_deploy()
|
||||
{
|
||||
cmds="NODE_NAME"
|
||||
_set_devops_params
|
||||
}
|
||||
|
||||
_devops_user()
|
||||
{
|
||||
case "$1" in
|
||||
list)
|
||||
;;
|
||||
create)
|
||||
;;
|
||||
delete)
|
||||
;;
|
||||
grant)
|
||||
;;
|
||||
password)
|
||||
;;
|
||||
*)
|
||||
cmds="create delete grant list password"
|
||||
_set_devops_params
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_devops_tag()
|
||||
{
|
||||
case "$1" in
|
||||
list)
|
||||
;;
|
||||
create)
|
||||
;;
|
||||
delete)
|
||||
;;
|
||||
*)
|
||||
cmds="create delete list"
|
||||
_set_devops_params
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_devops_key()
|
||||
{
|
||||
case "$1" in
|
||||
list)
|
||||
;;
|
||||
add)
|
||||
;;
|
||||
delete)
|
||||
;;
|
||||
*)
|
||||
cmds="add delete list"
|
||||
_set_devops_params
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_devops_image()
|
||||
{
|
||||
case "$1" in
|
||||
list)
|
||||
case "$2" in
|
||||
provider)
|
||||
case "$3" in
|
||||
ec2|openstack)
|
||||
_devops_options ""
|
||||
;;
|
||||
*)
|
||||
_set_devops_params_providers
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
cmds="provider"
|
||||
_set_devops_params
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
create)
|
||||
_devops_options
|
||||
;;
|
||||
update)
|
||||
if [[ "$2" == "" ]]; then
|
||||
cmds="IMAGE"
|
||||
_set_devops_params
|
||||
else
|
||||
if [[ $COMP_CWORD -eq 4 ]]; then
|
||||
_set_devops_params_file
|
||||
else
|
||||
_devops_options ""
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
delete|show)
|
||||
if [[ "$2" == "" ]]; then
|
||||
cmds="IMAGE"
|
||||
_set_devops_params
|
||||
else
|
||||
_devops_options ""
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
cmds="create delete list show update"
|
||||
_set_devops_params
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_devops_script()
|
||||
{
|
||||
case "$1" in
|
||||
list)
|
||||
;;
|
||||
add)
|
||||
;;
|
||||
run)
|
||||
;;
|
||||
delete)
|
||||
;;
|
||||
command)
|
||||
;;
|
||||
*)
|
||||
cmds="list add delete run command"
|
||||
_set_devops_params
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_devops_options()
|
||||
{
|
||||
declare -A common_options=([--help]="" [--version]="" [--host]="HOST" [--api]="API" [--user]="USER" [--format]="table json" [--completion]="")
|
||||
val="${common_options[${COMP_WORDS[COMP_CWORD - 1]}]}"
|
||||
if [ -z "$val" ]; then
|
||||
cmds="${!common_options[@]}"
|
||||
else
|
||||
cmds="$val"
|
||||
fi
|
||||
_set_devops_params
|
||||
}
|
||||
|
||||
# set copmletion providers
|
||||
_set_devops_params_providers()
|
||||
{
|
||||
cmds="ec2 openstack"
|
||||
_set_devops_params
|
||||
}
|
||||
|
||||
# set copmletion from $cmds
|
||||
_set_devops_params()
|
||||
{
|
||||
COMPREPLY=( $(compgen -W "${cmds}" -- ${cur}) )
|
||||
}
|
||||
|
||||
# set copmletion if type is FILE
|
||||
_set_devops_params_file()
|
||||
{
|
||||
COMPREPLY=($(compgen -f "${COMP_WORDS[${COMP_CWORD}]}" ))
|
||||
}
|
||||
complete -o filenames -o bashdefault -F _devops devops
|
||||
25
devops-client/devops-client.gemspec
Normal file
25
devops-client/devops-client.gemspec
Normal file
@ -0,0 +1,25 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
lib = File.expand_path('../lib', __FILE__)
|
||||
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
||||
require 'devops-client/version'
|
||||
require 'devops-client/name'
|
||||
|
||||
Gem::Specification.new do |gem|
|
||||
#devops-client
|
||||
gem.name = DevopsClient::NAME
|
||||
gem.version = DevopsClient::VERSION
|
||||
gem.authors = ["amartynov"]
|
||||
gem.email = ["amartynov@ggasoftware.com"]
|
||||
gem.description = %q{This is client for devops service}
|
||||
gem.summary = %q{This is client for devops service}
|
||||
gem.homepage = ""
|
||||
|
||||
gem.files = Dir['{bin,lib,completion,locales}/**/*', 'README*', 'LICENSE*']
|
||||
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
||||
# gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
||||
gem.require_paths = ["lib"]
|
||||
|
||||
gem.add_dependency("httpclient", ">= 2.3")
|
||||
gem.add_dependency("json")
|
||||
gem.add_dependency("terminal-table")
|
||||
end
|
||||
181
devops-client/lib/devops-client.rb
Normal file
181
devops-client/lib/devops-client.rb
Normal file
@ -0,0 +1,181 @@
|
||||
require 'devops-client/name'
|
||||
require "devops-client/version"
|
||||
require "devops-client/handler/handler_factory"
|
||||
require "exceptions/not_found"
|
||||
require "exceptions/invalid_query"
|
||||
require "exceptions/devops_exception"
|
||||
require "optparse"
|
||||
require "devops-client/i18n"
|
||||
|
||||
module DevopsClient
|
||||
|
||||
DEVOPS_HOME = "#{ENV["HOME"]}/.devops/"
|
||||
# properties file key=value
|
||||
@@config_file = File.join(DEVOPS_HOME, "devops-client.conf")
|
||||
#CONFIG_FILE="#{ENV["HOME"]}/.devops/devops-client.conf"
|
||||
|
||||
def self.config_file
|
||||
@@config_file
|
||||
end
|
||||
|
||||
def self.run
|
||||
|
||||
DevopsClient::get_config_file_option
|
||||
config = DevopsClient::read_config(@@config_file)
|
||||
|
||||
I18n.language=(config[:locale] || "en")
|
||||
|
||||
if ARGV.include? "--completion"
|
||||
init_completion
|
||||
exit
|
||||
end
|
||||
|
||||
if config[:host].nil?
|
||||
abort(I18n.t("config.invalid.host"), :file => @@config_file)
|
||||
end
|
||||
[:api, :username, :password].each do |key|
|
||||
if config[key].nil?
|
||||
abort(I18n.t("config.invalid.empty", :file => @@config_file, :key => key))
|
||||
end
|
||||
end
|
||||
configure_proxy config
|
||||
|
||||
host = config[:host]
|
||||
default = {:username => config[:username], :api => config[:api], :host => config[:host]}
|
||||
auth = {:username => config[:username], :password => config[:password], :type => "basic"}
|
||||
|
||||
handler = HandlerFactory.create(ARGV[0], host, auth, default)
|
||||
result = handler.handle
|
||||
if result.is_a?(Hash)
|
||||
puts result["message"]
|
||||
else
|
||||
puts result
|
||||
end
|
||||
rescue OptionParser::InvalidOption => e
|
||||
puts e.message
|
||||
exit(11)
|
||||
rescue NotFound => e
|
||||
puts "Not found: #{e.message}"
|
||||
exit(12)
|
||||
rescue InvalidQuery => e
|
||||
puts "Invalid query: #{e.message}"
|
||||
exit(13)
|
||||
rescue DevopsException => e
|
||||
puts I18n.t("log.error", :msg => e.message)
|
||||
exit(14)
|
||||
rescue => e
|
||||
puts I18n.t("log.error", :msg => e.message)
|
||||
raise e
|
||||
rescue Interrupt
|
||||
puts "\nInterrupted"
|
||||
exit(15)
|
||||
end
|
||||
|
||||
PROXY_TYPE_NONE = "none"
|
||||
PROXY_TYPE_SYSTEM = "system"
|
||||
PROXY_TYPE_CUSTOM = "custom"
|
||||
PROXY_TYPES = [PROXY_TYPE_NONE, PROXY_TYPE_SYSTEM, PROXY_TYPE_CUSTOM]
|
||||
PROXY_ENV = ["all_proxy", "ALL_PROXY", "proxy", "PROXY", "http_proxy", "HTTP_PROXY", "https_proxy", "HTTPS_PROXY"]
|
||||
def self.configure_proxy config
|
||||
config[:proxy_type] = PROXY_TYPE_NONE if config[:proxy_type].nil?
|
||||
case config[:proxy_type]
|
||||
when PROXY_TYPE_SYSTEM
|
||||
nil
|
||||
when PROXY_TYPE_NONE
|
||||
PROXY_ENV.each {|k| ENV[k] = nil}
|
||||
when PROXY_TYPE_CUSTOM
|
||||
["http_proxy", "HTTP_PROXY"].each {|k| ENV[k] = config[:http_proxy]}
|
||||
else
|
||||
abort(I18n.t("config.invalid.proxy_type", :file => @@config_file, :values => PROXY_TYPES.join(", ")))
|
||||
end
|
||||
end
|
||||
|
||||
def self.read_config file
|
||||
config = {}
|
||||
if File.exists? file
|
||||
File.open(file, "r") do |f|
|
||||
f.each_line do |line|
|
||||
line.strip!
|
||||
next if line.empty? or line.start_with?("#")
|
||||
buf = line.split("=")
|
||||
config[buf[0].strip.to_sym] = buf[1].strip if !(buf[1].nil? or buf[1].empty?)
|
||||
end
|
||||
end
|
||||
else
|
||||
puts I18n.t("log.warn", :msg => I18n.t("config.not_exist", :file => file))
|
||||
config = set_default_config(file)
|
||||
end
|
||||
config
|
||||
end
|
||||
|
||||
def self.set_default_config file
|
||||
locales = I18n.locales
|
||||
config = {:api => "v2.0", :locale => "en"}
|
||||
I18n.language = config[:locale]
|
||||
config[:locale] = begin
|
||||
l = get_config_parameter(I18n.t("config.property.lang", :langs => locales.join(", ")), config[:locale])
|
||||
raise ArgumentError unless locales.include?(l)
|
||||
I18n.language = l
|
||||
l
|
||||
rescue ArgumentError
|
||||
retry
|
||||
end
|
||||
config[:host] = get_config_parameter(I18n.t("config.property.host"))
|
||||
config[:api] = get_config_parameter(I18n.t("config.property.api"), config[:api])
|
||||
config[:username] = get_config_parameter(I18n.t("config.property.username"))
|
||||
config[:password] = get_config_parameter(I18n.t("config.property.password"))
|
||||
begin
|
||||
config[:proxy_type] = get_config_parameter(I18n.t("config.property.proxy_type"))
|
||||
raise ArgumentError unless PROXY_TYPES.include?(config[:proxy_type])
|
||||
rescue ArgumentError
|
||||
retry
|
||||
end
|
||||
if config[:proxy_type] == PROXY_TYPE_CUSTOM
|
||||
config[:http_proxy] = get_config_parameter(I18n.t("config.property.http_proxy"))
|
||||
end
|
||||
|
||||
dir = File.dirname(@@config_file)
|
||||
require "fileutils"
|
||||
FileUtils.mkdir(dir) unless File.exists? dir
|
||||
File.open(file, "w") do |f|
|
||||
config.each do |k,v|
|
||||
f.puts "#{k.to_s}=#{v}"
|
||||
end
|
||||
end
|
||||
puts I18n.t("config.created", :file => file)
|
||||
config
|
||||
end
|
||||
|
||||
def self.get_config_parameter msg, default=nil
|
||||
print(msg + (default.nil? ? ": " : "(#{default}): "))
|
||||
p = STDIN.gets.strip
|
||||
return (p.empty? ? default : p)
|
||||
end
|
||||
|
||||
def self.get_config_file_option
|
||||
ARGV.each_index do |i|
|
||||
if ARGV[i] == "-c" or ARGV[i] == "--config"
|
||||
if ARGV[i+1] !~ /^-.*/ and ARGV[i+i] !~ /^--.*/
|
||||
@@config_file = ARGV[i+1]
|
||||
ARGV.delete_at(i)
|
||||
ARGV.delete_at(i)
|
||||
else
|
||||
puts I18n.t("log.error", :msg => I18n.t("config.invalid.parameter"))
|
||||
exit(3)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.init_completion
|
||||
spec = Gem::Specification.find_by_name(DevopsClient::NAME)
|
||||
gem_root = spec.gem_dir
|
||||
path = File.join(gem_root, "completion", "devops_complete.sh")
|
||||
require "fileutils"
|
||||
FileUtils.cp(path, DEVOPS_HOME)
|
||||
file = File.join(DEVOPS_HOME, "devops_complete.sh")
|
||||
puts I18n.t("completion.message", :file => file)
|
||||
puts "\n\e[32m#{I18n.t("completion.put", :file => file)}\e[0m"
|
||||
end
|
||||
|
||||
end
|
||||
@ -0,0 +1,32 @@
|
||||
require "devops-client/handler/handler"
|
||||
require "devops-client/options/bootstrap_templates_options"
|
||||
require "json"
|
||||
require "devops-client/output/bootstrap_templates"
|
||||
|
||||
class BootstrapTemplates < Handler
|
||||
|
||||
include Output::BootstrapTemplates
|
||||
|
||||
def initialize(host, def_options={})
|
||||
self.host = host
|
||||
self.options = def_options
|
||||
@options_parser = BootstrapTemplatesOptions.new(ARGV, def_options)
|
||||
end
|
||||
|
||||
def handle
|
||||
case ARGV[1]
|
||||
when "list"
|
||||
self.options = @options_parser.list_options
|
||||
list_handler @options_parser.args
|
||||
output
|
||||
else
|
||||
@options_parser.invalid_command
|
||||
end
|
||||
end
|
||||
|
||||
def list_handler args
|
||||
@list = get("/templates")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
31
devops-client/lib/devops-client/handler/deploy.rb
Normal file
31
devops-client/lib/devops-client/handler/deploy.rb
Normal file
@ -0,0 +1,31 @@
|
||||
require "devops-client/handler/handler"
|
||||
require "devops-client/options/deploy_options"
|
||||
|
||||
class Deploy < Handler
|
||||
|
||||
def initialize(host, def_options={})
|
||||
self.host = host
|
||||
# self.def_options = def_options
|
||||
@options_parser = DeployOptions.new(ARGV, def_options)
|
||||
end
|
||||
|
||||
def handle
|
||||
if ARGV.size > 1
|
||||
self.options = @options_parser.deploy_options
|
||||
deploy_handler @options_parser.args
|
||||
else
|
||||
@options_parser.invalid_command
|
||||
end
|
||||
end
|
||||
|
||||
def deploy_handler args
|
||||
tags = options[:tags]
|
||||
names = args[1..-1]
|
||||
if names.empty?
|
||||
@options_parser.invalid_deploy_command
|
||||
abort(r)
|
||||
end
|
||||
post_chunk("/deploy", :names => names, :tags => tags)
|
||||
end
|
||||
|
||||
end
|
||||
68
devops-client/lib/devops-client/handler/filter.rb
Normal file
68
devops-client/lib/devops-client/handler/filter.rb
Normal file
@ -0,0 +1,68 @@
|
||||
require "devops-client/handler/handler"
|
||||
require "devops-client/options/filter_options"
|
||||
require "json"
|
||||
require "devops-client/output/filters"
|
||||
|
||||
class Filter < Handler
|
||||
|
||||
attr_accessor :def_options
|
||||
|
||||
def initialize(host, def_options)
|
||||
self.host = host
|
||||
self.def_options = def_options
|
||||
@options_parser = FilterOptions.new(ARGV, def_options)
|
||||
end
|
||||
|
||||
include Output::Filters
|
||||
|
||||
def handle
|
||||
case ARGV[1]
|
||||
when "image"
|
||||
provider = ARGV[3]
|
||||
case ARGV[2]
|
||||
when "list"
|
||||
self.options = @options_parser.image_list_options
|
||||
check_provider provider
|
||||
@list = get("/filter/#{provider}/images")
|
||||
output
|
||||
when "add"
|
||||
self.options = @options_parser.image_add_options
|
||||
check_provider provider
|
||||
@list = put_body("/filter/#{provider}/image", get_images(ARGV).to_json)
|
||||
@list = @list["images"] unless @list.nil?
|
||||
output
|
||||
when "delete"
|
||||
self.options = @options_parser.image_delete_options
|
||||
check_provider provider
|
||||
images = get_images(ARGV)
|
||||
if question(I18n.t("handler.filter.question.delete", :name => images.join("', '")))
|
||||
@list = delete_body("/filter/#{provider}/image", images.to_json)
|
||||
@list = @list["images"] unless @list.nil?
|
||||
output
|
||||
end
|
||||
else
|
||||
@options_parser.invalid_image_command
|
||||
abort("Invalid image parameter: #{ARGV[2]}, it should be 'add' or 'delete' or 'list'")
|
||||
end
|
||||
else
|
||||
@options_parser.invalid_command
|
||||
end
|
||||
end
|
||||
|
||||
def check_provider provider
|
||||
if provider != "ec2" and provider != "openstack"
|
||||
@options_parser.invalid_image_command
|
||||
abort("Invalid image parameter: provider '#{provider}', it should be 'ec2' or 'openstack'")
|
||||
end
|
||||
end
|
||||
|
||||
def get_images args
|
||||
images = args[4..-1]
|
||||
if images.empty?
|
||||
@options_parser.invalid_image_command
|
||||
abort("Images list is empty")
|
||||
end
|
||||
images
|
||||
end
|
||||
|
||||
end
|
||||
37
devops-client/lib/devops-client/handler/flavor.rb
Normal file
37
devops-client/lib/devops-client/handler/flavor.rb
Normal file
@ -0,0 +1,37 @@
|
||||
require "devops-client/handler/handler"
|
||||
require "devops-client/options/flavor_options"
|
||||
require "json"
|
||||
require "devops-client/output/flavors"
|
||||
|
||||
class Flavor < Handler
|
||||
|
||||
include Output::Flavors
|
||||
|
||||
def initialize(host, def_options={})
|
||||
self.host = host
|
||||
self.options = def_options
|
||||
@options_parser = FlavorOptions.new(ARGV, def_options)
|
||||
end
|
||||
|
||||
def handle
|
||||
case ARGV[1]
|
||||
when "list"
|
||||
self.options = @options_parser.list_options
|
||||
list_handler @options_parser.args
|
||||
output
|
||||
else
|
||||
@options_parser.invalid_command
|
||||
end
|
||||
end
|
||||
|
||||
def list_handler args
|
||||
r = inspect_parameters @options_parser.list_params, args[2]
|
||||
unless r.nil?
|
||||
@options_parser.invalid_list_command
|
||||
abort(r)
|
||||
end
|
||||
@provider = args[2]
|
||||
@list = get("/flavors/#{args[2]}").sort!{|x,y| x["id"] <=> y["id"]}
|
||||
end
|
||||
|
||||
end
|
||||
39
devops-client/lib/devops-client/handler/group.rb
Normal file
39
devops-client/lib/devops-client/handler/group.rb
Normal file
@ -0,0 +1,39 @@
|
||||
require "devops-client/handler/handler"
|
||||
require "devops-client/options/group_options"
|
||||
require "json"
|
||||
require "devops-client/output/groups"
|
||||
|
||||
class Group < Handler
|
||||
|
||||
include Output::Groups
|
||||
|
||||
def initialize(host, def_options={})
|
||||
self.host = host
|
||||
self.options = def_options
|
||||
@options_parser = GroupOptions.new(ARGV, def_options)
|
||||
end
|
||||
|
||||
def handle
|
||||
case ARGV[1]
|
||||
when "list"
|
||||
self.options = @options_parser.list_options
|
||||
list_handler @options_parser.args
|
||||
output
|
||||
else
|
||||
@options_parser.invalid_command
|
||||
end
|
||||
end
|
||||
|
||||
def list_handler args
|
||||
r = inspect_parameters @options_parser.list_params, args[2]
|
||||
unless r.nil?
|
||||
@options_parser.invalid_list_command
|
||||
abort(r)
|
||||
end
|
||||
@provider = args[2]
|
||||
p = {}
|
||||
p["vpc-id"] = args[3] unless args[3].nil?
|
||||
@list = get("/groups/#{args[2]}", p)
|
||||
end
|
||||
|
||||
end
|
||||
336
devops-client/lib/devops-client/handler/handler.rb
Normal file
336
devops-client/lib/devops-client/handler/handler.rb
Normal file
@ -0,0 +1,336 @@
|
||||
require "httpclient"
|
||||
require "exceptions/devops_exception"
|
||||
require "exceptions/not_found"
|
||||
require "exceptions/invalid_query"
|
||||
require "devops-client/options/common_options"
|
||||
require "uri"
|
||||
require "json"
|
||||
require "devops-client/i18n"
|
||||
|
||||
class Handler
|
||||
|
||||
attr_reader :options
|
||||
attr_writer :host
|
||||
attr_accessor :auth
|
||||
|
||||
def host
|
||||
"http://#{@host}"
|
||||
end
|
||||
|
||||
#TODO: only basic auth now
|
||||
def username
|
||||
self.options[:username] || self.auth[:username]
|
||||
end
|
||||
|
||||
def password
|
||||
self.options[:password] || self.auth[:password]
|
||||
end
|
||||
|
||||
def options= o
|
||||
self.host = o.delete(:host) if o.has_key? :host
|
||||
@options = o
|
||||
end
|
||||
|
||||
def get_chunk path, params={}
|
||||
submit do |http|
|
||||
http.get(create_url(path), convert_params(params)) do |chunk|
|
||||
puts chunk
|
||||
end
|
||||
end
|
||||
""
|
||||
end
|
||||
|
||||
def get path, params={}
|
||||
get_with_headers path, params, self.headers("Content-Type")
|
||||
end
|
||||
|
||||
def get_with_headers path, params={}, headers={}
|
||||
submit do |http|
|
||||
http.get(create_url(path), convert_params(params), headers)
|
||||
end
|
||||
end
|
||||
|
||||
def post path, params={}
|
||||
self.post_body(path, params.to_json)
|
||||
end
|
||||
|
||||
def post_body path, body
|
||||
post_body_with_headers path, body, self.headers
|
||||
end
|
||||
|
||||
def post_chunk_body path, body, json=true
|
||||
h = (json ? self.headers : self.headers("Content-Type", "Accept"))
|
||||
submit do |http|
|
||||
buf = ""
|
||||
resp = http.post(create_url(path), body, h) do |chunk|
|
||||
puts chunk
|
||||
buf = chunk
|
||||
end
|
||||
if resp.ok?
|
||||
status = check_status(buf)
|
||||
exit(status) unless status == 0
|
||||
end
|
||||
resp
|
||||
end
|
||||
""
|
||||
end
|
||||
|
||||
def post_chunk path, params={}
|
||||
self.post_chunk_body path, params.to_json
|
||||
end
|
||||
|
||||
def post_body_with_headers path, body='', headers={}
|
||||
submit do |http|
|
||||
http.post(create_url(path), body, headers)
|
||||
end
|
||||
end
|
||||
|
||||
def delete path, params={}
|
||||
delete_body path, params.to_json
|
||||
end
|
||||
|
||||
def delete_body path, body
|
||||
submit do |http|
|
||||
http.delete(create_url(path), body, self.headers)
|
||||
end
|
||||
end
|
||||
|
||||
def put path, params={}
|
||||
put_body path, params.to_json
|
||||
end
|
||||
|
||||
def put_body path, body
|
||||
submit do |http|
|
||||
http.put(create_url(path), body, self.headers)
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
def puts_warn msg
|
||||
puts "\e[33m#{msg}\e[0m"
|
||||
end
|
||||
|
||||
def puts_error msg
|
||||
puts "\e[31m#{msg}\e[0m"
|
||||
end
|
||||
|
||||
def question str
|
||||
return true if self.options[:no_ask]
|
||||
if block_given?
|
||||
yield
|
||||
end
|
||||
res = false
|
||||
#system("stty raw -echo")
|
||||
begin
|
||||
print "#{str} (y/n): "
|
||||
s = STDIN.gets.strip
|
||||
if s == "y"
|
||||
res = true
|
||||
elsif s == "n"
|
||||
res == false
|
||||
else
|
||||
raise ArgumentError.new
|
||||
end
|
||||
rescue ArgumentError
|
||||
retry
|
||||
end
|
||||
#print "#{s}\n\r"
|
||||
#system("stty -raw echo")
|
||||
res
|
||||
end
|
||||
|
||||
def choose_image_cmd images, t=nil
|
||||
abort(I18n.t("handler.error.list.empty", :name => "Image")) if images.empty?
|
||||
if options[:image_id].nil?
|
||||
images[ choose_number_from_list(I18n.t("headers.image"), images, t) ]
|
||||
else
|
||||
i = images.detect { |i| i["name"] == options[:image_id]}
|
||||
abort("No such image") if i.nil?
|
||||
return i
|
||||
end
|
||||
end
|
||||
|
||||
def get_comma_separated_list msg
|
||||
print msg
|
||||
STDIN.gets.strip.split(",").map{|e| e.strip}
|
||||
end
|
||||
|
||||
def enter_parameter msg
|
||||
str = enter_parameter_or_empty(msg)
|
||||
raise ArgumentError.new if str.empty?
|
||||
str
|
||||
rescue ArgumentError
|
||||
retry
|
||||
end
|
||||
|
||||
def enter_parameter_or_empty msg
|
||||
print msg
|
||||
return STDIN.gets.strip
|
||||
end
|
||||
|
||||
def choose_number_from_list title, list, table=nil, default=nil
|
||||
i = 0
|
||||
if table.nil?
|
||||
puts I18n.t("handler.message.choose", :name => title.downcase) + "\n" + list.map{|p| i += 1; "#{i}. #{p}"}.join("\n")
|
||||
else
|
||||
puts table
|
||||
end
|
||||
begin
|
||||
print "#{title}: "
|
||||
buf = STDIN.gets.strip
|
||||
if buf.empty? and !default.nil?
|
||||
return default
|
||||
end
|
||||
i = Integer buf
|
||||
rescue ArgumentError
|
||||
retry
|
||||
end until i > 0 and i <= list.size
|
||||
return i - 1
|
||||
end
|
||||
|
||||
def choose_indexes_from_list title, list, table=nil, default=nil, defindex=nil
|
||||
abort(I18n.t("handler.error.list.empty", :name => title)) if list.empty?
|
||||
ar = nil
|
||||
if table.nil?
|
||||
i = 0
|
||||
print I18n.t("handler.message.choose", :name => title.downcase) + "\n#{list.map{|p| i += 1; "#{i}. #{p}"}.join("\n")}\n"
|
||||
else
|
||||
puts table
|
||||
end
|
||||
msg = if default.nil?
|
||||
I18n.t("handler.message.choose_list", :name => title)
|
||||
else
|
||||
I18n.t("handler.message.choose_list_default", :name => title, :default => default)
|
||||
end
|
||||
begin
|
||||
ar = get_comma_separated_list(msg).map do |g|
|
||||
n = Integer g.strip
|
||||
raise ArgumentError.new(I18n.t("handler.error.number.invalid")) if n < 1 or n > list.size
|
||||
n
|
||||
end
|
||||
if ar.empty? and !default.nil?
|
||||
return [ defindex ]
|
||||
end
|
||||
rescue ArgumentError
|
||||
retry
|
||||
end
|
||||
ar.map{|i| i - 1}
|
||||
end
|
||||
|
||||
def output
|
||||
case self.options[:format]
|
||||
when CommonOptions::TABLE_FORMAT
|
||||
table
|
||||
when CommonOptions::JSON_FORMAT
|
||||
json
|
||||
when CommonOptions::CSV_FORMAT
|
||||
csv
|
||||
end
|
||||
end
|
||||
|
||||
def update_object_from_file object_class, object_id, file
|
||||
unless File.exists?(file)
|
||||
@options_parser.invalid_update_command
|
||||
abort I18n.t("handler.error.file.not_exist", :file => file)
|
||||
end
|
||||
update_object_from_json object_class, object_id, File.read(file)
|
||||
end
|
||||
|
||||
def update_object_from_json object_class, object_id, json
|
||||
put_body "/#{object_class}/#{object_id}", json
|
||||
rescue NotFound => e
|
||||
post_body "/#{object_class}", json
|
||||
end
|
||||
|
||||
def create_url path
|
||||
URI.join(self.host, self.options[:api] + path).to_s
|
||||
end
|
||||
|
||||
def submit
|
||||
http = HTTPClient.new
|
||||
http.receive_timeout = 0
|
||||
http.send_timeout = 0
|
||||
http.set_auth(nil, self.username, self.password)
|
||||
res = yield http
|
||||
if res.ok?
|
||||
return (res.contenttype.include?("application/json") ? JSON.parse(res.body) : res.body)
|
||||
end
|
||||
case res.status
|
||||
when 404
|
||||
raise NotFound.new(extract_message(res))
|
||||
when 400
|
||||
raise InvalidQuery.new(extract_message(res))
|
||||
when 401
|
||||
e = extract_message(res)
|
||||
e = I18n.t("handler.error.unauthorized") if (e.nil? or e.strip.empty?)
|
||||
raise DevopsException.new(e)
|
||||
else
|
||||
raise DevopsException.new(extract_message(res))
|
||||
end
|
||||
end
|
||||
|
||||
def extract_message result
|
||||
return nil if result.body.nil?
|
||||
result.contenttype.include?("application/json") ? JSON.parse(result.body)["message"] : result.body
|
||||
end
|
||||
|
||||
def convert_params params
|
||||
params_filter(params.select{|k,v| k != :cmd and !v.nil?}).join("&")
|
||||
end
|
||||
|
||||
def params_filter params
|
||||
r = []
|
||||
return params if params.kind_of?(String)
|
||||
params.each do |k,v|
|
||||
key = k.to_s
|
||||
if v.kind_of?(Array)
|
||||
v.each do |val|
|
||||
r.push "#{key}[]=#{val}"
|
||||
end
|
||||
elsif v.kind_of?(Hash)
|
||||
buf = {}
|
||||
v.each do |k1,v1|
|
||||
buf["#{key}[#{k1}]"] = v1
|
||||
end
|
||||
r = r + params_filter(buf)
|
||||
else
|
||||
r.push "#{key}=#{v}"
|
||||
end
|
||||
end
|
||||
r
|
||||
end
|
||||
|
||||
def inspect_parameters names, *args
|
||||
names.each_with_index do |name, i|
|
||||
next if name.start_with? "[" and name.end_with? "]"
|
||||
if args[i].nil? or args[i].empty?
|
||||
return "\n" + I18n.t("handler.error.parameter.undefined", :name => name)
|
||||
end
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
def headers *exclude
|
||||
h = {
|
||||
"Accept" => "application/json",
|
||||
"Content-Type" => "application/json; charset=UTF-8"
|
||||
}
|
||||
|
||||
h["Accept-Language"] = I18n.lang
|
||||
exclude.each do |key|
|
||||
h.delete(key)
|
||||
end
|
||||
h
|
||||
end
|
||||
|
||||
def check_status status
|
||||
r = status.scan(/--\sStatus:\s([0-9]{1,5})\s--/i)[0]
|
||||
if r.nil?
|
||||
puts "WARN: status undefined"
|
||||
-1
|
||||
else
|
||||
r[0].to_i
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
56
devops-client/lib/devops-client/handler/handler_factory.rb
Normal file
56
devops-client/lib/devops-client/handler/handler_factory.rb
Normal file
@ -0,0 +1,56 @@
|
||||
class HandlerFactory
|
||||
|
||||
def self.create cmd, host, auth, def_options
|
||||
klass = case cmd
|
||||
when "flavor"
|
||||
require "devops-client/handler/flavor"
|
||||
Flavor
|
||||
when "image"
|
||||
require "devops-client/handler/image"
|
||||
Image
|
||||
when "filter"
|
||||
require "devops-client/handler/filter"
|
||||
Filter
|
||||
when "group"
|
||||
require "devops-client/handler/group"
|
||||
Group
|
||||
when "deploy"
|
||||
require "devops-client/handler/deploy"
|
||||
Deploy
|
||||
when "project"
|
||||
require "devops-client/handler/project"
|
||||
Project
|
||||
when "network"
|
||||
require "devops-client/handler/network"
|
||||
Network
|
||||
when "key"
|
||||
require "devops-client/handler/key"
|
||||
Key
|
||||
when "user"
|
||||
require "devops-client/handler/user"
|
||||
User
|
||||
when "provider"
|
||||
require "devops-client/handler/provider"
|
||||
Provider
|
||||
when "tag"
|
||||
require "devops-client/handler/tag"
|
||||
Tag
|
||||
when "server"
|
||||
require "devops-client/handler/server"
|
||||
Server
|
||||
when "script"
|
||||
require "devops-client/handler/script"
|
||||
Script
|
||||
when "templates"
|
||||
require "devops-client/handler/bootstrap_templates"
|
||||
BootstrapTemplates
|
||||
else
|
||||
require "devops-client/options/main"
|
||||
Main.new(ARGV, def_options).info
|
||||
exit(10)
|
||||
end
|
||||
service = klass.new(host, def_options)
|
||||
service.auth = auth
|
||||
service
|
||||
end
|
||||
end
|
||||
139
devops-client/lib/devops-client/handler/image.rb
Normal file
139
devops-client/lib/devops-client/handler/image.rb
Normal file
@ -0,0 +1,139 @@
|
||||
require "devops-client/handler/provider"
|
||||
require "devops-client/handler/handler"
|
||||
require "devops-client/options/image_options"
|
||||
require "devops-client/output/image"
|
||||
require "devops-client/handler/bootstrap_templates"
|
||||
|
||||
class Image < Handler
|
||||
|
||||
include Output::Image
|
||||
|
||||
def initialize(host, def_options={})
|
||||
self.host = host
|
||||
self.options = def_options
|
||||
@options_parser = ImageOptions.new(ARGV, def_options)
|
||||
end
|
||||
|
||||
def handle
|
||||
case ARGV[1]
|
||||
when "list"
|
||||
self.options = @options_parser.list_options
|
||||
list_handler @options_parser.args
|
||||
output
|
||||
when "show"
|
||||
self.options = @options_parser.show_options
|
||||
show_handler @options_parser.args
|
||||
output
|
||||
when "create"
|
||||
self.options = @options_parser.create_options
|
||||
create_handler
|
||||
when "delete"
|
||||
self.options = @options_parser.delete_options
|
||||
delete_handler @options_parser.args
|
||||
when "update"
|
||||
self.options = @options_parser.update_options
|
||||
update_handler @options_parser.args
|
||||
else
|
||||
@options_parser.invalid_command
|
||||
end
|
||||
end
|
||||
|
||||
def get_providers
|
||||
p = Provider.new(@host, self.options)
|
||||
p.auth = self.auth
|
||||
return p.list_handler(["provider", "list"]), p.table
|
||||
end
|
||||
|
||||
def get_templates
|
||||
bt = BootstrapTemplates.new(@host, self.options)
|
||||
bt.auth = self.auth
|
||||
return bt.list_handler(["templates", "list"]), bt.table
|
||||
end
|
||||
|
||||
def create_handler
|
||||
providers, table = get_providers
|
||||
provider = (self.options[:provider].nil? ? providers[ choose_number_from_list(I18n.t("headers.provider"), providers, table) ] : self.options[:provider])
|
||||
provider_images provider
|
||||
q = { "provider" => provider }
|
||||
|
||||
image = nil
|
||||
if options[:image_id].nil?
|
||||
image = choose_image_cmd(@list, self.table)
|
||||
else
|
||||
image = @list.detect{|i| i["id"] == options[:image_id]}
|
||||
abort("Invalid image id '#{options[:image_id]}'") if image.nil?
|
||||
end
|
||||
q["name"] = image["name"]
|
||||
q["id"] = image["id"]
|
||||
|
||||
if options[:ssh_username].nil?
|
||||
q["remote_user"] = enter_parameter(I18n.t("handler.image.create.ssh_user") + ": ")
|
||||
else
|
||||
q["remote_user"] = options[:ssh_username]
|
||||
end
|
||||
|
||||
q["bootstrap_template"] = if options[:bootstrap_template].nil? and options[:no_bootstrap_template] == false
|
||||
bt, bt_t = get_templates
|
||||
i = choose_number_from_list(I18n.t("handler.image.create.template"), bt, bt_t, -1)
|
||||
if i == -1
|
||||
nil
|
||||
else
|
||||
bt[i]
|
||||
end
|
||||
else
|
||||
nil
|
||||
end
|
||||
json = JSON.pretty_generate(q)
|
||||
post_body "/image", json if question(I18n.t("handler.image.question.create")){puts json}
|
||||
end
|
||||
|
||||
def list_handler args
|
||||
if args[2].nil?
|
||||
@provider = false
|
||||
@list = get("/images")
|
||||
elsif args[2] == "provider" and (args[3] == "ec2" || args[3] == "openstack")
|
||||
provider_images args[3]
|
||||
elsif args[2] == "ec2" || args[2] == "openstack"
|
||||
@provider = false
|
||||
@list = get("/images", :provider => args[2])
|
||||
else
|
||||
@options_parser.invalid_list_command
|
||||
abort()
|
||||
end
|
||||
end
|
||||
|
||||
def provider_images p
|
||||
@provider = true
|
||||
@list = get("/images/provider/#{p}")
|
||||
end
|
||||
|
||||
def show_handler args
|
||||
r = inspect_parameters @options_parser.show_params, args[2]
|
||||
unless r.nil?
|
||||
@options_parser.invalid_show_command
|
||||
abort(r)
|
||||
end
|
||||
@show = get "/image/#{args[2]}"
|
||||
end
|
||||
|
||||
def delete_handler args
|
||||
r = inspect_parameters @options_parser.delete_params, args[2]
|
||||
unless r.nil?
|
||||
@options_parser.invalid_delete_command
|
||||
abort(r)
|
||||
end
|
||||
if question(I18n.t("handler.image.question.delete", :name => args[2]))
|
||||
delete "/image/#{args[2]}"
|
||||
end
|
||||
end
|
||||
|
||||
def update_handler args
|
||||
r = inspect_parameters @options_parser.update_params, args[2], args[3]
|
||||
unless r.nil?
|
||||
@options_parser.invalid_update_command
|
||||
abort(r)
|
||||
end
|
||||
update_object_from_file "image", args[2], args[3]
|
||||
end
|
||||
|
||||
end
|
||||
63
devops-client/lib/devops-client/handler/key.rb
Normal file
63
devops-client/lib/devops-client/handler/key.rb
Normal file
@ -0,0 +1,63 @@
|
||||
require "devops-client/handler/handler"
|
||||
require "devops-client/options/key_options"
|
||||
require "json"
|
||||
require "devops-client/output/key"
|
||||
|
||||
class Key < Handler
|
||||
include Output::Key
|
||||
|
||||
def initialize(host, def_options={})
|
||||
self.host = host
|
||||
self.options = def_options
|
||||
@options_parser = KeyOptions.new(ARGV, def_options)
|
||||
end
|
||||
|
||||
def handle
|
||||
case ARGV[1]
|
||||
when "list"
|
||||
self.options = @options_parser.list_options
|
||||
list_handler
|
||||
output
|
||||
when "add"
|
||||
self.options = @options_parser.add_options
|
||||
add_handler @options_parser.args
|
||||
when "delete"
|
||||
self.options = @options_parser.delete_options
|
||||
delete_handler @options_parser.args
|
||||
else
|
||||
@options_parser.invalid_command
|
||||
end
|
||||
end
|
||||
|
||||
def add_handler args
|
||||
r = inspect_parameters @options_parser.add_params, args[2], args[3]
|
||||
unless r.nil?
|
||||
@options_parser.invalid_add_command
|
||||
abort(r)
|
||||
end
|
||||
|
||||
content = File.read(args[3])
|
||||
q = {
|
||||
"key_name" => args[2],
|
||||
"file_name" => File.basename(args[3]),
|
||||
"content" => content
|
||||
}
|
||||
post "/key", q
|
||||
end
|
||||
|
||||
def delete_handler args
|
||||
r = inspect_parameters @options_parser.delete_params, args[2]
|
||||
unless r.nil?
|
||||
@options_parser.invalid_delete_command
|
||||
abort(r)
|
||||
end
|
||||
if question(I18n.t("handler.key.question.delete", :name => args[2]))
|
||||
delete "/key/#{args[2]}"
|
||||
end
|
||||
end
|
||||
|
||||
def list_handler
|
||||
@list = get("/keys")
|
||||
end
|
||||
|
||||
end
|
||||
38
devops-client/lib/devops-client/handler/network.rb
Normal file
38
devops-client/lib/devops-client/handler/network.rb
Normal file
@ -0,0 +1,38 @@
|
||||
require "devops-client/handler/handler"
|
||||
require "devops-client/options/network_options"
|
||||
require "json"
|
||||
require "devops-client/output/network"
|
||||
|
||||
class Network < Handler
|
||||
|
||||
include Output::Network
|
||||
|
||||
def initialize(host, def_options={})
|
||||
self.host = host
|
||||
self.options = def_options
|
||||
@options_parser = NetworkOptions.new(ARGV, def_options)
|
||||
end
|
||||
|
||||
def handle
|
||||
case ARGV[1]
|
||||
when "list"
|
||||
self.options = @options_parser.list_options
|
||||
list_handler @options_parser.args
|
||||
output
|
||||
else
|
||||
@options_parser.invalid_command
|
||||
end
|
||||
end
|
||||
|
||||
def list_handler args
|
||||
r = inspect_parameters @options_parser.list_params, args[2]
|
||||
unless r.nil?
|
||||
@options_parser.invalid_list_command
|
||||
abort(r)
|
||||
end
|
||||
@provider = args[2]
|
||||
@list = get("/networks/#{args[2]}").sort!{|x,y| x["name"] <=> y["name"]}
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
578
devops-client/lib/devops-client/handler/project.rb
Normal file
578
devops-client/lib/devops-client/handler/project.rb
Normal file
@ -0,0 +1,578 @@
|
||||
require "devops-client/handler/provider"
|
||||
require "devops-client/handler/image"
|
||||
require "devops-client/handler/flavor"
|
||||
require "devops-client/handler/network"
|
||||
require "devops-client/handler/group"
|
||||
require "devops-client/handler/user"
|
||||
require "devops-client/options/project_options"
|
||||
require "json"
|
||||
require "set"
|
||||
require "devops-client/output/project"
|
||||
|
||||
class Project < Handler
|
||||
|
||||
attr_accessor :def_options
|
||||
|
||||
include Output::Project
|
||||
|
||||
def initialize(host, def_options)
|
||||
self.host = host
|
||||
self.def_options = def_options
|
||||
@options_parser = ProjectOptions.new(ARGV, def_options)
|
||||
end
|
||||
|
||||
def handle
|
||||
case ARGV[1]
|
||||
when "create"
|
||||
self.options = @options_parser.create_options
|
||||
create_handler @options_parser.args
|
||||
when "delete"
|
||||
self.options = @options_parser.delete_options
|
||||
delete_handler @options_parser.args
|
||||
when "deploy"
|
||||
self.options = @options_parser.deploy_options
|
||||
deploy_handler @options_parser.args
|
||||
when "list"
|
||||
self.options = @options_parser.list_options
|
||||
list_handler
|
||||
output
|
||||
when "multi"
|
||||
case ARGV[2]
|
||||
when "create"
|
||||
self.options = @options_parser.multi_create_options
|
||||
multi_create_handler @options_parser.args
|
||||
else
|
||||
@options_parser.invalid_multi_command
|
||||
abort(I18n.t("handler.project.invalid_subcommand", :cmd => ARGV[1], :scmd => ARGV[2]))
|
||||
end
|
||||
when "servers"
|
||||
self.options = @options_parser.servers_options
|
||||
servers_handler @options_parser.args
|
||||
output
|
||||
when "set"
|
||||
case ARGV[2]
|
||||
when "run_list"
|
||||
self.options = @options_parser.set_run_list_options
|
||||
set_run_list_handler @options_parser.args
|
||||
else
|
||||
@options_parser.invalid_set_command
|
||||
abort(I18n.t("handler.project.invalid_subcommand", :cmd => ARGV[1], :scmd => ARGV[2]))
|
||||
end
|
||||
when "show"
|
||||
self.options = @options_parser.show_options
|
||||
show_handler @options_parser.args
|
||||
output
|
||||
when "update"
|
||||
self.options = @options_parser.update_options
|
||||
update_handler @options_parser.args
|
||||
when "user"
|
||||
case ARGV[2]
|
||||
when "add"
|
||||
self.options = @options_parser.user_add_options
|
||||
user_add_handler @options_parser.args
|
||||
when "delete"
|
||||
self.options = @options_parser.user_delete_options
|
||||
user_delete_handler @options_parser.args
|
||||
else
|
||||
@options_parser.invalid_user_command
|
||||
abort(I18n.t("handler.project.invalid_subcommand", :cmd => ARGV[1], :scmd => ARGV[2]))
|
||||
end
|
||||
when "test"
|
||||
self.options = @options_parser.test_options
|
||||
test_handler @options_parser.args
|
||||
output
|
||||
else
|
||||
@options_parser.invalid_command
|
||||
end
|
||||
end
|
||||
|
||||
def list_handler
|
||||
@list = get "/projects"
|
||||
end
|
||||
|
||||
def delete_handler args
|
||||
r = inspect_parameters @options_parser.delete_params, args[2], args[3]
|
||||
unless r.nil?
|
||||
@options_parser.invalid_delete_command
|
||||
abort(r)
|
||||
end
|
||||
o = {}
|
||||
o[:deploy_env] = args[3] unless args[3].nil?
|
||||
|
||||
message = args[2]
|
||||
message += ".#{args[3]}" unless args[3].nil?
|
||||
if question(I18n.t("handler.project.question.delete", :name => message))
|
||||
delete "/project/#{args[2]}", o
|
||||
end
|
||||
end
|
||||
|
||||
def show_handler args
|
||||
r = inspect_parameters @options_parser.show_params, args[2]
|
||||
unless r.nil?
|
||||
@options_parser.invalid_show_command
|
||||
abort(r)
|
||||
end
|
||||
@show = get_project_info_obj(args[2])
|
||||
end
|
||||
|
||||
def update_handler args
|
||||
r = inspect_parameters @options_parser.update_params, args[2], args[3]
|
||||
unless r.nil?
|
||||
@options_parser.invalid_update_command
|
||||
abort(r)
|
||||
end
|
||||
update_object_from_file "project", args[2], args[3]
|
||||
end
|
||||
|
||||
def create_handler args
|
||||
file = self.options[:file]
|
||||
unless file.nil?
|
||||
json = File.read(file)
|
||||
begin
|
||||
JSON.parse(json)
|
||||
rescue JSON::ParserError => e
|
||||
abort(I18n.t("handler.project.create.invalid_json", :file => file))
|
||||
end
|
||||
post_body("/project", json)
|
||||
else
|
||||
r = inspect_parameters @options_parser.create_params, args[2]
|
||||
unless r.nil?
|
||||
@options_parser.invalid_create_command
|
||||
abort(r)
|
||||
end
|
||||
unless self.options[:username].nil? || self.options[:password].nil?
|
||||
self.auth[:username] = self.options[:username]
|
||||
self.auth[:password] = self.options[:password]
|
||||
self.def_options[:username] = self.auth[:username]
|
||||
end
|
||||
create_project args, :create_project_deploy_env_cmd
|
||||
end
|
||||
end
|
||||
|
||||
def servers_handler args
|
||||
r = inspect_parameters @options_parser.servers_params, args[2], args[3]
|
||||
unless r.nil?
|
||||
@options_parser.invalid_servers_command
|
||||
abort(r)
|
||||
end
|
||||
o = {}
|
||||
unless args[3].nil?
|
||||
o[:deploy_env] = args[3]
|
||||
end
|
||||
@servers = get "/project/#{args[2]}/servers", o
|
||||
end
|
||||
|
||||
def user_add_handler args
|
||||
r = inspect_parameters @options_parser.user_add_params, args[3], args[4]
|
||||
unless r.nil?
|
||||
@options_parser.invalid_user_add_command
|
||||
abort(r)
|
||||
end
|
||||
q = {:users => args[4..-1]}
|
||||
q[:deploy_env] = options[:deploy_env] unless options[:deploy_env].nil?
|
||||
put "/project/#{args[3]}/user", q
|
||||
end
|
||||
|
||||
def user_delete_handler args
|
||||
r = inspect_parameters @options_parser.user_delete_params, args[3], args[4]
|
||||
unless r.nil?
|
||||
@options_parser.invalid_user_delete_command
|
||||
abort(r)
|
||||
end
|
||||
q = {:users => args[4..-1]}
|
||||
q[:deploy_env] = options[:deploy_env] unless options[:deploy_env].nil?
|
||||
delete_body "/project/#{args[3]}/user", q.to_json
|
||||
end
|
||||
|
||||
def multi_create_handler args
|
||||
r = inspect_parameters @options_parser.multi_create_params, args[3]
|
||||
unless r.nil?
|
||||
@options_parser.invalid_multi_create_command
|
||||
abort(r)
|
||||
end
|
||||
|
||||
create_project args, :create_project_multi_deploy_env_cmd, :multi
|
||||
|
||||
i = Image.new(@host, self.def_options)
|
||||
images, ti = i.list_handler, i.table
|
||||
f = Flavor.new(@host, self.def_options)
|
||||
flavors, tf = f.list_handler, f.table
|
||||
g = Group.new(@host, self.def_options)
|
||||
groups, tg = g.list_handler, g.table
|
||||
|
||||
list = list_handler
|
||||
info, multi = {}, {:type => "multi", :name => args[3], :deploy_envs => []}
|
||||
begin # Add environment
|
||||
nodes, projects, servers = [], [], {}
|
||||
deploy_env = {:identifier => enter_parameter("Deploy environment identifier: ")}
|
||||
begin # Add server
|
||||
server_name = args[3] + "_" + enter_parameter("Server name: " + args[3] + "_")
|
||||
s = servers[server_name] = {}
|
||||
s[:groups] = choose_indexes_from_list("Security groups", list, tg, "default", list.index("default")).map{|i| list[i]}
|
||||
s[:flavor] = choose_flavor_cmd(flavors, tf)["name"]
|
||||
s[:image] = choose_image_cmd(images, ti)["id"]
|
||||
subprojects = s[:subprojects] = []
|
||||
|
||||
begin # Add project
|
||||
o = {}
|
||||
o[:project_id] = project_id = choose_project(list, table)
|
||||
info[project_id] = get_project_info_obj(project_id) unless info.has_key?(project_id)
|
||||
envs = info[project_id]["deploy_envs"].map{|de| de["identifier"]}
|
||||
o[:project_env] = ( envs.size > 1 ? choose_project_env(envs) : envs[0] )
|
||||
subprojects.push o
|
||||
end while question("Add project?")
|
||||
|
||||
end while question("Add server?")
|
||||
|
||||
deploy_env[:servers] = servers
|
||||
multi[:deploy_envs].push deploy_env
|
||||
end while question(I18n.t("handler.project.question.add_env"))
|
||||
puts JSON.pretty_generate(multi)
|
||||
post "/project", :json => multi.to_json if question(I18n.t("handler.project.question.create"))
|
||||
end
|
||||
|
||||
def set_run_list_handler args
|
||||
r = inspect_parameters @options_parser.set_run_list_params, args[3], args[4], args[5]
|
||||
unless r.nil?
|
||||
@options_parser.invalid_set_run_list_command
|
||||
abort(r)
|
||||
end
|
||||
run_list = []
|
||||
args[5..args.size].each do |e|
|
||||
run_list += e.split(",")
|
||||
end
|
||||
if run_list.empty?
|
||||
exit unless question(I18n.t("handler.project.run_list.empty"))
|
||||
else
|
||||
exit unless Project.validate_run_list(run_list)
|
||||
end
|
||||
put "/project/#{args[3]}/#{args[4]}/run_list", run_list
|
||||
end
|
||||
|
||||
def deploy_handler args
|
||||
r = inspect_parameters @options_parser.deploy_params, args[2], args[3]
|
||||
unless r.nil?
|
||||
@options_parser.invalid_deploy_command
|
||||
abort(r)
|
||||
end
|
||||
q = {}
|
||||
q[:servers] = options[:servers] unless options[:servers].nil?
|
||||
q[:deploy_env] = args[3] unless args[3].nil?
|
||||
post_chunk "/project/#{args[2]}/deploy", q
|
||||
end
|
||||
|
||||
def test_handler args
|
||||
r = inspect_parameters @options_parser.test_params, args[2], args[3]
|
||||
unless r.nil?
|
||||
@options_parser.invalid_test_command
|
||||
abort(r)
|
||||
end
|
||||
@test = post "/project/test/#{args[2]}/#{args[3]}"
|
||||
end
|
||||
|
||||
protected
|
||||
def get_project_info_obj project_id
|
||||
get("/project/#{project_id}")
|
||||
end
|
||||
|
||||
def get_providers
|
||||
p = Provider.new(@host, self.def_options)
|
||||
p.auth = self.auth
|
||||
return p.list_handler(["provider", "list"]), p.table
|
||||
end
|
||||
|
||||
def get_images provider
|
||||
img = Image.new(@host, self.def_options)
|
||||
img.auth = self.auth
|
||||
return img.list_handler(["image", "list", provider]), img.table
|
||||
end
|
||||
|
||||
def get_flavors provider
|
||||
f = Flavor.new(@host, self.def_options)
|
||||
f.auth = self.auth
|
||||
return f.list_handler(["flavor", "list", provider]), f.table
|
||||
end
|
||||
|
||||
def get_groups provider, vpcId
|
||||
g = Group.new(@host, self.def_options)
|
||||
g.auth = self.auth
|
||||
p = ["group", "list", provider]
|
||||
p.push vpcId if !vpcId.nil? and provider == "ec2"
|
||||
return g.list_handler(p), g.table
|
||||
end
|
||||
|
||||
def get_networks provider
|
||||
n = Network.new(@host, self.def_options)
|
||||
n.auth = self.auth
|
||||
return n.list_handler(["network", "list", provider]), n.table
|
||||
end
|
||||
|
||||
def get_users
|
||||
u = User.new(@host, self.def_options)
|
||||
u.auth = self.auth
|
||||
return u.list_handler, u.table
|
||||
end
|
||||
|
||||
def create_project args, env_method_name, type=nil
|
||||
project_name = args[2]
|
||||
providers = {}
|
||||
begin
|
||||
project = get_project_info_obj(project_name)
|
||||
puts_warn I18n.t("handler.project.exist", :project => project_name)
|
||||
names = project["deploy_envs"].map{|de| de["identifier"]}
|
||||
while question(I18n.t("handler.project.question.add_env"))
|
||||
d = method(env_method_name).call(project_name, providers, names)
|
||||
project["deploy_envs"].push d
|
||||
break if self.options[:no_ask]
|
||||
end
|
||||
puts json = JSON.pretty_generate(project)
|
||||
update_object_from_json("project", project_name, json) if question(I18n.t("handler.project.question.update"))
|
||||
rescue NotFound => e
|
||||
project = create_project_cmd(project_name, providers, env_method_name)
|
||||
project[:name] = args[2]
|
||||
puts json = JSON.pretty_generate(project)
|
||||
post_body("/project", json) if question(I18n.t("handler.project.question.create"))
|
||||
end
|
||||
end
|
||||
|
||||
def create_project_cmd project_name, providers, env_method
|
||||
project = {:deploy_envs => []}
|
||||
names = []
|
||||
begin
|
||||
d = method(env_method).call(project_name, providers, names)
|
||||
project[:deploy_envs].push d
|
||||
break if self.options[:no_ask]
|
||||
end while question(I18n.t("handler.project.question.add_env"))
|
||||
project
|
||||
end
|
||||
|
||||
def create_project_deploy_env_cmd project, providers, names
|
||||
d = {}
|
||||
set_identifier(d, names)
|
||||
|
||||
set_provider(d, providers)
|
||||
buf = providers[d[:provider]]
|
||||
|
||||
set_flavor(d, buf)
|
||||
set_image(d, buf)
|
||||
vpc_id = set_subnets(d, buf)
|
||||
set_groups(d, buf, vpc_id)
|
||||
set_users(d, buf)
|
||||
|
||||
unless self.options[:run_list].nil?
|
||||
self.options[:run_list] = self.options[:run_list].split(",").map{|e| e.strip}
|
||||
abort("Invalid run list: '#{self.options[:run_list].join(",")}'") unless Project.validate_run_list(self.options[:run_list])
|
||||
end
|
||||
set_parameter d, :run_list do
|
||||
set_run_list_cmd project, d[:identifier]
|
||||
end
|
||||
|
||||
unless self.options[:no_expires]
|
||||
set_parameter d, :expires do
|
||||
s = enter_parameter_or_empty(I18n.t("options.project.create.expires") + ": ").strip
|
||||
s.empty? ? nil : s
|
||||
end
|
||||
end
|
||||
d
|
||||
end
|
||||
|
||||
def create_project_multi_deploy_env_cmd project, providers, names
|
||||
d = {}
|
||||
set_identifier(d, names)
|
||||
|
||||
set_provider(d, providers)
|
||||
buf = providers[d[:provider]]
|
||||
|
||||
set_flavor(d, buf)
|
||||
set_image(d, buf)
|
||||
vpc_id = set_subnets(d, buf)
|
||||
set_groups(d, buf, vpc_id)
|
||||
set_users(d, buf)
|
||||
|
||||
unless self.options[:run_list].nil?
|
||||
self.options[:run_list] = self.options[:run_list].split(",").map{|e| e.strip}
|
||||
abort("Invalid run list: '#{self.options[:run_list].join(",")}'") unless Project.validate_run_list(self.options[:run_list])
|
||||
end
|
||||
set_parameter d, :run_list do
|
||||
set_run_list_cmd project, d[:identifier]
|
||||
end
|
||||
|
||||
unless self.options[:no_expires]
|
||||
set_parameter d, :expires do
|
||||
s = enter_parameter_or_empty(I18n.t("options.project.create.expires") + ": ").strip
|
||||
s.empty? ? nil : s
|
||||
end
|
||||
end
|
||||
d
|
||||
end
|
||||
|
||||
def set_identifier d, names
|
||||
set_parameter d, :identifier do
|
||||
begin
|
||||
n = enter_parameter I18n.t("handler.project.create.env") + ": "
|
||||
if names.include?(n)
|
||||
puts I18n.t("handler.project.create.env_exist", :env => n)
|
||||
raise ArgumentError
|
||||
else
|
||||
names.push n
|
||||
n
|
||||
end
|
||||
rescue ArgumentError
|
||||
retry
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def set_provider d, providers
|
||||
if providers[:obj].nil?
|
||||
providers[:obj], providers[:table] = get_providers
|
||||
providers[:obj].each{|p| providers[p] = {}}
|
||||
end
|
||||
|
||||
set_parameter d, :provider do
|
||||
providers[:obj][ choose_number_from_list(I18n.t("headers.provider"), providers[:obj], providers[:table]) ]
|
||||
end
|
||||
end
|
||||
|
||||
def set_flavor d, buf
|
||||
flavors, tf = nil, nil
|
||||
if buf[:flavors].nil?
|
||||
flavors, tf = get_flavors(d[:provider])
|
||||
add_object buf, :flavors, flavors, tf
|
||||
else
|
||||
flavors, tf = buf[:flavors][:obj], buf[:flavors][:table]
|
||||
end
|
||||
unless self.options[:flavor].nil?
|
||||
f = flavors.detect { |f| f["id"] == self.options[:flavor] }
|
||||
abort(I18n.t("handler.project.create.flavor.not_found")) if f.nil?
|
||||
end
|
||||
set_parameter d, :flavor do
|
||||
choose_flavor_cmd(flavors, tf)["id"]
|
||||
end
|
||||
end
|
||||
|
||||
def set_image d, buf
|
||||
images, ti = nil, nil
|
||||
if buf[:images].nil?
|
||||
images, ti = get_images(d[:provider])
|
||||
add_object buf, :images, images, ti
|
||||
else
|
||||
images, ti = buf[:images][:obj], buf[:images][:table]
|
||||
end
|
||||
set_parameter d, :image do
|
||||
choose_image_cmd(images, ti)["id"]
|
||||
end
|
||||
end
|
||||
|
||||
def set_subnets d, buf
|
||||
networks, tn = nil, nil
|
||||
if buf[:networks].nil?
|
||||
networks, tn = get_networks(d[:provider])
|
||||
add_object buf, :networks, networks, tn
|
||||
else
|
||||
networks, tn = buf[:networks][:obj], buf[:networks][:table]
|
||||
end
|
||||
unless self.options[:subnets].nil?
|
||||
if "ec2" == d[:provider]
|
||||
self.options[:subnets] = [ self.options[:subnets][0] ]
|
||||
end
|
||||
end
|
||||
vpc_id = nil
|
||||
set_parameter d, :subnets do
|
||||
if "ec2" == d[:provider]
|
||||
if networks.any?
|
||||
num = choose_number_from_list(I18n.t("handler.project.create.subnet.ec2"), networks, tn, -1)
|
||||
vpc_id = networks[num]["vpcId"] unless num == -1
|
||||
num == -1 ? [] : [ networks[num]["subnetId"] ]
|
||||
else
|
||||
[]
|
||||
end
|
||||
else
|
||||
s = []
|
||||
begin
|
||||
s = choose_indexes_from_list(I18n.t("handler.project.create.subnet.openstack"), networks, tn).map{|i| networks[i]["name"]}
|
||||
end while s.empty?
|
||||
s
|
||||
end
|
||||
end
|
||||
return vpc_id
|
||||
end
|
||||
|
||||
def set_groups d, buf, vpc_id
|
||||
groups, tg = nil, nil
|
||||
if buf[:groups].nil?
|
||||
groups, tg = get_groups(d[:provider], vpc_id)
|
||||
add_object buf, :groups, groups, tg
|
||||
else
|
||||
groups, tg = buf[:groups][:obj], buf[:groups][:table]
|
||||
end
|
||||
set_parameter d, :groups do
|
||||
list = groups.keys
|
||||
choose_indexes_from_list(I18n.t("options.project.create.groups"), list, tg, "default", list.index("default")).map{|i| list[i]}
|
||||
end
|
||||
end
|
||||
|
||||
def set_users d, buf
|
||||
users, tu = nil, nil
|
||||
if buf[:users].nil?
|
||||
users, tu = get_users
|
||||
add_object buf, :users, users, tu
|
||||
else
|
||||
users, tu = buf[:users][:obj], buf[:users][:table]
|
||||
end
|
||||
set_parameter d, :users do
|
||||
list = users.map{|u| u["id"]}
|
||||
Set.new choose_indexes_from_list(I18n.t("handler.project.create.user"), list, tu).map{|i| list[i]}
|
||||
end
|
||||
d[:users].add(self.options[:username])
|
||||
d[:users] = d[:users].to_a
|
||||
end
|
||||
|
||||
def add_object tec, key, obj, table
|
||||
tec[key] = {:obj => obj, :table => table}
|
||||
end
|
||||
|
||||
def set_parameter obj, key
|
||||
if self.options[key].nil?
|
||||
obj[key] = yield
|
||||
else
|
||||
obj[key] = self.options[key]
|
||||
end
|
||||
end
|
||||
|
||||
# returns flavor hash
|
||||
def choose_flavor_cmd flavors, table=nil
|
||||
abort(I18n.t("handler.flavor.list.empty")) if flavors.empty?
|
||||
flavors[ choose_number_from_list(I18n.t("headers.flavor"), flavors.map{|f| "#{f["id"]}. #{f["name"]} - #{f["ram"]}, #{f["disk"]}, #{f["v_cpus"]} CPU"}.join("\n"), table) ]
|
||||
end
|
||||
|
||||
# returns project id
|
||||
def choose_project projects, table=nil
|
||||
abort(I18n.t("handler.project.list.empty")) if projects.empty?
|
||||
projects[ choose_number_from_list(I18n.t("headers.project"), projects, table) ]
|
||||
end
|
||||
|
||||
# returns project env
|
||||
def choose_project_env project_envs, table=nil
|
||||
abort(I18n.t("handler.project.env.list.empty")) if project_envs.empty?
|
||||
project_envs[ choose_number_from_list(I18n.t("headers.project_env"), project_envs, table) ]
|
||||
end
|
||||
|
||||
def set_run_list_cmd project, env
|
||||
res = nil
|
||||
begin
|
||||
res = get_comma_separated_list(I18n.t("options.project.create.run_list") + ": ")
|
||||
end until Project.validate_run_list(res)
|
||||
res
|
||||
end
|
||||
|
||||
def self.validate_run_list run_list
|
||||
return true if run_list.empty?
|
||||
rl = /\Arole|recipe\[[\w-]+(::[\w-]+)?\]\Z/
|
||||
e = run_list.select {|l| (rl =~ l).nil?}
|
||||
res = e.empty?
|
||||
puts I18n.t("handler.project.create.run_list.invalid", :list => e.join(", ")) unless res
|
||||
res
|
||||
end
|
||||
|
||||
end
|
||||
35
devops-client/lib/devops-client/handler/provider.rb
Normal file
35
devops-client/lib/devops-client/handler/provider.rb
Normal file
@ -0,0 +1,35 @@
|
||||
require "devops-client/handler/handler"
|
||||
require "devops-client/options/provider_options"
|
||||
require "devops-client/output/provider"
|
||||
|
||||
class Provider < Handler
|
||||
|
||||
include Output::Provider
|
||||
|
||||
def initialize(host, def_options={})
|
||||
self.host = host
|
||||
self.options = def_options
|
||||
@options_parser = ProviderOptions.new(ARGV, def_options)
|
||||
end
|
||||
|
||||
def handle
|
||||
case ARGV[1]
|
||||
when "list"
|
||||
self.options = @options_parser.list_options
|
||||
list_handler @options_parser.args
|
||||
output
|
||||
else
|
||||
@options_parser.invalid_command
|
||||
end
|
||||
end
|
||||
|
||||
def list_handler args
|
||||
r = inspect_parameters @options_parser.list_params
|
||||
unless r.nil?
|
||||
@options_parser.invalid_list_command
|
||||
abort(r)
|
||||
end
|
||||
@list = get("/providers").sort!{|x,y| x["id"] <=> y["id"]}
|
||||
end
|
||||
|
||||
end
|
||||
84
devops-client/lib/devops-client/handler/script.rb
Normal file
84
devops-client/lib/devops-client/handler/script.rb
Normal file
@ -0,0 +1,84 @@
|
||||
require "devops-client/handler/handler"
|
||||
require "devops-client/options/script_options"
|
||||
require "devops-client/output/script"
|
||||
|
||||
class Script < Handler
|
||||
include Output::Script
|
||||
|
||||
def initialize(host, def_options={})
|
||||
self.host = host
|
||||
self.options = def_options
|
||||
@options_parser = ScriptOptions.new(ARGV, def_options)
|
||||
end
|
||||
|
||||
def handle
|
||||
case ARGV[1]
|
||||
when "list"
|
||||
self.options = @options_parser.list_options
|
||||
list_handler @options_parser.args
|
||||
output
|
||||
when "add"
|
||||
self.options = @options_parser.add_options
|
||||
add_handler @options_parser.args
|
||||
when "run"
|
||||
self.options = @options_parser.run_options
|
||||
run_handler @options_parser.args
|
||||
when "delete"
|
||||
self.options = @options_parser.delete_options
|
||||
delete_handler @options_parser.args
|
||||
when "command"
|
||||
self.options = @options_parser.command_options
|
||||
command_handler @options_parser.args
|
||||
else
|
||||
@options_parser.invalid_command
|
||||
end
|
||||
end
|
||||
|
||||
def command_handler args
|
||||
r = inspect_parameters @options_parser.command_params, args[2], args[3]
|
||||
unless r.nil?
|
||||
@options_parser.invalid_command_command
|
||||
abort(r)
|
||||
end
|
||||
post_chunk_body "/script/command/#{args[2]}", args[3], false
|
||||
end
|
||||
|
||||
def list_handler args
|
||||
@list = get("/scripts")
|
||||
end
|
||||
|
||||
def add_handler args
|
||||
r = inspect_parameters @options_parser.add_params, args[2], args[3]
|
||||
unless r.nil?
|
||||
@options_parser.invalid_add_command
|
||||
abort(r)
|
||||
end
|
||||
abort("File '#{args[3]}' does not exist") unless File.exists?(args[3])
|
||||
put_body "/script/#{args[2]}", File.read(args[3])
|
||||
end
|
||||
|
||||
def delete_handler args
|
||||
r = inspect_parameters @options_parser.delete_params, args[2]
|
||||
unless r.nil?
|
||||
@options_parser.invalid_delete_command
|
||||
abort(r)
|
||||
end
|
||||
if question(I18n.t("handler.script.question.delete", :name => args[2]))
|
||||
delete "/script/#{args[2]}"
|
||||
end
|
||||
end
|
||||
|
||||
def run_handler args
|
||||
r = inspect_parameters @options_parser.run_params, args[2], args[3]
|
||||
unless r.nil?
|
||||
@options_parser.invalid_run_command
|
||||
abort(r)
|
||||
end
|
||||
q = {
|
||||
:nodes => args[3..-1]
|
||||
}
|
||||
q[:params] = self.options[:params] unless self.options[:params].nil?
|
||||
post_chunk "/script/run/#{args[2]}", q
|
||||
end
|
||||
|
||||
end
|
||||
167
devops-client/lib/devops-client/handler/server.rb
Normal file
167
devops-client/lib/devops-client/handler/server.rb
Normal file
@ -0,0 +1,167 @@
|
||||
require "devops-client/handler/handler"
|
||||
require "devops-client/options/server_options"
|
||||
require "devops-client/output/server"
|
||||
require "devops-client/handler/project"
|
||||
|
||||
class Server < Handler
|
||||
|
||||
include Output::Server
|
||||
|
||||
def initialize(host, def_options={})
|
||||
self.host = host
|
||||
@options_parser = ServerOptions.new(ARGV, def_options)
|
||||
end
|
||||
|
||||
def handle
|
||||
case ARGV[1]
|
||||
when "list"
|
||||
self.options = @options_parser.list_options
|
||||
list_handler @options_parser.args
|
||||
output
|
||||
when "bootstrap"
|
||||
self.options = @options_parser.bootstrap_options
|
||||
bootstrap_handler @options_parser.args
|
||||
when "create"
|
||||
self.options = @options_parser.create_options
|
||||
create_handler @options_parser.args
|
||||
when "delete"
|
||||
self.options = @options_parser.delete_options
|
||||
delete_handler @options_parser.args
|
||||
when "show"
|
||||
self.options = @options_parser.show_options
|
||||
show_handler @options_parser.args
|
||||
output
|
||||
when "sync"
|
||||
self.options = @options_parser.sync_options
|
||||
sync_handler
|
||||
when "pause"
|
||||
self.options = @options_parser.pause_options
|
||||
pause_handler @options_parser.args
|
||||
when "unpause"
|
||||
self.options = @options_parser.unpause_options
|
||||
unpause_handler @options_parser.args
|
||||
when "add"
|
||||
self.options = @options_parser.add_options
|
||||
add_static_handler @options_parser.args
|
||||
else
|
||||
@options_parser.invalid_command
|
||||
end
|
||||
end
|
||||
|
||||
def list_handler args
|
||||
if args[2].nil?
|
||||
@list = get("/servers")
|
||||
return @list
|
||||
end
|
||||
self.options[:type] = args[2]
|
||||
@list = case args[2]
|
||||
when "chef"
|
||||
get("/servers/chef").map {|l| {"chef_node_name" => l}}
|
||||
when "ec2", "openstack"
|
||||
get("/servers/#{args[2]}")
|
||||
else
|
||||
@options_parser.invalid_list_command
|
||||
abort("Invlid argument '#{args[2]}'")
|
||||
end
|
||||
end
|
||||
|
||||
def create_handler args
|
||||
r = inspect_parameters @options_parser.create_params, args[2], args[3]
|
||||
unless r.nil?
|
||||
@options_parser.invalid_create_command
|
||||
abort(r)
|
||||
end
|
||||
|
||||
q = {
|
||||
:project => args[2],
|
||||
:deploy_env => args[3]
|
||||
}
|
||||
|
||||
[:key, :without_bootstrap, :name, :groups, :force].each do |k|
|
||||
q[k] = self.options[k] unless self.options[k].nil?
|
||||
end
|
||||
|
||||
post_chunk "/server", q
|
||||
end
|
||||
|
||||
def delete_handler args
|
||||
args[2..-1].each do |name|
|
||||
r = inspect_parameters @options_parser.delete_params, name
|
||||
unless r.nil?
|
||||
@options_parser.invalid_delete_command
|
||||
abort(r)
|
||||
end
|
||||
if question(I18n.t("handler.server.question.delete", :name => name))
|
||||
puts "Server '#{name}', deleting..."
|
||||
o = delete("/server/#{name}", options)
|
||||
["server", "chef_node", "chef_client", "message"].each do |k|
|
||||
puts o[k] unless o[k].nil?
|
||||
end
|
||||
end
|
||||
end
|
||||
""
|
||||
end
|
||||
|
||||
def show_handler args
|
||||
r = inspect_parameters @options_parser.show_params, args[2]
|
||||
unless r.nil?
|
||||
@options_parser.invalid_show_command
|
||||
abort r
|
||||
end
|
||||
@show = get("/server/#{args[2]}")
|
||||
end
|
||||
|
||||
def bootstrap_handler args
|
||||
r = inspect_parameters @options_parser.bootstrap_params, args[2]
|
||||
unless r.nil?
|
||||
@options_parser.invalid_bootstrap_command
|
||||
abort(r)
|
||||
end
|
||||
q = {
|
||||
:instance_id => args[2]
|
||||
}
|
||||
[:name, :bootstrap_template, :run_list].each do |k|
|
||||
q[k] = self.options[k] unless self.options[k].nil?
|
||||
end
|
||||
if q.has_key?(:run_list)
|
||||
abort unless Project.validate_run_list(q[:run_list])
|
||||
end
|
||||
post_chunk "/server/bootstrap", q
|
||||
end
|
||||
|
||||
def add_static_handler args # add <project> <env> <private_ip> <ssh_username> --public-ip <public_ip> -k <keyname>
|
||||
r = inspect_parameters @options_parser.add_params, args[2], args[3], args[4], args[5], args[6]
|
||||
unless r.nil?
|
||||
@options_parser.invalid_add_command
|
||||
abort(r)
|
||||
end
|
||||
q = {
|
||||
:project => args[2],
|
||||
:deploy_env => args[3],
|
||||
:private_ip => args[4],
|
||||
:remote_user => args[5],
|
||||
:key => args[6]
|
||||
}
|
||||
q[:public_ip] = self.options[:public_ip] unless self.options[:public_ip].nil?
|
||||
post_chunk "/server/add", q
|
||||
end
|
||||
|
||||
def pause_handler args
|
||||
r = inspect_parameters @options_parser.pause_params, args[2]
|
||||
unless r.nil?
|
||||
@options_parser.invalid_pause_command
|
||||
abort(r)
|
||||
end
|
||||
post "/server/#{args[2]}/pause"
|
||||
end
|
||||
|
||||
def unpause_handler args
|
||||
r = inspect_parameters @options_parser.unpause_params, args[2]
|
||||
unless r.nil?
|
||||
@options_parser.invalid_unpause_command
|
||||
abort(r)
|
||||
end
|
||||
post "/server/#{args[2]}/unpause"
|
||||
end
|
||||
|
||||
end
|
||||
64
devops-client/lib/devops-client/handler/tag.rb
Normal file
64
devops-client/lib/devops-client/handler/tag.rb
Normal file
@ -0,0 +1,64 @@
|
||||
require "devops-client/handler/handler"
|
||||
require "devops-client/options/tag_options"
|
||||
require "json"
|
||||
require "devops-client/output/tag"
|
||||
|
||||
class Tag < Handler
|
||||
include Output::Tag
|
||||
|
||||
def initialize(host, def_options={})
|
||||
self.host = host
|
||||
self.options = def_options
|
||||
@options_parser = TagOptions.new(ARGV, def_options)
|
||||
end
|
||||
|
||||
def handle
|
||||
case ARGV[1]
|
||||
when "list"
|
||||
self.options = @options_parser.list_options
|
||||
list_handler @options_parser.args
|
||||
output
|
||||
when "create"
|
||||
self.options = @options_parser.create_options
|
||||
create_handler @options_parser.args
|
||||
when "delete"
|
||||
self.options = @options_parser.delete_options
|
||||
delete_handler @options_parser.args
|
||||
else
|
||||
@options_parser.invalid_command
|
||||
end
|
||||
end
|
||||
|
||||
def list_handler args
|
||||
r = inspect_parameters @options_parser.list_params, args[2]
|
||||
unless r.nil?
|
||||
@options_parser.invalid_list_command
|
||||
abort(r)
|
||||
end
|
||||
@list = get("/tags/#{args[2]}")
|
||||
end
|
||||
|
||||
def create_handler args
|
||||
if args.length == 3
|
||||
@options_parser.invalid_create_command
|
||||
abort()
|
||||
end
|
||||
node = args[2]
|
||||
tags = args[3..-1]
|
||||
|
||||
post "/tags/#{node}", tags
|
||||
end
|
||||
|
||||
def delete_handler args
|
||||
if args.length == 3
|
||||
@options_parser.invalid_delete_command
|
||||
abort()
|
||||
end
|
||||
node = args[2]
|
||||
tags = args[3..-1]
|
||||
|
||||
if question(I18n.t("handler.user.question.delete", :name => tags.join("', '"), :node => node))
|
||||
delete "/tags/#{node}", tags
|
||||
end
|
||||
end
|
||||
end
|
||||
129
devops-client/lib/devops-client/handler/user.rb
Normal file
129
devops-client/lib/devops-client/handler/user.rb
Normal file
@ -0,0 +1,129 @@
|
||||
require "devops-client/handler/handler"
|
||||
require "devops-client/options/user_options"
|
||||
require "devops-client/output/user"
|
||||
|
||||
class User < Handler
|
||||
include Output::User
|
||||
|
||||
def initialize(host, def_options={})
|
||||
self.host = host
|
||||
self.options = def_options
|
||||
@options_parser = UserOptions.new(ARGV, def_options)
|
||||
end
|
||||
|
||||
def handle
|
||||
case ARGV[1]
|
||||
when "list"
|
||||
self.options = @options_parser.list_options
|
||||
list_handler
|
||||
output
|
||||
when "create"
|
||||
self.options = @options_parser.create_options
|
||||
create_handler @options_parser.args
|
||||
when "delete"
|
||||
self.options = @options_parser.delete_options
|
||||
delete_handler @options_parser.args
|
||||
when "grant"
|
||||
self.options = @options_parser.grant_options
|
||||
grant_handler @options_parser.args
|
||||
when "password"
|
||||
self.options = @options_parser.password_options
|
||||
password_handler @options_parser.args
|
||||
when "email"
|
||||
self.options = @options_parser.email_options
|
||||
email_handler @options_parser.args
|
||||
else
|
||||
@options_parser.invalid_command
|
||||
end
|
||||
end
|
||||
|
||||
def list_handler
|
||||
@list = get("/users")
|
||||
end
|
||||
|
||||
def create_handler args
|
||||
r = inspect_parameters @options_parser.create_params, args[2], args[3]
|
||||
unless r.nil?
|
||||
@options_parser.invalid_create_command
|
||||
abort(r)
|
||||
end
|
||||
|
||||
password = self.options[:new_password] || enter_password(args[2])
|
||||
|
||||
q = {
|
||||
"username" => args[2],
|
||||
"password" => password,
|
||||
"email" => args[3]
|
||||
}
|
||||
post "/user", q
|
||||
end
|
||||
|
||||
def delete_handler args
|
||||
r = inspect_parameters @options_parser.delete_params, args[2]
|
||||
unless r.nil?
|
||||
@options_parser.invalid_delete_command
|
||||
abort(r)
|
||||
end
|
||||
|
||||
if question(I18n.t("handler.user.question.delete", :name => args[2]))
|
||||
delete "/user/#{args[2]}"
|
||||
end
|
||||
end
|
||||
|
||||
def password_handler args
|
||||
r = inspect_parameters @options_parser.password_params, args[2]
|
||||
unless r.nil?
|
||||
@options_parser.invalid_password_command
|
||||
abort(r)
|
||||
end
|
||||
|
||||
password = enter_password(args[2])
|
||||
q = {
|
||||
"password" => password
|
||||
}
|
||||
|
||||
put "/user/#{args[2]}/password", q
|
||||
end
|
||||
|
||||
def email_handler args
|
||||
r = inspect_parameters @options_parser.email_params, args[2], args[3]
|
||||
unless r.nil?
|
||||
@options_parser.invalid_email_command
|
||||
abort(r)
|
||||
end
|
||||
q = {
|
||||
"email" => args[3]
|
||||
}
|
||||
put "/user/#{args[2]}/email", q
|
||||
end
|
||||
|
||||
def grant_handler args
|
||||
r = inspect_parameters @options_parser.grant_params, args[2], args[3], args[4]
|
||||
unless r.nil?
|
||||
@options_parser.invalid_grant_command
|
||||
abort(r)
|
||||
end
|
||||
|
||||
args[3] = '' if args[3].nil?
|
||||
q = {
|
||||
'cmd' => args[3],
|
||||
'privileges' => args[4]
|
||||
}
|
||||
|
||||
put "/user/#{args[2]}", q
|
||||
end
|
||||
|
||||
def enter_password user
|
||||
print "Enter password for '#{user}': "
|
||||
password = ""
|
||||
begin
|
||||
system("stty -echo")
|
||||
password = STDIN.gets.strip
|
||||
puts
|
||||
ensure
|
||||
system("stty echo")
|
||||
end
|
||||
password
|
||||
end
|
||||
|
||||
end
|
||||
53
devops-client/lib/devops-client/i18n.rb
Normal file
53
devops-client/lib/devops-client/i18n.rb
Normal file
@ -0,0 +1,53 @@
|
||||
module I18n
|
||||
|
||||
@@lang = {}
|
||||
|
||||
def self.language= locale
|
||||
spec = Gem::Specification.find_by_name(DevopsClient::NAME)
|
||||
gem_root = spec.gem_dir
|
||||
path = File.join(gem_root, "locales", "#{locale}.yml")
|
||||
raise ArgumentError.new("Invalid locale '#{locale}'") unless File.exist?(path)
|
||||
require 'yaml'
|
||||
begin
|
||||
@@lang = YAML.load_file(path)[locale]
|
||||
rescue
|
||||
raise ArgumentError.new("Invalid file '#{locale}.yml'")
|
||||
end
|
||||
end
|
||||
|
||||
def self.t label, options={}
|
||||
path = label.split(".")
|
||||
buf = @@lang
|
||||
begin
|
||||
path.each do |index|
|
||||
buf = buf[index]
|
||||
end
|
||||
raise ArgumentError unless buf.is_a?(String)
|
||||
rescue
|
||||
return "Translation missing"
|
||||
end
|
||||
options.each do |k,v|
|
||||
buf.gsub!("%{#{k.to_s}}", v.to_s)
|
||||
end
|
||||
buf
|
||||
end
|
||||
|
||||
def self.locales
|
||||
spec = Gem::Specification.find_by_name(DevopsClient::NAME)
|
||||
gem_root = spec.gem_dir
|
||||
path = File.join(gem_root, "locales")
|
||||
locales = []
|
||||
Dir.foreach(path) do |item|
|
||||
next if item.start_with? '.'
|
||||
if item.end_with? ".yml"
|
||||
locales.push item.split(".")[0]
|
||||
end
|
||||
end
|
||||
locales
|
||||
end
|
||||
|
||||
def self.lang
|
||||
@@lang
|
||||
end
|
||||
|
||||
end
|
||||
3
devops-client/lib/devops-client/name.rb
Normal file
3
devops-client/lib/devops-client/name.rb
Normal file
@ -0,0 +1,3 @@
|
||||
module DevopsClient
|
||||
NAME = "devops-client"
|
||||
end
|
||||
@ -0,0 +1,13 @@
|
||||
require "devops-client/options/common_options"
|
||||
|
||||
class BootstrapTemplatesOptions < CommonOptions
|
||||
|
||||
commands :list
|
||||
|
||||
def initialize args, def_options
|
||||
super(args, def_options)
|
||||
self.header = I18n.t("headers.template")
|
||||
self.banner_header = "templates"
|
||||
end
|
||||
|
||||
end
|
||||
170
devops-client/lib/devops-client/options/common_options.rb
Normal file
170
devops-client/lib/devops-client/options/common_options.rb
Normal file
@ -0,0 +1,170 @@
|
||||
require "optparse"
|
||||
require "devops-client/version"
|
||||
|
||||
class CommonOptions
|
||||
|
||||
attr_accessor :header, :args, :default_options
|
||||
attr_writer :banner_header
|
||||
|
||||
TABLE_FORMAT = "table"
|
||||
JSON_FORMAT = "json"
|
||||
CSV_FORMAT = "csv"
|
||||
OUTPUT_FROMATS = [TABLE_FORMAT, JSON_FORMAT, CSV_FORMAT]
|
||||
|
||||
def initialize args, def_options
|
||||
self.args = args
|
||||
self.default_options = def_options
|
||||
end
|
||||
|
||||
def self.commands *cmds
|
||||
cmds.each do |cmd|
|
||||
if cmd.is_a?(Hash)
|
||||
key = cmd.keys[0]
|
||||
cmd[key].each do |subcmd|
|
||||
create_command key.to_s, subcmd.to_s
|
||||
end
|
||||
invalid_command_method = "invalid_#{key}_command"
|
||||
banner_method = "#{key}_banner"
|
||||
|
||||
define_method invalid_command_method do
|
||||
puts "#{self.header}:\n#{self.send(banner_method)}"
|
||||
end
|
||||
|
||||
define_method banner_method do
|
||||
cmd[key].map{|sc| self.send("#{key}_#{sc}_banner")}.join("") + "\n"
|
||||
end
|
||||
else
|
||||
create_command cmd.to_s
|
||||
end
|
||||
end
|
||||
|
||||
define_method "banners" do
|
||||
r = []
|
||||
cmds.each do |cmd|
|
||||
if cmd.is_a?(Hash)
|
||||
key = cmd.keys[0]
|
||||
cmd[key].each do |subcmd|
|
||||
r.push self.send("#{key}_#{subcmd}_banner")
|
||||
end
|
||||
else
|
||||
r.push self.send("#{cmd.to_s}_banner")
|
||||
end
|
||||
end
|
||||
r
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def self.create_command cmd, subcmd=nil
|
||||
name = (subcmd.nil? ? cmd : "#{cmd}_#{subcmd}")
|
||||
banner = (subcmd.nil? ? cmd : "#{cmd} #{subcmd}")
|
||||
|
||||
invalid_command_method = "invalid_#{name}_command"
|
||||
banner_method = "#{name}_banner"
|
||||
|
||||
define_method invalid_command_method do
|
||||
puts "#{self.header}:\n#{self.send(banner_method)}"
|
||||
end
|
||||
|
||||
params_method = "#{name}_params"
|
||||
define_method banner_method do
|
||||
self.banner_header + " #{banner} #{(self.send(params_method) || []).join(" ")}\n"
|
||||
end
|
||||
|
||||
options_method = "#{name}_options"
|
||||
define_method options_method do
|
||||
self.options do |opts, options|
|
||||
opts.banner << self.send(banner_method)
|
||||
end
|
||||
end
|
||||
|
||||
attr_accessor params_method
|
||||
|
||||
end
|
||||
|
||||
def options
|
||||
o = {}
|
||||
optparse = OptionParser.new do |opts|
|
||||
|
||||
opts.banner = "\n" + I18n.t("options.usage", :cmd => $0) + "\n\n" + I18n.t("options.commands") + ":\n"
|
||||
|
||||
if block_given?
|
||||
opts.separator(I18n.t("options.options") + ":\n")
|
||||
yield opts, o
|
||||
end
|
||||
|
||||
opts.separator("\n" + I18n.t("options.common_options") + ":\n")
|
||||
opts.on("-h", "--help", I18n.t("options.common.help")) do
|
||||
opts.banner << "\n"
|
||||
puts opts
|
||||
exit
|
||||
end
|
||||
|
||||
o[:no_ask] = false
|
||||
opts.on("-y", "--assumeyes", I18n.t("options.common.confirmation")) do
|
||||
o[:no_ask] = true;
|
||||
end
|
||||
|
||||
#Not used, just for banner purposes. This should be fixed when we find how to deal with options separetely
|
||||
opts.on("-c", "--config CONFIG", I18n.t("options.common.config", :file => DevopsClient.config_file)) do
|
||||
puts "Not implemented yet"
|
||||
exit
|
||||
end
|
||||
|
||||
opts.on("-v", "--version", I18n.t("options.common.version")) do
|
||||
puts I18n.t("options.common.version") + ": #{DevopsClient::VERSION}"
|
||||
exit
|
||||
end
|
||||
|
||||
opts.on("--host HOST", I18n.t("options.common.host", :host => default_options[:host])) do |h|
|
||||
o[:host] = h
|
||||
end
|
||||
|
||||
o[:api] = default_options[:api]
|
||||
opts.on("--api VER", I18n.t("options.common.api", :api => o[:api])) do |a|
|
||||
o[:api] = a
|
||||
end
|
||||
|
||||
o[:username] = default_options[:username]
|
||||
opts.on("--user USERNAME", I18n.t("options.common.username", :username => o[:username])) do |u|
|
||||
o[:username] = u.strip
|
||||
print I18n.t("handler.user.password_for", :user => o[:username])
|
||||
begin
|
||||
system("stty -echo")
|
||||
o[:password] = STDIN.gets.strip
|
||||
ensure
|
||||
system("stty echo")
|
||||
end
|
||||
puts
|
||||
end
|
||||
|
||||
o[:format] = TABLE_FORMAT
|
||||
opts.on("--format FORMAT", I18n.t("options.common.format", :formats => OUTPUT_FROMATS.join("', '"), :format => TABLE_FORMAT)) do |f|
|
||||
o[:format] = f if OUTPUT_FROMATS.include?(f)
|
||||
end
|
||||
|
||||
# should be handled in lib/devops-client.rb
|
||||
opts.on("", "--completion", I18n.t("options.common.completion"))
|
||||
|
||||
end
|
||||
optparse.parse!(self.args)
|
||||
o
|
||||
end
|
||||
|
||||
def invalid_command
|
||||
options do |opts, options|
|
||||
opts.banner << self.error_banner
|
||||
puts opts.banner
|
||||
exit(2)
|
||||
end
|
||||
end
|
||||
|
||||
def error_banner
|
||||
"\t#{self.header}:\n\t#{self.banners.join("\t")}\n"
|
||||
end
|
||||
|
||||
def banner_header
|
||||
"\t" + @banner_header
|
||||
end
|
||||
|
||||
end
|
||||
36
devops-client/lib/devops-client/options/deploy_options.rb
Normal file
36
devops-client/lib/devops-client/options/deploy_options.rb
Normal file
@ -0,0 +1,36 @@
|
||||
require "devops-client/options/common_options"
|
||||
|
||||
class DeployOptions < CommonOptions
|
||||
|
||||
attr_accessor :deploy_params
|
||||
|
||||
def initialize args, def_options
|
||||
super(args, def_options)
|
||||
self.header = I18n.t("headers.deploy")
|
||||
# self.deploy_params = ["PROJECT_ID", "DEPLOY_ENV"]
|
||||
end
|
||||
|
||||
def deploy_options
|
||||
options do |opts, options|
|
||||
opts.banner << self.banner
|
||||
|
||||
options[:tag] = nil
|
||||
opts.on("--tag TAG1,TAG2...", "Tag names, comma separated list") do |tags|
|
||||
options[:tags] = tags.split(",")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def banners
|
||||
[ self.banner ]
|
||||
end
|
||||
|
||||
def banner
|
||||
"\tdeploy NODE_NAME [NODE_NAME ...]\n"
|
||||
end
|
||||
|
||||
def invalid_deploy_command
|
||||
puts "#{self.header}:\n#{self.banner}"
|
||||
end
|
||||
|
||||
end
|
||||
18
devops-client/lib/devops-client/options/filter_options.rb
Normal file
18
devops-client/lib/devops-client/options/filter_options.rb
Normal file
@ -0,0 +1,18 @@
|
||||
require "optparse"
|
||||
require "devops-client/options/common_options"
|
||||
|
||||
class FilterOptions < CommonOptions
|
||||
|
||||
commands :image => [:add, :delete, :list]
|
||||
|
||||
def initialize args, def_options
|
||||
super(args, def_options)
|
||||
self.header = I18n.t("headers.filters")
|
||||
self.banner_header = "filter"
|
||||
p = "PROVIDER"
|
||||
self.image_list_params = [p]
|
||||
i = "IMAGE [IMAGE ...]"
|
||||
self.image_add_params = [p, i]
|
||||
self.image_delete_params = [p, i]
|
||||
end
|
||||
end
|
||||
15
devops-client/lib/devops-client/options/flavor_options.rb
Normal file
15
devops-client/lib/devops-client/options/flavor_options.rb
Normal file
@ -0,0 +1,15 @@
|
||||
require "optparse"
|
||||
require "devops-client/options/common_options"
|
||||
|
||||
class FlavorOptions < CommonOptions
|
||||
|
||||
commands :list
|
||||
|
||||
def initialize args, def_options
|
||||
super(args, def_options)
|
||||
self.header = I18n.t("headers.flavor")
|
||||
self.banner_header = "flavor"
|
||||
self.list_params = ["PROVIDER"]
|
||||
end
|
||||
|
||||
end
|
||||
15
devops-client/lib/devops-client/options/group_options.rb
Normal file
15
devops-client/lib/devops-client/options/group_options.rb
Normal file
@ -0,0 +1,15 @@
|
||||
require "optparse"
|
||||
require "devops-client/options/common_options"
|
||||
|
||||
class GroupOptions < CommonOptions
|
||||
|
||||
commands :list
|
||||
|
||||
def initialize args, def_options
|
||||
super(args, def_options)
|
||||
self.header = I18n.t("headers.group")
|
||||
self.banner_header = "group"
|
||||
self.list_params = ["PROVIDER", "[VPC-ID]"]
|
||||
end
|
||||
|
||||
end
|
||||
50
devops-client/lib/devops-client/options/image_options.rb
Normal file
50
devops-client/lib/devops-client/options/image_options.rb
Normal file
@ -0,0 +1,50 @@
|
||||
require "devops-client/options/common_options"
|
||||
|
||||
class ImageOptions < CommonOptions
|
||||
|
||||
commands :create, :delete, :list, :show, :update
|
||||
|
||||
def initialize args, def_options
|
||||
super(args, def_options)
|
||||
self.header = I18n.t("headers.image")
|
||||
self.banner_header = "image"
|
||||
self.list_params = ["[provider]", "[ec2|openstack]"]
|
||||
self.show_params = ["IMAGE"]
|
||||
self.delete_params = ["IMAGE"]
|
||||
self.update_params = ["IMAGE", "FILE"]
|
||||
end
|
||||
|
||||
def create_options
|
||||
|
||||
self.options do |opts, options|
|
||||
opts.banner << self.create_banner
|
||||
|
||||
options[:provider] = nil
|
||||
opts.on("--provider PROVIDER", "Image provider") do |provider|
|
||||
options[:provider] = provider
|
||||
end
|
||||
|
||||
options[:image_id] = nil
|
||||
opts.on("--image IMAGE_ID", "Image identifier") do |image_id|
|
||||
options[:image_id] = image_id
|
||||
end
|
||||
|
||||
options[:ssh_username] = nil
|
||||
opts.on("--ssh_user USER", "SSH user name") do |username|
|
||||
options[:ssh_username] = username
|
||||
end
|
||||
|
||||
options[:bootstrap_template] = nil
|
||||
opts.on("--bootstrap_template TEMPLATE", "Bootstrap template") do |template|
|
||||
options[:bootstrap_template] = template
|
||||
end
|
||||
|
||||
options[:no_bootstrap_template] = false
|
||||
opts.on("--no_bootstrap_template", "Do not specify bootstrap template") do
|
||||
options[:no_bootstrap_template] = true
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
14
devops-client/lib/devops-client/options/key_options.rb
Normal file
14
devops-client/lib/devops-client/options/key_options.rb
Normal file
@ -0,0 +1,14 @@
|
||||
require "devops-client/options/common_options"
|
||||
|
||||
class KeyOptions < CommonOptions
|
||||
commands :add, :delete, :list
|
||||
|
||||
def initialize args, def_options
|
||||
super(args, def_options)
|
||||
self.header = I18n.t("headers.key")
|
||||
self.banner_header = "key"
|
||||
self.add_params = ["KEY_NAME", "FILE"]
|
||||
self.delete_params = ["KEY_NAME"]
|
||||
end
|
||||
|
||||
end
|
||||
46
devops-client/lib/devops-client/options/main.rb
Normal file
46
devops-client/lib/devops-client/options/main.rb
Normal file
@ -0,0 +1,46 @@
|
||||
require "optparse"
|
||||
require "devops-client/options/server_options"
|
||||
require "devops-client/options/image_options"
|
||||
require "devops-client/options/project_options"
|
||||
require "devops-client/options/provider_options"
|
||||
require "devops-client/options/flavor_options"
|
||||
require "devops-client/options/common_options"
|
||||
require "devops-client/options/group_options"
|
||||
require "devops-client/options/deploy_options"
|
||||
require "devops-client/options/key_options"
|
||||
require "devops-client/options/user_options"
|
||||
require "devops-client/options/tag_options"
|
||||
require "devops-client/options/script_options"
|
||||
require "devops-client/options/filter_options"
|
||||
require "devops-client/options/network_options"
|
||||
require "devops-client/options/bootstrap_templates_options"
|
||||
|
||||
class Main < CommonOptions
|
||||
|
||||
def initialize args, def_options
|
||||
super(args, def_options)
|
||||
end
|
||||
|
||||
def info
|
||||
o = nil
|
||||
options do |opts, options|
|
||||
opts.banner << BootstrapTemplatesOptions.new(ARGV, default_options).error_banner
|
||||
opts.banner << DeployOptions.new(ARGV, default_options).error_banner
|
||||
opts.banner << FilterOptions.new(ARGV, default_options).error_banner
|
||||
opts.banner << FlavorOptions.new(ARGV, default_options).error_banner
|
||||
opts.banner << GroupOptions.new(ARGV, default_options).error_banner
|
||||
opts.banner << ImageOptions.new(ARGV, default_options).error_banner
|
||||
opts.banner << KeyOptions.new(ARGV, default_options).error_banner
|
||||
opts.banner << NetworkOptions.new(ARGV, default_options).error_banner
|
||||
opts.banner << ProjectOptions.new(ARGV, default_options).error_banner
|
||||
opts.banner << ProviderOptions.new(ARGV, default_options).error_banner
|
||||
opts.banner << ScriptOptions.new(ARGV, default_options).error_banner
|
||||
opts.banner << ServerOptions.new(ARGV, default_options).error_banner
|
||||
opts.banner << TagOptions.new(ARGV, default_options).error_banner
|
||||
opts.banner << UserOptions.new(ARGV, default_options).error_banner
|
||||
o = opts
|
||||
end
|
||||
puts(o.banner + "\n")
|
||||
end
|
||||
|
||||
end
|
||||
16
devops-client/lib/devops-client/options/network_options.rb
Normal file
16
devops-client/lib/devops-client/options/network_options.rb
Normal file
@ -0,0 +1,16 @@
|
||||
require "optparse"
|
||||
require "devops-client/options/common_options"
|
||||
|
||||
class NetworkOptions < CommonOptions
|
||||
|
||||
commands :list
|
||||
|
||||
def initialize args, def_options
|
||||
super(args, def_options)
|
||||
self.header = I18n.t("headers.network")
|
||||
self.banner_header = "network"
|
||||
self.list_params = ["PROVIDER"]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
119
devops-client/lib/devops-client/options/project_options.rb
Normal file
119
devops-client/lib/devops-client/options/project_options.rb
Normal file
@ -0,0 +1,119 @@
|
||||
require "devops-client/options/common_options"
|
||||
require "set"
|
||||
|
||||
class ProjectOptions < CommonOptions
|
||||
|
||||
commands :create, :delete, :deploy, :list, {:multi => [:create]}, :servers, {:set => [:run_list]}, :show, :test, :update, {:user => [:add, :delete]}
|
||||
|
||||
def initialize args, def_options
|
||||
super(args, def_options)
|
||||
self.header = I18n.t("headers.project")
|
||||
self.banner_header = "project"
|
||||
id = "PROJECT_ID"
|
||||
env = "DEPLOY_ENV"
|
||||
self.show_params = [id]
|
||||
self.create_params = [id]
|
||||
self.delete_params = [id, "[#{env}]"]
|
||||
self.deploy_params = [id, "[#{env}]"]
|
||||
self.set_run_list_params = [id, env, "[(recipe[mycookbook::myrecipe])|(role[myrole]) ...]"]
|
||||
self.servers_params = [id, "[#{env}]"]
|
||||
self.multi_create_params = [id]
|
||||
self.update_params = [id, "FILE"]
|
||||
self.user_add_params = [id, "USER_NAME", "[USER_NAME ...]"]
|
||||
self.user_delete_params = [id, "USER_NAME", "[USER_NAME ...]"]
|
||||
self.test_params = [id, env]
|
||||
end
|
||||
|
||||
def create_options
|
||||
self.options do |opts, options|
|
||||
opts.banner << self.create_banner
|
||||
options[:groups] = nil
|
||||
opts.on("--groups GROUP_1,GROUP_2...", I18n.t("options.project.create.groups")) do |groups|
|
||||
options[:groups] = groups.split(",")
|
||||
end
|
||||
|
||||
options[:identifier] = nil
|
||||
opts.on("--deploy_env DEPLOY_ID", I18n.t("options.project.create.deploy_env")) do |identifier|
|
||||
options[:identifier] = identifier
|
||||
end
|
||||
|
||||
options[:file] = nil
|
||||
opts.on("-f", "--file FILE", I18n.t("options.project.create.file")) do |file|
|
||||
abort("File '#{file}' does not exist") unless File.exist?(file)
|
||||
options[:file] = file
|
||||
end
|
||||
|
||||
options[:subnets] = nil
|
||||
opts.on("--subnets SUBNET,SUBNET...", I18n.t("options.project.create.subnets")) do |subnet|
|
||||
options[:subnets] = subnet.split(",")
|
||||
end
|
||||
|
||||
options[:flavor] = nil
|
||||
opts.on("--flavor FLAVOR", I18n.t("options.project.create.flavor")) do |flavor|
|
||||
options[:flavor] = flavor
|
||||
end
|
||||
|
||||
options[:image] = nil
|
||||
opts.on("--image IMAGE_ID", I18n.t("options.project.create.image")) do |image|
|
||||
options[:image] = image
|
||||
end
|
||||
|
||||
options[:run_list] = nil
|
||||
opts.on("--run_list RUN_LIST", I18n.t("options.project.create.run_list")) do |run_list|
|
||||
options[:run_list] = run_list
|
||||
end
|
||||
|
||||
options[:users] = nil
|
||||
opts.on("--users USER,USER...", I18n.t("options.project.create.users")) do |users|
|
||||
options[:users] = Set.new(users.split(","))
|
||||
end
|
||||
|
||||
options[:provider] = nil
|
||||
opts.on("--provider PROVIDER", I18n.t("options.project.create.provider")) do |provider|
|
||||
options[:provider] = provider
|
||||
end
|
||||
|
||||
options[:no_expires] = false
|
||||
opts.on("--no_expires", I18n.t("options.project.create.no_expires")) do
|
||||
options[:no_expires] = true
|
||||
end
|
||||
|
||||
options[:expires] = nil
|
||||
opts.on("--expires EXPIRES", I18n.t("options.project.create.expires")) do |e|
|
||||
options[:expires] = e
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def user_add_options
|
||||
self.options do |opts, options|
|
||||
opts.banner << self.user_add_banner
|
||||
options[:deploy_env] = nil
|
||||
opts.on("--deploy_env ENV", I18n.t("options.project.user_add.deploy_env")) do |env|
|
||||
options[:deploy_env] = env
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def user_delete_options
|
||||
self.options do |opts, options|
|
||||
opts.banner << self.user_delete_banner
|
||||
options[:deploy_env] = nil
|
||||
opts.on("--deploy_env ENV", I18n.t("options.project.user_delete.deploy_env")) do |env|
|
||||
options[:deploy_env] = env
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def deploy_options
|
||||
options do |opts, options|
|
||||
opts.banner << self.deploy_banner
|
||||
options[:servers] = nil
|
||||
opts.on("--servers SERVERS", I18n.t("options.project.deploy.servers")) do |l|
|
||||
options[:servers] = l.split(",")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
15
devops-client/lib/devops-client/options/provider_options.rb
Normal file
15
devops-client/lib/devops-client/options/provider_options.rb
Normal file
@ -0,0 +1,15 @@
|
||||
require "optparse"
|
||||
require "devops-client/options/common_options"
|
||||
|
||||
class ProviderOptions < CommonOptions
|
||||
|
||||
commands :list
|
||||
|
||||
def initialize args, def_options
|
||||
super(args, def_options)
|
||||
self.header = I18n.t("headers.provider")
|
||||
self.banner_header = "provider"
|
||||
self.list_params = []
|
||||
end
|
||||
|
||||
end
|
||||
27
devops-client/lib/devops-client/options/script_options.rb
Normal file
27
devops-client/lib/devops-client/options/script_options.rb
Normal file
@ -0,0 +1,27 @@
|
||||
require "devops-client/options/common_options"
|
||||
|
||||
class ScriptOptions < CommonOptions
|
||||
|
||||
commands :list, :add, :delete, :run, :command
|
||||
|
||||
def initialize args, def_options
|
||||
super(args, def_options)
|
||||
self.header = I18n.t("headers.script")
|
||||
self.banner_header = "script"
|
||||
sname = "SCRIPT_NAME"
|
||||
self.add_params = [sname, "FILE"]
|
||||
self.delete_params = [sname]
|
||||
self.run_params = [sname, "NODE_NAME", "[NODE_NAME ...]"]
|
||||
self.command_params = ["NODE_NAME", "'sh command'"]
|
||||
end
|
||||
|
||||
def run_options
|
||||
options do |opts, options|
|
||||
opts.banner << self.delete_banner
|
||||
opts.on("--params PARAMS", I18n.t("options.script.run.params")) do |p|
|
||||
options[:params] = p.split(",")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
101
devops-client/lib/devops-client/options/server_options.rb
Normal file
101
devops-client/lib/devops-client/options/server_options.rb
Normal file
@ -0,0 +1,101 @@
|
||||
require "devops-client/options/common_options"
|
||||
|
||||
class ServerOptions < CommonOptions
|
||||
|
||||
commands :add, :bootstrap, :create, :delete, :list, :pause, :show, :unpause # :sync,
|
||||
|
||||
def initialize args, def_options
|
||||
super(args, def_options)
|
||||
self.header = I18n.t("headers.server")
|
||||
self.banner_header = "server"
|
||||
self.list_params = ["[chef|ec2|openstack]"]
|
||||
self.create_params = ["PROJECT_ID", "DEPLOY_ENV"]
|
||||
node_params = ["NODE_NAME"]
|
||||
self.delete_params = node_params
|
||||
self.show_params = node_params
|
||||
self.pause_params = node_params
|
||||
self.unpause_params = node_params
|
||||
self.bootstrap_params = ["INSTANCE_ID"]
|
||||
self.add_params = ["PROJECT_ID", "DEPLOY_ENV", "IP", "SSH_USER", "KEY_ID"]
|
||||
end
|
||||
|
||||
def delete_options
|
||||
options do |opts, options|
|
||||
opts.banner << self.delete_banner
|
||||
options[:key] = "node"
|
||||
opts.on('--instance', "Delete node by instance id") do
|
||||
options[:key] = "instance"
|
||||
end
|
||||
|
||||
options[:no_ask] = false
|
||||
opts.on("--no_ask", "Don't ask for permission for server deletion") do
|
||||
options[:no_ask] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def create_options
|
||||
options do |opts, options|
|
||||
opts.banner << self.create_banner
|
||||
opts.on('--without-bootstrap', "Run server without bootsraping phase") do
|
||||
options[:without_bootstrap] = true
|
||||
end
|
||||
|
||||
=begin
|
||||
opts.on('--public-ip', "Associate public IP with server") do
|
||||
options[:public_ip] = true
|
||||
end
|
||||
=end
|
||||
|
||||
opts.on("-N", "--name NAME", "Set node name") do |n|
|
||||
options[:name] = n
|
||||
end
|
||||
|
||||
opts.on("-G", "--groups X,Y,Z", "The security groups for this server") do |g|
|
||||
options[:groups] = g.split(",")
|
||||
end
|
||||
|
||||
opts.on("-f", "--force", "Cancel rollback operation on error") do |f|
|
||||
options[:force] = true
|
||||
end
|
||||
|
||||
opts.on("--key KEY", "Use another key for the server") do |k|
|
||||
options[:key] = k
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
def bootstrap_options
|
||||
options do |opts, options|
|
||||
opts.banner << self.bootstrap_banner
|
||||
|
||||
opts.on("-N", "--name NAME", "Set chef name") do |n|
|
||||
options[:name] = n
|
||||
end
|
||||
|
||||
opts.on("--bootstrap_template TEMPLATE", "Bootstrap template") do |template|
|
||||
options[:bootstrap_template] = template
|
||||
end
|
||||
|
||||
opts.on("--run_list LIST", "Comma separated list like 'role[my_role],recipe[my_recipe]'") do |list|
|
||||
options[:run_list] = list.split(",")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def add_options
|
||||
options do |opts, options|
|
||||
opts.banner << self.add_banner
|
||||
|
||||
opts.on('--public-ip PUBLIC_IP', "Specify public IP for the server") do |ip|
|
||||
options[:public_ip] = ip
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def delete_banner
|
||||
self.banner_header + " delete NODE_NAME [NODE_NAME ...]\n"
|
||||
end
|
||||
|
||||
end
|
||||
14
devops-client/lib/devops-client/options/tag_options.rb
Normal file
14
devops-client/lib/devops-client/options/tag_options.rb
Normal file
@ -0,0 +1,14 @@
|
||||
require "devops-client/options/common_options"
|
||||
|
||||
class TagOptions < CommonOptions
|
||||
commands :create, :delete, :list
|
||||
|
||||
def initialize args, def_options
|
||||
super(args, def_options)
|
||||
self.header = I18n.t("headers.tag")
|
||||
self.banner_header = "tag"
|
||||
self.create_params = ["NODE_NAME", "TAG_NAME", "[TAG_NAME ...]"]
|
||||
self.delete_params = ["NODE_NAME", "TAG_NAME", "[TAG_NAME ...]"]
|
||||
self.list_params = ["NODE_NAME"]
|
||||
end
|
||||
end
|
||||
29
devops-client/lib/devops-client/options/user_options.rb
Normal file
29
devops-client/lib/devops-client/options/user_options.rb
Normal file
@ -0,0 +1,29 @@
|
||||
require "devops-client/options/common_options"
|
||||
|
||||
class UserOptions < CommonOptions
|
||||
commands :create, :delete, :grant, :list, :password, :email
|
||||
|
||||
def initialize args, def_options
|
||||
super(args, def_options)
|
||||
self.header = I18n.t("headers.user")
|
||||
self.banner_header = "user"
|
||||
self.create_params = ["USER_NAME", "EMAIL"]
|
||||
self.delete_params = ["USER_NAME"]
|
||||
self.password_params = ["USER_NAME"]
|
||||
self.email_params = ["USER_NAME", "EMAIL"]
|
||||
self.grant_params = ["USER_NAME", "[COMMAND]", "[PRIVILEGES]"]
|
||||
end
|
||||
|
||||
def create_options
|
||||
self.options do |opts, options|
|
||||
opts.banner << self.create_banner
|
||||
|
||||
options[:new_password] = nil
|
||||
opts.on("--password PASSWORD", "New user password") do |p|
|
||||
options[:new_password] = p
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
41
devops-client/lib/devops-client/output/base.rb
Normal file
41
devops-client/lib/devops-client/output/base.rb
Normal file
@ -0,0 +1,41 @@
|
||||
require "terminal-table"
|
||||
require "csv"
|
||||
require "json"
|
||||
|
||||
module Output
|
||||
module Base
|
||||
|
||||
def create_table headers, rows, title=nil, with_num=true, separator=false
|
||||
return nil if headers.nil? or rows.nil?
|
||||
if with_num
|
||||
headers.unshift(I18n.t("output.table_header.number"))
|
||||
rows.each_with_index {|row, i| row.unshift(i + 1)}
|
||||
end
|
||||
table = Terminal::Table.new do |t|
|
||||
titles = ["#{I18n.t("output.table_header.api_version")}: #{self.options[:api]}",
|
||||
"#{title}"
|
||||
]
|
||||
t.title = titles.join( "\n" )
|
||||
t.headings = headers
|
||||
t.add_row rows[0]
|
||||
rows[1..-1].each do |r|
|
||||
t.add_separator if separator
|
||||
t.add_row r
|
||||
end
|
||||
end
|
||||
table
|
||||
end
|
||||
|
||||
def create_csv headers, rows, with_num=true, separator=":"
|
||||
if with_num
|
||||
headers.unshift(I18n.t("output.table_header.number"))
|
||||
rows.each_with_index {|row, i| row.unshift(i + 1)}
|
||||
end
|
||||
c = CSV.new("", {col_sep: separator, headers: true})
|
||||
c << headers
|
||||
rows.each{|r| c << r}
|
||||
c.string
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,34 @@
|
||||
require "devops-client/output/base"
|
||||
|
||||
module Output
|
||||
module BootstrapTemplates
|
||||
include Base
|
||||
|
||||
def table
|
||||
headers, rows = create(@list)
|
||||
create_table(headers, rows, I18n.t("output.title.bootstrap_template.list"))
|
||||
end
|
||||
|
||||
def csv
|
||||
headers, rows = create(@list)
|
||||
create_csv(headers, rows)
|
||||
end
|
||||
|
||||
def json
|
||||
JSON.pretty_generate @list
|
||||
end
|
||||
|
||||
private
|
||||
def create list
|
||||
abort I18n.t("output.not_found.bootstrap_template.list") if list.nil? or list.empty?
|
||||
headers = [ I18n.t("output.table_header.name") ]
|
||||
rows = []
|
||||
list.each do |l|
|
||||
rows.push [ l ]
|
||||
end
|
||||
return headers, rows
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
32
devops-client/lib/devops-client/output/filters.rb
Normal file
32
devops-client/lib/devops-client/output/filters.rb
Normal file
@ -0,0 +1,32 @@
|
||||
require "devops-client/output/base"
|
||||
|
||||
module Output
|
||||
module Filters
|
||||
include Base
|
||||
|
||||
def table
|
||||
headers, rows = create(@list)
|
||||
create_table(headers, rows, I18n.t("output.title.filter.list"))
|
||||
end
|
||||
|
||||
def csv
|
||||
headers, rows = create(@list)
|
||||
create_csv(headers, rows)
|
||||
end
|
||||
|
||||
def json
|
||||
JSON.pretty_generate @list
|
||||
end
|
||||
|
||||
private
|
||||
def create list
|
||||
abort(I18n.t("output.not_found.filter.list")) if list.nil? or list.empty?
|
||||
headers = [ I18n.t("output.table_header.image_id") ]
|
||||
rows = []
|
||||
list.each do |l|
|
||||
rows.push [ l ]
|
||||
end
|
||||
return headers, rows
|
||||
end
|
||||
end
|
||||
end
|
||||
51
devops-client/lib/devops-client/output/flavors.rb
Normal file
51
devops-client/lib/devops-client/output/flavors.rb
Normal file
@ -0,0 +1,51 @@
|
||||
require "devops-client/output/base"
|
||||
|
||||
module Output
|
||||
module Flavors
|
||||
include Base
|
||||
|
||||
def table
|
||||
headers, rows = create(@list, @provider)
|
||||
create_table(headers, rows, I18n.t("output.title.flavor.list"))
|
||||
end
|
||||
|
||||
def csv
|
||||
headers, rows = create(@list, @provider)
|
||||
create_csv(headers, rows)
|
||||
end
|
||||
|
||||
def json
|
||||
JSON.pretty_generate @list
|
||||
end
|
||||
|
||||
private
|
||||
def create list, provider
|
||||
abort(I18n.t("output.not_found.flavor.list")) if list.nil? or list.empty?
|
||||
headers = nil
|
||||
rows = []
|
||||
if provider == "openstack"
|
||||
headers = [
|
||||
I18n.t("output.table_header.id"),
|
||||
I18n.t("output.table_header.virtual_cpus"),
|
||||
I18n.t("output.table_header.disk"),
|
||||
I18n.t("output.table_header.ram")
|
||||
]
|
||||
list.each do |l|
|
||||
rows << [ l["id"], l["v_cpus"], l["disk"], l["ram"] ]
|
||||
end
|
||||
elsif provider == "ec2"
|
||||
headers = [
|
||||
I18n.t("output.table_header.name"),
|
||||
I18n.t("output.table_header.id"),
|
||||
I18n.t("output.table_header.virtual_cpus"),
|
||||
I18n.t("output.table_header.disk"),
|
||||
I18n.t("output.table_header.ram")
|
||||
]
|
||||
list.each do |l|
|
||||
rows << [ l["name"], l["id"], l["cores"], l["disk"], l["ram"] ]
|
||||
end
|
||||
end
|
||||
return headers, rows
|
||||
end
|
||||
end
|
||||
end
|
||||
48
devops-client/lib/devops-client/output/groups.rb
Normal file
48
devops-client/lib/devops-client/output/groups.rb
Normal file
@ -0,0 +1,48 @@
|
||||
require "devops-client/output/base"
|
||||
|
||||
module Output
|
||||
module Groups
|
||||
include Base
|
||||
|
||||
def table
|
||||
headers, rows = create(@list)
|
||||
create_table(headers, rows, I18n.t("output.title.group.list"), true, true)
|
||||
end
|
||||
|
||||
def csv
|
||||
headers, rows = create(@list)
|
||||
create_csv(headers, rows)
|
||||
end
|
||||
|
||||
def json
|
||||
JSON.pretty_generate @list
|
||||
end
|
||||
|
||||
private
|
||||
def create list
|
||||
abort(I18n.t("output.not_found.group.list")) if list.nil? or list.empty?
|
||||
headers = [
|
||||
I18n.t("output.table_header.name"),
|
||||
I18n.t("output.table_header.protocol"),
|
||||
I18n.t("output.table_header.from"),
|
||||
I18n.t("output.table_header.to"),
|
||||
I18n.t("output.table_header.cidr"),
|
||||
I18n.t("output.table_header.description")
|
||||
]
|
||||
rows = []
|
||||
list.each do |name, v|
|
||||
next if v.nil? or v.empty?
|
||||
p, f, t, c = [], [], [], []
|
||||
v["rules"].map do |l|
|
||||
p.push l["protocol"]
|
||||
f.push l["from"]
|
||||
t.push l["to"]
|
||||
c.push l["cidr"]
|
||||
end
|
||||
rows.push [ name, p.join("\n"), f.join("\n"), t.join("\n"), c.join("\n"), v["description"] ]
|
||||
end
|
||||
return headers, rows
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
79
devops-client/lib/devops-client/output/image.rb
Normal file
79
devops-client/lib/devops-client/output/image.rb
Normal file
@ -0,0 +1,79 @@
|
||||
require "devops-client/output/base"
|
||||
|
||||
module Output
|
||||
module Image
|
||||
include Base
|
||||
|
||||
def table
|
||||
title, headers, rows = nil, nil, nil
|
||||
with_num = if !@list.nil?
|
||||
title = I18n.t("output.title.image.list")
|
||||
headers, rows = create_list(@list, @provider)
|
||||
true
|
||||
elsif !@show.nil?
|
||||
title = I18n.t("output.title.image.show", :id => @show["id"])
|
||||
headers, rows = create_show @show
|
||||
false
|
||||
end
|
||||
create_table headers, rows, title, with_num
|
||||
end
|
||||
|
||||
def csv
|
||||
title, headers, rows = nil, nil, nil
|
||||
with_num = if !@list.nil?
|
||||
headers, rows = create_list(@list, @provider)
|
||||
true
|
||||
elsif !@show.nil?
|
||||
headers, rows = create_show @show
|
||||
false
|
||||
end
|
||||
create_csv headers, rows, with_num
|
||||
end
|
||||
|
||||
def json
|
||||
JSON.pretty_generate( case ARGV[1]
|
||||
when "list"
|
||||
@list
|
||||
when "show"
|
||||
@show
|
||||
end)
|
||||
end
|
||||
|
||||
private
|
||||
def create_list list, provider
|
||||
abort(I18n.t("output.not_found.image.list")) if list.empty?
|
||||
rows = []
|
||||
headers = if provider
|
||||
list.each {|l| rows.push [ l["name"], l["id"], l["status"] ]}
|
||||
[
|
||||
I18n.t("output.table_header.name"),
|
||||
I18n.t("output.table_header.id"),
|
||||
I18n.t("output.table_header.status")
|
||||
]
|
||||
else
|
||||
list.each {|l| rows.push [ l["id"], l["name"], l["bootstrap_template"], l["remote_user"], l["provider"] ] }
|
||||
[
|
||||
I18n.t("output.table_header.id"),
|
||||
I18n.t("output.table_header.name"),
|
||||
I18n.t("output.table_header.template"),
|
||||
I18n.t("output.table_header.remote_user"),
|
||||
I18n.t("output.table_header.provider")
|
||||
]
|
||||
end
|
||||
return headers, rows
|
||||
end
|
||||
|
||||
def create_show show
|
||||
rows = [ [ show["id"], show["name"], show["bootstrap_template"], show["remote_user"], show["provider"] ] ]
|
||||
headers = [
|
||||
I18n.t("output.table_header.id"),
|
||||
I18n.t("output.table_header.name"),
|
||||
I18n.t("output.table_header.template"),
|
||||
I18n.t("output.table_header.remote_user"),
|
||||
I18n.t("output.table_header.provider")
|
||||
]
|
||||
return headers, rows
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
33
devops-client/lib/devops-client/output/key.rb
Normal file
33
devops-client/lib/devops-client/output/key.rb
Normal file
@ -0,0 +1,33 @@
|
||||
require "devops-client/output/base"
|
||||
|
||||
module Output
|
||||
module Key
|
||||
include Base
|
||||
|
||||
def table
|
||||
title = I18n.t("output.title.key.list")
|
||||
headers, rows = create(@list)
|
||||
create_table headers, rows, title
|
||||
end
|
||||
|
||||
def csv
|
||||
headers, rows = create(@list)
|
||||
create_csv headers, rows
|
||||
end
|
||||
|
||||
def json
|
||||
JSON.pretty_generate( case ARGV[1]
|
||||
when "list"
|
||||
@list
|
||||
end)
|
||||
end
|
||||
|
||||
private
|
||||
def create list
|
||||
abort(I18n.t("output.not_found.key.list")) if list.nil? or list.empty?
|
||||
rows = []
|
||||
list.each {|l| rows.push [ l["id"], l["scope"] ] }
|
||||
return [ I18n.t("output.table_header.id"), I18n.t("output.table_header.scope") ], rows
|
||||
end
|
||||
end
|
||||
end
|
||||
50
devops-client/lib/devops-client/output/network.rb
Normal file
50
devops-client/lib/devops-client/output/network.rb
Normal file
@ -0,0 +1,50 @@
|
||||
require "devops-client/output/base"
|
||||
|
||||
module Output
|
||||
module Network
|
||||
include Base
|
||||
|
||||
def table
|
||||
headers, rows = create(@list, @provider)
|
||||
create_table(headers, rows, I18n.t("output.title.network.list"))
|
||||
end
|
||||
|
||||
def csv
|
||||
headers, rows = create(@list, @provider)
|
||||
create_csv(headers, rows)
|
||||
end
|
||||
|
||||
def json
|
||||
JSON.pretty_generate @list
|
||||
end
|
||||
|
||||
private
|
||||
def create list, provider
|
||||
headers = nil
|
||||
rows = []
|
||||
if provider == "openstack"
|
||||
abort(I18n.t("output.not_found.network.list")) if list.nil? or list.empty?
|
||||
headers = [ I18n.t("output.table_header.name"), I18n.t("output.table_header.cidr") ]
|
||||
list.each do |l|
|
||||
rows.push [ l["name"], l["cidr"] ]
|
||||
end
|
||||
elsif provider == "ec2"
|
||||
if list.nil? or list.empty?
|
||||
puts(I18n.t("output.not_found.network.list"))
|
||||
return nil, nil
|
||||
end
|
||||
headers = [
|
||||
I18n.t("output.table_header.subnet"),
|
||||
I18n.t("output.table_header.vpc_id"),
|
||||
I18n.t("output.table_header.cidr"),
|
||||
I18n.t("output.table_header.zone")
|
||||
]
|
||||
list.each do |l|
|
||||
rows.push [ l["subnetId"], l["vpcId"], l["cidr"], l["zone"] ]
|
||||
end
|
||||
end
|
||||
return headers, rows
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
146
devops-client/lib/devops-client/output/project.rb
Normal file
146
devops-client/lib/devops-client/output/project.rb
Normal file
@ -0,0 +1,146 @@
|
||||
require "devops-client/output/base"
|
||||
|
||||
module Output
|
||||
module Project
|
||||
include Base
|
||||
|
||||
NODE_HEADER = "Node number"
|
||||
SUBPROJECT_HEADER = "Subproject"
|
||||
|
||||
def table
|
||||
title, = nil
|
||||
with_num, with_separator = true, false
|
||||
headers, rows = if !@list.nil?
|
||||
title = I18n.t("output.title.project.list")
|
||||
create_list(@list)
|
||||
elsif !@show.nil?
|
||||
title = I18n.t("output.title.project.show", :name => @show["name"])
|
||||
with_num = false
|
||||
with_separator = true
|
||||
create_show(@show)
|
||||
elsif !@servers.nil?
|
||||
title = ARGV[2]
|
||||
title += " " + ARGV[3] unless ARGV[3].nil?
|
||||
title = I18n.t("output.title.project.servers", :title => title)
|
||||
create_servers(@servers)
|
||||
elsif !@test.nil?
|
||||
with_num = false
|
||||
title = I18n.t("output.title.project.test", :project => ARGV[2], :env => ARGV[3])
|
||||
create_test(@test)
|
||||
end
|
||||
create_table(headers, rows, title, with_num, with_separator)
|
||||
end
|
||||
|
||||
def csv
|
||||
with_num = true
|
||||
headers, rows = if !@list.nil?
|
||||
create_list(@list)
|
||||
elsif !@show.nil?
|
||||
with_num = false
|
||||
create_show(@show)
|
||||
elsif !@servers.nil?
|
||||
create_servers(@servers)
|
||||
elsif !@test.nil?
|
||||
with_num = false
|
||||
create_test(@test)
|
||||
end
|
||||
create_csv(headers, rows, with_num)
|
||||
end
|
||||
|
||||
def json
|
||||
JSON.pretty_generate(case ARGV[1]
|
||||
when "list"
|
||||
@list
|
||||
when "show"
|
||||
@show
|
||||
when "servers"
|
||||
@servers
|
||||
when "test"
|
||||
@test
|
||||
end)
|
||||
end
|
||||
|
||||
private
|
||||
def create_list list
|
||||
abort(I18n.t("output.not_found.project.list")) if list.empty?
|
||||
rows = list.map {|l| [l]}
|
||||
headers = [ I18n.t("output.table_header.id") ]
|
||||
return headers, rows
|
||||
end
|
||||
|
||||
def create_show show
|
||||
rows = []
|
||||
headers = if show["type"] == "multi"
|
||||
show["deploy_envs"].each do |de|
|
||||
subprojects = []
|
||||
nodes = []
|
||||
de["servers"].each do |s|
|
||||
s["subprojects"].each do |sp|
|
||||
subprojects.push "#{sp["name"]} - #{sp["env"]}"
|
||||
nodes.push sp["node"]
|
||||
end
|
||||
end
|
||||
rows.push [ de["identifier"], subprojects.join("\n"), nodes.join("\n"), de["users"].join("\n") ]
|
||||
end
|
||||
[
|
||||
I18n.t("output.table_header.deploy_env"),
|
||||
I18n.t("output.table_header.subproject") + " - " + I18n.t("output.table_header.deploy_env"),
|
||||
I18n.t("output.table_header.node_number"),
|
||||
I18n.t("output.table_header.users")
|
||||
]
|
||||
else
|
||||
show["deploy_envs"].each do |de|
|
||||
rows.push [ show["name"], de["identifier"], de["image"], de["flavor"], de["run_list"].join("\n"), de["groups"].join("\n"), de["subnets"].join("\n"), de["users"].join("\n") ]
|
||||
end
|
||||
[
|
||||
I18n.t("output.table_header.id"),
|
||||
I18n.t("output.table_header.deploy_env"),
|
||||
I18n.t("output.table_header.image_id"),
|
||||
I18n.t("output.table_header.flavor"),
|
||||
I18n.t("output.table_header.run_list"),
|
||||
I18n.t("output.table_header.groups"),
|
||||
I18n.t("output.table_header.subnets"),
|
||||
I18n.t("output.table_header.users")
|
||||
]
|
||||
end
|
||||
return headers, rows
|
||||
end
|
||||
|
||||
def create_servers servers
|
||||
abort(I18n.t("output.not_found.project.servers")) if servers.empty?
|
||||
rows = []
|
||||
servers.each do |s|
|
||||
rows.push [ s["project"], s["deploy_env"], s["chef_node_name"], s["remote_user"], s["provider"], s["id"] ]
|
||||
end
|
||||
headers = [
|
||||
I18n.t("output.table_header.id"),
|
||||
I18n.t("output.table_header.deploy_env"),
|
||||
I18n.t("output.table_header.node_name"),
|
||||
I18n.t("output.table_header.remote_user"),
|
||||
I18n.t("output.table_header.provider"),
|
||||
I18n.t("output.table_header.instance_id")
|
||||
]
|
||||
return headers, rows
|
||||
end
|
||||
|
||||
def create_test test
|
||||
rows = []
|
||||
headers = [
|
||||
I18n.t("output.table_header.server"),
|
||||
I18n.t("output.table_header.node_name"),
|
||||
I18n.t("output.table_header.creation"),
|
||||
I18n.t("output.table_header.bootstrap"),
|
||||
I18n.t("output.table_header.deletion")
|
||||
]
|
||||
test["servers"].each do |s|
|
||||
rows.push [ s["id"],
|
||||
s["chef_node_name"],
|
||||
"#{s["create"]["status"]}\n#{s["create"]["time"]}",
|
||||
"#{s["bootstrap"]["status"]}\n#{s["bootstrap"]["time"]}",
|
||||
"#{s["delete"]["status"]}\n#{s["delete"]["time"]}" ]
|
||||
end
|
||||
return headers, rows
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
32
devops-client/lib/devops-client/output/provider.rb
Normal file
32
devops-client/lib/devops-client/output/provider.rb
Normal file
@ -0,0 +1,32 @@
|
||||
require "devops-client/output/base"
|
||||
|
||||
module Output
|
||||
module Provider
|
||||
include Base
|
||||
|
||||
def table
|
||||
headers, rows = create(@list)
|
||||
create_table(headers, rows, I18n.t("output.title.provider.list"))
|
||||
end
|
||||
|
||||
def csv
|
||||
headers, rows = create(@list)
|
||||
create_csv(headers, rows)
|
||||
end
|
||||
|
||||
def json
|
||||
JSON.pretty_generate @list
|
||||
end
|
||||
|
||||
private
|
||||
def create list
|
||||
abort(I18n.t("output.not_found.provider.list")) if list.empty?
|
||||
headers = [ I18n.t("output.table_header.provider") ]
|
||||
rows = []
|
||||
list.each do |l|
|
||||
rows.push [ l ]
|
||||
end
|
||||
return headers, rows
|
||||
end
|
||||
end
|
||||
end
|
||||
35
devops-client/lib/devops-client/output/script.rb
Normal file
35
devops-client/lib/devops-client/output/script.rb
Normal file
@ -0,0 +1,35 @@
|
||||
require "devops-client/output/base"
|
||||
|
||||
module Output
|
||||
module Script
|
||||
include Base
|
||||
|
||||
def table
|
||||
headers, rows = create(@list)
|
||||
create_table(headers, rows, I18n.t("output.title.script.list"))
|
||||
end
|
||||
|
||||
def csv
|
||||
headers, rows = create(@list)
|
||||
create_csv(headers, rows)
|
||||
end
|
||||
|
||||
def json
|
||||
JSON.pretty_generate( case ARGV[1]
|
||||
when "list"
|
||||
@list
|
||||
end)
|
||||
end
|
||||
|
||||
private
|
||||
def create list
|
||||
rows = []
|
||||
abort(I18n.t("output.not_found.script.list")) if list.nil? or list.empty?
|
||||
list.each do |l|
|
||||
rows.push [ l ]
|
||||
end
|
||||
headers = [I18n.t("output.table_header.name")]
|
||||
return headers, rows
|
||||
end
|
||||
end
|
||||
end
|
||||
121
devops-client/lib/devops-client/output/server.rb
Normal file
121
devops-client/lib/devops-client/output/server.rb
Normal file
@ -0,0 +1,121 @@
|
||||
require "devops-client/output/base"
|
||||
|
||||
module Output
|
||||
module Server
|
||||
include Base
|
||||
|
||||
def table
|
||||
title = nil
|
||||
headers, rows = if !@list.nil?
|
||||
case options[:type]
|
||||
when "chef"
|
||||
title = I18n.t("output.title.server.chef")
|
||||
when "openstack"
|
||||
title = I18n.t("output.title.server.openstack")
|
||||
when "ec2"
|
||||
title = I18n.t("output.title.server.ec2")
|
||||
else
|
||||
title = I18n.t("output.title.server.list")
|
||||
end
|
||||
create_list(@list)
|
||||
elsif !@show.nil?
|
||||
title = I18n.t("output.title.server.show", :name => @show["chef_node_name"])
|
||||
create_show(@show)
|
||||
end
|
||||
create_table headers, rows, title
|
||||
end
|
||||
|
||||
def csv
|
||||
headers, rows = if !@list.nil?
|
||||
create_list(@list)
|
||||
elsif !@show.nil?
|
||||
create_show(@show)
|
||||
end
|
||||
create_csv headers, rows
|
||||
end
|
||||
|
||||
def json
|
||||
JSON.pretty_generate(case ARGV[1]
|
||||
when "list"
|
||||
@list
|
||||
when "show"
|
||||
@show
|
||||
end)
|
||||
end
|
||||
|
||||
private
|
||||
def create_list list
|
||||
abort(I18n.t("output.not_found.server.list")) if list.empty?
|
||||
rows, keys = [], nil
|
||||
headers = case options[:type]
|
||||
when "chef"
|
||||
keys = ["chef_node_name"]
|
||||
title = "Chef servers"
|
||||
[I18n.t("output.table_header.node_name")]
|
||||
when "openstack"
|
||||
keys = ["instance_id", "name", "public_ip", "private_ip", "keypair", "flavor", "image", "state"]
|
||||
title = "Openstack servers"
|
||||
[
|
||||
I18n.t("output.table_header.instance_id"),
|
||||
I18n.t("output.table_header.node_name"),
|
||||
I18n.t("output.table_header.public_ip"),
|
||||
I18n.t("output.table_header.private_ip"),
|
||||
I18n.t("output.table_header.keypair"),
|
||||
I18n.t("output.table_header.flavor"),
|
||||
I18n.t("output.table_header.image"),
|
||||
I18n.t("output.table_header.state")
|
||||
]
|
||||
when "ec2"
|
||||
keys = ["instance_id", "name", "ip", "private_ip", "dns_name", "keypair", "flavor", "image", "zone", "state", "launched_at"]
|
||||
title = "Ec2 servers"
|
||||
[
|
||||
I18n.t("output.table_header.instance_id"),
|
||||
I18n.t("output.table_header.node_name"),
|
||||
I18n.t("output.table_header.public_ip"),
|
||||
I18n.t("output.table_header.private_ip"),
|
||||
I18n.t("output.table_header.dns"),
|
||||
I18n.t("output.table_header.keypair"),
|
||||
I18n.t("output.table_header.flavor"),
|
||||
I18n.t("output.table_header.image"),
|
||||
I18n.t("output.table_header.zone"),
|
||||
I18n.t("output.table_header.state"),
|
||||
I18n.t("output.table_header.created_at")
|
||||
]
|
||||
else
|
||||
keys = ["id", "chef_node_name"]
|
||||
title = "Servers"
|
||||
[
|
||||
I18n.t("output.table_header.instance_id"),
|
||||
I18n.t("output.table_header.node_name")
|
||||
]
|
||||
end
|
||||
list.each do |l|
|
||||
row = []
|
||||
keys.each{|k| row.push l[k]}
|
||||
rows.push row
|
||||
end
|
||||
return headers, rows
|
||||
end
|
||||
|
||||
def create_show show
|
||||
rows = []
|
||||
headers = [
|
||||
I18n.t("output.table_header.instance_id"),
|
||||
I18n.t("output.table_header.node_name"),
|
||||
I18n.t("output.table_header.project"),
|
||||
I18n.t("output.table_header.deploy_env"),
|
||||
I18n.t("output.table_header.provider"),
|
||||
I18n.t("output.table_header.remote_user"),
|
||||
I18n.t("output.table_header.private_ip"),
|
||||
I18n.t("output.table_header.created_at"),
|
||||
I18n.t("output.table_header.created_by")
|
||||
]
|
||||
keys = ["id", "chef_node_name", "project", "deploy_env", "provider", "remote_user", "private_ip", "created_at", "created_by"]
|
||||
row = []
|
||||
keys.each{|k| row.push show[k]}
|
||||
rows.push row
|
||||
return headers, rows
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
29
devops-client/lib/devops-client/output/tag.rb
Normal file
29
devops-client/lib/devops-client/output/tag.rb
Normal file
@ -0,0 +1,29 @@
|
||||
require "devops-client/output/base"
|
||||
|
||||
module Output
|
||||
module Tag
|
||||
include Base
|
||||
|
||||
def table
|
||||
headers, rows = create(@list)
|
||||
create_table(headers, rows, I18n.t("output.title.tag.list"))
|
||||
end
|
||||
|
||||
def csv
|
||||
headers, rows = create(@list)
|
||||
create_csv(headers, rows)
|
||||
end
|
||||
|
||||
def json
|
||||
JSON.pretty_generate @list
|
||||
end
|
||||
|
||||
private
|
||||
def create list
|
||||
abort(I18n.t("output.not_found.tag.list")) if list.empty?
|
||||
headers = [I18n.t("output.table_header.tag")]
|
||||
rows = list.map {|l| [ l ]}
|
||||
return headers, rows
|
||||
end
|
||||
end
|
||||
end
|
||||
79
devops-client/lib/devops-client/output/user.rb
Normal file
79
devops-client/lib/devops-client/output/user.rb
Normal file
@ -0,0 +1,79 @@
|
||||
require "devops-client/output/base"
|
||||
|
||||
module Output
|
||||
module User
|
||||
include Base
|
||||
|
||||
def table
|
||||
title, headers = nil, nil
|
||||
rows, with_num = create_subheader, false
|
||||
rows += create_rows(@list)
|
||||
headers = [
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
{:value => I18n.t("output.table_header.privileges"), :colspan => 12, :alignment => :center }
|
||||
]
|
||||
|
||||
create_table headers, rows, I18n.t("output.title.user.list"), with_num, true
|
||||
end
|
||||
|
||||
def csv
|
||||
rows = create_rows(@list)
|
||||
headers = create_subheader
|
||||
create_csv headers, rows
|
||||
end
|
||||
|
||||
def json
|
||||
JSON.pretty_generate( case ARGV[1]
|
||||
when "list"
|
||||
@list
|
||||
end)
|
||||
end
|
||||
|
||||
private
|
||||
def create_subheader
|
||||
[ [
|
||||
I18n.t("output.table_header.number"),
|
||||
I18n.t("output.table_header.id"),
|
||||
I18n.t("output.table_header.email"),
|
||||
I18n.t("output.table_header.image"),
|
||||
I18n.t("output.table_header.key"),
|
||||
I18n.t("output.table_header.project"),
|
||||
I18n.t("output.table_header.server"),
|
||||
I18n.t("output.table_header.users"),
|
||||
I18n.t("output.table_header.script"),
|
||||
I18n.t("output.table_header.filter"),
|
||||
I18n.t("output.table_header.flavor"),
|
||||
I18n.t("output.table_header.group"),
|
||||
I18n.t("output.table_header.network"),
|
||||
I18n.t("output.table_header.provider"),
|
||||
I18n.t("output.table_header.templates")
|
||||
] ]
|
||||
end
|
||||
|
||||
def create_rows list
|
||||
abort(I18n.t("output.not_found.user.list")) if list.nil? or list.empty?
|
||||
rows = []
|
||||
list.each_with_index do |l, i|
|
||||
next if l["privileges"].nil?
|
||||
|
||||
flavor = "#{l["privileges"]["flavor"]}"
|
||||
group = "#{l["privileges"]["group"]}"
|
||||
image = "#{l["privileges"]["image"]}"
|
||||
project = "#{l["privileges"]["project"]}"
|
||||
server = "#{l["privileges"]["server"]}"
|
||||
key = "#{l["privileges"]["key"]}"
|
||||
user = "#{l["privileges"]["user"]}"
|
||||
filter = "#{l["privileges"]["filter"]}"
|
||||
network = "#{l["privileges"]["network"]}"
|
||||
provider = "#{l["privileges"]["provider"]}"
|
||||
script = "#{l["privileges"]["script"]}"
|
||||
templates = "#{l["privileges"]["templates"]}"
|
||||
|
||||
rows.push [ (i + 1).to_s, l["id"], l["email"], image, key, project, server, user, script, filter, flavor, group, network, provider, templates]
|
||||
end
|
||||
rows
|
||||
end
|
||||
end
|
||||
end
|
||||
3
devops-client/lib/devops-client/version.rb
Normal file
3
devops-client/lib/devops-client/version.rb
Normal file
@ -0,0 +1,3 @@
|
||||
module DevopsClient
|
||||
VERSION = "2.1.29"
|
||||
end
|
||||
4
devops-client/lib/exceptions/devops_exception.rb
Normal file
4
devops-client/lib/exceptions/devops_exception.rb
Normal file
@ -0,0 +1,4 @@
|
||||
class DevopsException < StandardError
|
||||
|
||||
end
|
||||
|
||||
4
devops-client/lib/exceptions/invalid_query.rb
Normal file
4
devops-client/lib/exceptions/invalid_query.rb
Normal file
@ -0,0 +1,4 @@
|
||||
class InvalidQuery < StandardError
|
||||
|
||||
end
|
||||
|
||||
3
devops-client/lib/exceptions/not_found.rb
Normal file
3
devops-client/lib/exceptions/not_found.rb
Normal file
@ -0,0 +1,3 @@
|
||||
class NotFound < StandardError
|
||||
|
||||
end
|
||||
268
devops-client/locales/en.yml
Normal file
268
devops-client/locales/en.yml
Normal file
@ -0,0 +1,268 @@
|
||||
en:
|
||||
config:
|
||||
invalid:
|
||||
host: "Empty or invalid property 'host' in configuration file '%{file}'\n\nCheck your configuration '%{file}' file or use --host option"
|
||||
empty: "Empty or undefined property '%{key}' in configuration file '%{file}'"
|
||||
proxy_type: "Invalid 'proxy_type' property in configuration file '%{file}', available values: %{values}"
|
||||
parameter: "Wrong config file parameter: -c file"
|
||||
property:
|
||||
lang: "Language (%{langs})"
|
||||
host: "Devops server host and port"
|
||||
api: "API version"
|
||||
username: "Username"
|
||||
password: "Password"
|
||||
proxy_type: "Proxy type (none, system, custom)"
|
||||
http_proxy: "HTTP proxy"
|
||||
created: "Configuration file '%{file}' has been created"
|
||||
not_exist: "File '%{file}' does not exist"
|
||||
completion:
|
||||
message: "Bash completion file has been copied to '%{file}'"
|
||||
put: "Put '. %{file}' to your .bashrc file"
|
||||
log:
|
||||
info: "INFO: %{msg}"
|
||||
warn: "WARN: %{msg}"
|
||||
error: "ERROR: %{msg}"
|
||||
headers:
|
||||
flavor: "Flavor"
|
||||
template: "Bootstrap template"
|
||||
deploy: "Deploy"
|
||||
filters: "Filters"
|
||||
group: "Security groups"
|
||||
image: "Image"
|
||||
key: "Key"
|
||||
network: "Network"
|
||||
project: "Project"
|
||||
project_env: "Project environment"
|
||||
provider: "Provider"
|
||||
server: "Server"
|
||||
script: "Script"
|
||||
tag: "Tag"
|
||||
user: "User"
|
||||
handler:
|
||||
flavor:
|
||||
list:
|
||||
empty: "Flavors list is empty"
|
||||
env:
|
||||
list:
|
||||
empty: "Project environment list is empty"
|
||||
image:
|
||||
question:
|
||||
delete: "Are you sure to delete image '%{name}'?"
|
||||
create: "Create image?"
|
||||
create:
|
||||
ssh_user: "The ssh username"
|
||||
template: "Bootstrap template or empty value"
|
||||
filter:
|
||||
question:
|
||||
delete: "Are you sure to delete image filter(s) '%{name}'?"
|
||||
key:
|
||||
question:
|
||||
delete: "Are you sure to delete key '%{name}'?"
|
||||
project:
|
||||
list:
|
||||
empty: "Project list is empty"
|
||||
question:
|
||||
delete: "Are you sure to delete project '%{name}'?"
|
||||
create: "Create project?"
|
||||
update: "Update project?"
|
||||
add_env: "Add environment?"
|
||||
parameter:
|
||||
run_list:
|
||||
empty: "WARN: run list is empty, continue?"
|
||||
exist: "Project '%{project}' already exist"
|
||||
create:
|
||||
invalid_json: "Invalid JSON in file '%{file}'"
|
||||
env: "Deploy environment identifier"
|
||||
env_exist: "Deploy environment '%{env}' already exist"
|
||||
flavor:
|
||||
not_found: "No such flavor"
|
||||
subnet:
|
||||
ec2: "Subnet (or Enter)"
|
||||
openstack: "Subnets"
|
||||
user: "Users, you will be added automatically"
|
||||
run_list:
|
||||
invalid: "ERROR: invalid run list elements: %{list}"
|
||||
invalid_subcommand: "Invalid subcommand for '%{cmd}': '%{scmd}'"
|
||||
script:
|
||||
question:
|
||||
delete: "Are you sure to delete script '%{name}'?"
|
||||
tag:
|
||||
question:
|
||||
delete: "Are you sure to delete tag(s) '%{name}' from node '%{node}'?"
|
||||
user:
|
||||
question:
|
||||
delete: "Are you sure to delete user '%{name}'?"
|
||||
password_for: "Password for %{user}:"
|
||||
server:
|
||||
question:
|
||||
delete: "Are you sure to delete server '%{name}'?"
|
||||
message:
|
||||
choose_list_default: "Choose %{name} (comma separated), like 1,2,3 or empty for default value '%{default}': "
|
||||
choose_list: "Choose %{name} (comma separated), like 1,2,3: "
|
||||
choose: "Choose '%{name}': "
|
||||
error:
|
||||
parameter:
|
||||
undefined: "ERROR: parameter '%{name}' is undefined"
|
||||
unauthorized: "401 - Unauthorized"
|
||||
file:
|
||||
not_exist: "File '%{file}' does not exist"
|
||||
number:
|
||||
invalid: "Invalid number"
|
||||
list:
|
||||
empty: "%{name} list is empty"
|
||||
output:
|
||||
table_header:
|
||||
api_version: "API version"
|
||||
number: "Number"
|
||||
id: "Id"
|
||||
name: "Name"
|
||||
provider: "Provider"
|
||||
remote_user: "Remote user"
|
||||
disk: "Disk"
|
||||
virtual_cpus: "Virtual CPUs"
|
||||
ram: "RAM"
|
||||
image_id: "Image Id"
|
||||
protocol: "Protocol"
|
||||
from: "From"
|
||||
to: "To"
|
||||
cidr: "CIDR"
|
||||
description: "Description"
|
||||
status: "Status"
|
||||
template: "Bootstrap template"
|
||||
scope: "Scope"
|
||||
subnet: "Subnet"
|
||||
vpc_id: "VPC Id"
|
||||
zone: "Zone"
|
||||
deploy_env: "Environment"
|
||||
node_name: "Node name"
|
||||
image: "Image"
|
||||
flavor: "Flavor"
|
||||
group: "Group"
|
||||
key: "Keys"
|
||||
templates: "Templates"
|
||||
run_list: "Run list"
|
||||
groups: "Security groups"
|
||||
subnets: "Subnets"
|
||||
users: "Users"
|
||||
server: "Server"
|
||||
project: "Project"
|
||||
script: "Script"
|
||||
network: "Network"
|
||||
filter: "Filter"
|
||||
creation: "Creation"
|
||||
bootatrap: "Bootstrap"
|
||||
deletion: "Deletion"
|
||||
instance_id: "Instance Id"
|
||||
subproject: "Subproject"
|
||||
node_number: "Node number"
|
||||
tag: "Tag"
|
||||
privileges: "Privileges"
|
||||
email: "E-mail"
|
||||
state: "State"
|
||||
public_ip: "Public IP"
|
||||
private_ip: "Private IP"
|
||||
dns: "DNS"
|
||||
keypair: "Keypair"
|
||||
created_at: "Created at"
|
||||
created_by: "Created by"
|
||||
title:
|
||||
flavor:
|
||||
list: "Flavors"
|
||||
bootstrap_template:
|
||||
list: "Bootstrap templates"
|
||||
filter:
|
||||
list: "Filters"
|
||||
group:
|
||||
list: "Security groups"
|
||||
image:
|
||||
list: "Images"
|
||||
show: "Image '%{id}' information"
|
||||
key:
|
||||
list: "Keys"
|
||||
network:
|
||||
list: "Networks"
|
||||
project:
|
||||
list: "Projects"
|
||||
show: "Project '%{name}' information"
|
||||
servers: "Project '%{title}' servers"
|
||||
test: "Project test: %{project} - %{env}"
|
||||
provider:
|
||||
list: "Providers"
|
||||
script:
|
||||
list: "Scripts"
|
||||
server:
|
||||
list: "Servers"
|
||||
chef: "Chef servers"
|
||||
ec2: "Ec2 servers"
|
||||
openstack: "Openstack servers"
|
||||
show: "Server '%{name}'"
|
||||
tag:
|
||||
list: "Tags"
|
||||
users:
|
||||
list: "Users"
|
||||
not_found:
|
||||
flavor:
|
||||
list: "No flavor found"
|
||||
bootstrap_template:
|
||||
list: "No bootstrap templates found"
|
||||
filter:
|
||||
list: "No filters found"
|
||||
group:
|
||||
list: "No security groups found"
|
||||
image:
|
||||
list: "No images found"
|
||||
key:
|
||||
list: "No keys found"
|
||||
network:
|
||||
list: "No networks found"
|
||||
project:
|
||||
list: "No project found"
|
||||
servers: "No servers for project '%{name}' found"
|
||||
provider:
|
||||
list: "Empty providers list"
|
||||
script:
|
||||
list: "No scripts uploaded"
|
||||
server:
|
||||
list: "No servers found"
|
||||
tag:
|
||||
list: "No tags found"
|
||||
user:
|
||||
list: "No users found"
|
||||
options:
|
||||
usage: "Usage: %{cmd} command [options]"
|
||||
commands: "Commands"
|
||||
options: "Options"
|
||||
common_options: "Common options"
|
||||
common:
|
||||
help: "Show help"
|
||||
confirmation: "Answer 'yes' for all questions"
|
||||
config: "Specify devops client config file (%{file})"
|
||||
version: "devops client version"
|
||||
host: "devops service host address (%{host})"
|
||||
api: "devops service API version (%{api})"
|
||||
username: "devops username (%{username})"
|
||||
format: "Output format: '%{formats}' (%{format})"
|
||||
completion: "Initialize bash completion script"
|
||||
project:
|
||||
header: "Project"
|
||||
create:
|
||||
groups: "Security groups (comma separated list)"
|
||||
deploy_env: "Deploy environment identifier"
|
||||
file: "File in JSON with project settings"
|
||||
subnets: "Subnets identifiers for deploy environment (ec2 - only one subnet, openstack - comma separated list)"
|
||||
flavor: "Specify flavor for the project environment"
|
||||
image: "Specify image identifier for the project environment"
|
||||
run_list: "Additional recipes and roles (comma separated), like recipe[mycookbook::myrecipe],role[myrole]"
|
||||
users: "Users list (comma separated) for deploy environment control"
|
||||
provider: "Provider - 'ec2' or 'openstack'"
|
||||
no_expires: "Without expiry time"
|
||||
expires: "Expiry time (5m, 3h, 2d, 1w, etc)"
|
||||
user_add:
|
||||
deploy_env: "Add user to deploy environment"
|
||||
user_delete:
|
||||
deploy_env: "Delete user from deploy environment"
|
||||
deploy:
|
||||
servers: "Servers list (comma separated) for deploy"
|
||||
script:
|
||||
run:
|
||||
params: "Script arguments (comma separated list)"
|
||||
268
devops-client/locales/ru.yml
Normal file
268
devops-client/locales/ru.yml
Normal file
@ -0,0 +1,268 @@
|
||||
ru:
|
||||
config:
|
||||
invalid:
|
||||
host: "Пустой или неверный параметр 'host' в конфигурационном файле '%{file}'\n\nПроверьте конфигурационный файл '%{file}' или используйте опцию --host"
|
||||
empty: "Параметр '%{key}' в конфигурационном файле '%{file}' пуст или не определен"
|
||||
proxy_type: "Неверный параметр 'proxy_type' в конфигурационном файле '%{file}', допустимые значения: %{values}"
|
||||
parameter: "Неверный параметр конфигурационного файла: -c file"
|
||||
property:
|
||||
lang: "Язык (%{langs})"
|
||||
host: "Адрес и порт сервера Devops"
|
||||
api: "Версия API"
|
||||
username: "Имя пользователя"
|
||||
password: "Пароль"
|
||||
proxy_type: "Тип прокси (none, system, custom)"
|
||||
http_proxy: "HTTP прокси"
|
||||
created: "Конфигурационный файл '%{file}' создан"
|
||||
not_exist: "Файл '%{file}' не существует"
|
||||
completion:
|
||||
message: "Файл дополнений bash скопирован в '%{file}'"
|
||||
put: "Добавьте '. %{file}' в Ваш .bashrc файл"
|
||||
log:
|
||||
info: "Информация: %{msg}"
|
||||
warn: "Предупреждение: %{msg}"
|
||||
error: "Ошибка: %{msg}"
|
||||
headers:
|
||||
flavor: "Конфигурация"
|
||||
deploy: "Деплой"
|
||||
template: "Шаблон загрузки"
|
||||
filters: "Фильтры"
|
||||
group: "Группы безопасности"
|
||||
image: "Образ"
|
||||
key: "Ключ"
|
||||
network: "Сеть"
|
||||
project: "Проект"
|
||||
project_env: "Окружение проект"
|
||||
provider: "Провайдер"
|
||||
server: "Сервер"
|
||||
script: "Скрипт"
|
||||
tag: "Тег"
|
||||
user: "Пользователь"
|
||||
handler:
|
||||
flavor:
|
||||
list:
|
||||
empty: "Список конфигураций пуст"
|
||||
image:
|
||||
question:
|
||||
delete: "Вы уверены, что хотите удалить образ '%{name}'?"
|
||||
create: "Создать образ?"
|
||||
create:
|
||||
ssh_user: "Имя пользоватлея ssh"
|
||||
template: "Шаблон загрузки или пустое значение"
|
||||
filter:
|
||||
question:
|
||||
delete: "Вы уверены, что хотите удалить фильтр(ы) образа '%{name}'?"
|
||||
key:
|
||||
question:
|
||||
delete: "Вы уверены, что хотите удалить ключ '%{name}'?"
|
||||
project:
|
||||
list:
|
||||
empty: "Список проектов пуст"
|
||||
env:
|
||||
list:
|
||||
empty: "Список окружений проекта пуст"
|
||||
question:
|
||||
delete: "Вы уверены, что хотите удалить проект '%{name}'?"
|
||||
create: "Создать проект?"
|
||||
update: "Обновить проект?"
|
||||
add_env: "Добавить окружение?"
|
||||
parameter:
|
||||
run_list:
|
||||
empty: "Предупреждение: список запуска пуст, продолжить?"
|
||||
exist: "Проект '%{project}' уже существует"
|
||||
create:
|
||||
invalid_json: "Неверный JSON в файле '%{file}'"
|
||||
env: "Идентификатор окружения"
|
||||
env_exist: "Окружение '%{env}' уже существует"
|
||||
flavor:
|
||||
not_found: "Конфигурация не надена"
|
||||
subnet:
|
||||
ec2: "Подсеть (или Enter)"
|
||||
openstack: "Подсети"
|
||||
user: "Пользователи, Вы будете добалены автоматически"
|
||||
run_list:
|
||||
invalid: "ОШИБКА: неверные элементы run_list: %{list}"
|
||||
invalid_subcommand: "Неверная подкомманда для '%{cmd}': '%{scmd}'"
|
||||
script:
|
||||
question:
|
||||
delete: "Вы уверены, что хотите удалить скрипт '%{name}'?"
|
||||
tag:
|
||||
question:
|
||||
delete: "Вы уверены, что хотите удалить тэг(и) '%{name}' из ноды '%{node}'?"
|
||||
user:
|
||||
question:
|
||||
delete: "Вы уверены, что хотите удалить пользователя '%{name}'?"
|
||||
password_for: "Пароль для %{user}:"
|
||||
server:
|
||||
question:
|
||||
delete: "Вы уверены, что хотите удалить сервер '%{name}'?"
|
||||
message:
|
||||
choose_list_default: "Выберите значения из списка (разделенные запятой), например 1,2,3 или оставте пустым для значеия по умолчанию '%{default}': "
|
||||
choose_list: "Выберите значения из списка (разделенные запятой), например 1,2,3: "
|
||||
choose: "Выберите '%{name}': "
|
||||
error:
|
||||
parameter:
|
||||
undefined: "Ошибка: параметр '%{name}' не указан"
|
||||
unauthorized: "401 - Не авторизован"
|
||||
file:
|
||||
not_exist: "Файл '%{file}' не существует"
|
||||
number:
|
||||
invalid: "Неверный номер"
|
||||
list:
|
||||
empty: "%{name} список пуст"
|
||||
output:
|
||||
table_header:
|
||||
api_version: "Версия API"
|
||||
number: "Номер"
|
||||
id: "Id"
|
||||
name: "Имя"
|
||||
provider: "Провайдер"
|
||||
remote_user: "Пользователь"
|
||||
disk: "Диск"
|
||||
virtual_cpus: "Виртуальные ЦПУ"
|
||||
ram: "RAM"
|
||||
image_id: "Id образа"
|
||||
protocol: "Протокол"
|
||||
from: "Из"
|
||||
to: "В"
|
||||
cidr: "CIDR"
|
||||
description: "Описание"
|
||||
status: "Статус"
|
||||
template: "Шаблон"
|
||||
scope: "Область"
|
||||
subnet: "Подсеть"
|
||||
vpc_id: "VPC Id"
|
||||
zone: "Зона"
|
||||
deploy_env: "Окружение"
|
||||
node_name: "Имя ноды"
|
||||
image: "Образ"
|
||||
flavor: "Конфигурация"
|
||||
group: "Группы"
|
||||
key: "Ключи"
|
||||
templates: "Шаблоны"
|
||||
run_list: "Список запуска"
|
||||
groups: "Группы безоп."
|
||||
subnets: "Подсети"
|
||||
users: "Пользователи"
|
||||
server: "Сервер"
|
||||
project: "Проект"
|
||||
script: "Скрипт"
|
||||
network: "Сеть"
|
||||
filter: "Филтер"
|
||||
creation: "Создание"
|
||||
bootatrap: "Бутстрап"
|
||||
deletion: "Удаление"
|
||||
instance_id: "Id объекта"
|
||||
subproject: "Подпроект"
|
||||
node_number: "Номер ноды"
|
||||
tag: "Тэг"
|
||||
privileges: "Привилегии"
|
||||
email: "E-mail"
|
||||
state: "Состояние"
|
||||
public_ip: "Публичный IP"
|
||||
private_ip: "Приватный IP"
|
||||
dns: "DNS"
|
||||
keypair: "Ключ"
|
||||
created_at: "Создан"
|
||||
created_by: "Создал"
|
||||
title:
|
||||
flavor:
|
||||
list: "Конфигурации"
|
||||
bootstrap_template:
|
||||
list: "Шаблоны"
|
||||
filter:
|
||||
list: "Фильтры"
|
||||
group:
|
||||
list: "Группы безопасности"
|
||||
image:
|
||||
list: "Образы"
|
||||
show: "Информация об образе '%{id}'"
|
||||
key:
|
||||
list: "Ключи"
|
||||
network:
|
||||
list: "Сети"
|
||||
project:
|
||||
list: "Проекты"
|
||||
show: "Информация о проекте '%{name}'"
|
||||
servers: "Сервера проекта '%{title}'"
|
||||
test: "Тестирование проекта: %{project} - %{env}"
|
||||
provider:
|
||||
list: "Провайдеры"
|
||||
script:
|
||||
list: "Скрипты"
|
||||
server:
|
||||
list: "Сервера"
|
||||
chef: "Сервера Chef"
|
||||
ec2: "Сервера Ec2"
|
||||
openstack: "Сервера Openstack"
|
||||
show: "Сервер '%{name}'"
|
||||
tag:
|
||||
list: "Теги"
|
||||
users:
|
||||
list: "Пользователи"
|
||||
not_found:
|
||||
flavor:
|
||||
list: "Ни одной конфигурации не найдено"
|
||||
bootstrap_template:
|
||||
list: "Ни одного шаблона не найдено"
|
||||
filter:
|
||||
list: "Ни одного фильтра не найдено"
|
||||
group:
|
||||
list: "Ни одной группы безопасности не найдено"
|
||||
image:
|
||||
list: "Ни одного образа не найдено"
|
||||
key:
|
||||
list: "Ни одного ключа не найдено"
|
||||
network:
|
||||
list: "Ни одной сети не найдено"
|
||||
project:
|
||||
list: "Ни одного проекта не найдено"
|
||||
servers: "Ни одного сервера для проекта '%{name}' не найдено"
|
||||
provider:
|
||||
list: "Ни одного провайдера не найдено"
|
||||
script:
|
||||
list: "Не загружено ни одного скрипта"
|
||||
server:
|
||||
list: "Ни одного сервера не найдено"
|
||||
tag:
|
||||
list: "Ни одного тега не найдено"
|
||||
user:
|
||||
list: "Ни одного пользователя не найдено"
|
||||
options:
|
||||
usage: "Использование: %{cmd} команда [опции]"
|
||||
commands: "Команды"
|
||||
options: "Опции"
|
||||
common_options: "Общие опции"
|
||||
common:
|
||||
help: "Показать помощь"
|
||||
confirmation: "Ответить 'да' на все вопросы"
|
||||
config: "Указать конфигурационный файл для devops клиента (%{file})"
|
||||
version: "Версия devops клиента"
|
||||
host: "Адрес хоста devops сервера (%{host})"
|
||||
api: "Версия API devops сервера (%{api})"
|
||||
username: "Имя пользователя devops (%{username})"
|
||||
format: "Формат вывода: '%{formats}' (%{format})"
|
||||
completion: "Инициализировать bash скрипт автодополнений"
|
||||
project:
|
||||
header: "Проект"
|
||||
create:
|
||||
groups: "Группы безопасности (список разделенный запятой)"
|
||||
deploy_env: "Идентификатор окружения"
|
||||
file: "Файл в формате JSON, содержащий настройки проекта"
|
||||
subnets: "Идентификаторы подсетей для окружения (ec2 - только одна подсеть, openstack - список разделенный запятой)"
|
||||
flavor: "Указать flavor для окружения проекта"
|
||||
image: "Указать идентификатор образа для окружения проекта"
|
||||
run_list: "Дополнительные рецепты и роли (разделенный запятой), например recipe[mycookbook::myrecipe],role[myrole]"
|
||||
users: "Список пользователей (разделенный запятой) для управления окружением проекта"
|
||||
provider: "Провайдер - 'ec2' или 'openstack'"
|
||||
no_expires: "Без времени жизни"
|
||||
expires: "Время жизни (5m, 3h, 2d, 1w, etc)"
|
||||
user_add:
|
||||
deploy_env: "Добавить пользователя к окружению проекта"
|
||||
user_delete:
|
||||
deploy_env: "Удалить пользователя из окружения проекта"
|
||||
deploy:
|
||||
servers: "Список серверов (разделенный запятой) для деплоя"
|
||||
script:
|
||||
run:
|
||||
params: "Аргументы скрипта (список разделенный запятой)"
|
||||
104
devops-client/tests/base_test.rb
Normal file
104
devops-client/tests/base_test.rb
Normal file
@ -0,0 +1,104 @@
|
||||
require "json"
|
||||
class BaseTest
|
||||
|
||||
COMMAND = "devops"
|
||||
SUCCESS = "\e[32msuccess\e[0m"
|
||||
FAILED = "\e[31mfailed\e[0m"
|
||||
CONFIGS = ["./devops-client-test.conf"]
|
||||
#, "-c ./test_conf1.conf"]
|
||||
|
||||
TITLE_SEPARATOR = "-" * 80
|
||||
END_SEPARATOR = "*" * 80 + "\n"
|
||||
|
||||
def title= title
|
||||
@title = title
|
||||
end
|
||||
|
||||
def run_tests cmds, check=true
|
||||
puts
|
||||
puts @title
|
||||
puts TITLE_SEPARATOR
|
||||
cmds.each do |cmd|
|
||||
command = create_cmd(cmd)
|
||||
s = `#{command}`
|
||||
if check
|
||||
if $?.success?
|
||||
print SUCCESS
|
||||
else
|
||||
print FAILED
|
||||
puts_error s
|
||||
exit(1)
|
||||
end
|
||||
end
|
||||
puts
|
||||
end
|
||||
puts END_SEPARATOR
|
||||
end
|
||||
|
||||
def run_test_with_block cmd
|
||||
puts
|
||||
puts @title
|
||||
puts TITLE_SEPARATOR
|
||||
command = create_cmd(cmd)
|
||||
s = `#{command}`
|
||||
if $?.success?
|
||||
puts SUCCESS
|
||||
if block_given?
|
||||
print "Validation block...\t"
|
||||
res = yield s
|
||||
if res
|
||||
puts SUCCESS
|
||||
else
|
||||
puts FAILED
|
||||
puts_error("Validation block returns 'false'")
|
||||
exit(-1)
|
||||
end
|
||||
end
|
||||
else
|
||||
puts FAILED
|
||||
puts_error s
|
||||
exit(1)
|
||||
end
|
||||
puts END_SEPARATOR
|
||||
end
|
||||
|
||||
def run_tests_invalid cmds
|
||||
puts
|
||||
puts @title
|
||||
puts TITLE_SEPARATOR
|
||||
cmds.each do |cmd|
|
||||
command = create_cmd(cmd)
|
||||
s = `#{command}`
|
||||
if $?.success?
|
||||
puts FAILED
|
||||
exit(1)
|
||||
else
|
||||
puts SUCCESS
|
||||
end
|
||||
end
|
||||
puts END_SEPARATOR
|
||||
end
|
||||
|
||||
def puts_error str
|
||||
puts "\e[31m#{str}\e[0m"
|
||||
end
|
||||
|
||||
def puts_warn str
|
||||
puts "\e[33m#{str}\e[0m"
|
||||
end
|
||||
|
||||
def config= conf
|
||||
@config = conf
|
||||
end
|
||||
|
||||
def create_cmd cmd
|
||||
command = if @config.nil?
|
||||
"#{COMMAND} #{cmd}"
|
||||
else
|
||||
"#{COMMAND} -c #{@config} #{cmd}"
|
||||
end
|
||||
print "#{command}...\t"
|
||||
command
|
||||
end
|
||||
|
||||
end
|
||||
284
devops-client/tests/create_server.rb
Normal file
284
devops-client/tests/create_server.rb
Normal file
@ -0,0 +1,284 @@
|
||||
require "./base_test"
|
||||
require "json"
|
||||
|
||||
class CreateServer < BaseTest
|
||||
TITLE = "Create server tests. "
|
||||
|
||||
def run
|
||||
openstack = {
|
||||
:name => "openstack",
|
||||
:image => "36dc7618-4178-4e29-be43-286fbfe90f50",
|
||||
:flavor => "m1.small",
|
||||
:ssh_user => "root",
|
||||
:server_name => "test_create_server_openstack",
|
||||
:states => {:pause => "PAUSED", :unpause => "ACTIVE"}
|
||||
}
|
||||
ec2 = {
|
||||
:name => "ec2",
|
||||
:image => "ami-83e4bcea",
|
||||
:flavor => "m1.small",
|
||||
:ssh_user => "ec2-user",
|
||||
:server_name => "test_create_server_ec2",
|
||||
:states => {:pause => "stopped", :unpause => "running"}
|
||||
}
|
||||
|
||||
project = {
|
||||
"name" => "project_test",
|
||||
"deploy_envs" => [
|
||||
{
|
||||
"flavor" => openstack[:flavor],
|
||||
"groups" => [
|
||||
"default"
|
||||
],
|
||||
"identifier" => "openstack",
|
||||
"image" => openstack[:image],
|
||||
"provider" => "openstack",
|
||||
"run_list" => [
|
||||
"role[project_test_openstack]"
|
||||
],
|
||||
"subnets" => [ "private" ],
|
||||
"users" => [ "user_for_testing" ],
|
||||
"expires" => nil
|
||||
},
|
||||
{
|
||||
"flavor" => ec2[:flavor],
|
||||
"groups" => [
|
||||
"default"
|
||||
],
|
||||
"identifier" => "ec2",
|
||||
"image" => ec2[:image],
|
||||
"provider" => "ec2",
|
||||
"run_list" => [
|
||||
"role[project_test_ec2]"
|
||||
],
|
||||
"subnets" => [],
|
||||
"users" => [ "user_for_testing" ],
|
||||
"expires" => nil
|
||||
}
|
||||
|
||||
]
|
||||
}
|
||||
|
||||
self.config = CONFIGS[0]
|
||||
|
||||
prepare openstack
|
||||
prepare ec2
|
||||
|
||||
env_os = project["deploy_envs"][0]
|
||||
env_ec2 = project["deploy_envs"][1]
|
||||
self.title = TITLE + "Create project '#{project["name"]}'"
|
||||
run_tests [
|
||||
"project create #{project["name"]} --groups #{env_os["groups"].join(",")} --deploy_env #{env_os["identifier"]} --subnets #{env_os["subnets"].join(",")} --flavor #{env_os["flavor"]} --image #{env_os["image"]} --run_list role[#{project["name"]}_#{env_os["identifier"]}] --users #{env_os["users"].join(",")} --provider openstack -y --no_expires",
|
||||
"project create #{project["name"]} --groups #{env_ec2["groups"].join(",")} --deploy_env #{env_ec2["identifier"]} --flavor #{env_ec2["flavor"]} --image #{env_ec2["image"]} --run_list role[#{project["name"]}_#{env_ec2["identifier"]}] --users #{env_ec2["users"].join(",")} --provider ec2 -y --no_expires"
|
||||
]
|
||||
|
||||
self.title = TITLE + "Project list"
|
||||
run_test_with_block "project list --format json" do |l|
|
||||
projects = JSON.parse(l)
|
||||
projects.include? project["name"]
|
||||
end
|
||||
|
||||
self.title = TITLE + "Show project '#{project["name"]}'"
|
||||
run_test_with_block "project show #{project["name"]} --format json" do |p|
|
||||
pr = JSON.parse(p)
|
||||
name = (project["name"] == pr["name"])
|
||||
envs = (project["deploy_envs"].size == pr["deploy_envs"].size)
|
||||
o = pr["deploy_envs"].detect{|e| e["identifier"] == "openstack"}
|
||||
po = project["deploy_envs"][0]
|
||||
e = pr["deploy_envs"].detect{|e| e["identifier"] == "ec2"}
|
||||
pe = project["deploy_envs"][1]
|
||||
unless name
|
||||
puts "Project name is not a '#{project["name"]}'"
|
||||
end
|
||||
unless envs
|
||||
puts "Project environments not equals #{project["deploy_envs"].size}"
|
||||
end
|
||||
name and envs and check_envs(po, o) and check_envs(pe, e)
|
||||
end
|
||||
|
||||
self.title = TITLE + "Add user 'root' to project '#{project["name"]}'"
|
||||
run_tests [ "project user add #{project["name"]} root" ]
|
||||
|
||||
self.title = TITLE + "Show project '#{project["name"]}' with user 'root'"
|
||||
run_test_with_block "project show #{project["name"]} --format json" do |p|
|
||||
pr = JSON.parse(p)
|
||||
envs = true
|
||||
pr["deploy_envs"].each {|e| envs = (envs and e["users"].include?("root"))}
|
||||
envs
|
||||
end
|
||||
|
||||
self.title = TITLE + "Delete user 'root' from project '#{project["name"]}'"
|
||||
run_tests [ "project user delete #{project["name"]} root -y" ]
|
||||
|
||||
self.title = TITLE + "Show project '#{project["name"]}' without user 'root'"
|
||||
run_test_with_block "project show #{project["name"]} --format json" do |p|
|
||||
pr = JSON.parse(p)
|
||||
envs = true
|
||||
pr["deploy_envs"].each {|e| envs = (envs and !e["users"].include?("root"))}
|
||||
envs
|
||||
end
|
||||
|
||||
create_server project["name"], env_os["identifier"], openstack
|
||||
create_server project["name"], env_ec2["identifier"], ec2
|
||||
|
||||
self.title = TITLE + "Delete project '#{project["name"]}'"
|
||||
run_tests [ "project delete #{project["name"]} -y" ]
|
||||
|
||||
clear openstack
|
||||
clear ec2
|
||||
|
||||
end
|
||||
|
||||
def check_envs origin, created
|
||||
r = true
|
||||
%w(flavor groups identifier image provider run_list subnets users expires).each do |key|
|
||||
flag = (origin[key] == created[key])
|
||||
unless flag
|
||||
puts "Environments params '#{key}' not equals ('#{origin[key].inspect}' and '#{created[key].inspect}')"
|
||||
end
|
||||
r = r and flag
|
||||
end
|
||||
r
|
||||
end
|
||||
|
||||
def prepare conf
|
||||
name = conf[:name]
|
||||
self.title = TITLE + "Check #{name} flavor"
|
||||
run_test_with_block "flavor list #{name} --format json" do |f|
|
||||
flavors = JSON.parse(f)
|
||||
!flavors.detect{|o| o["id"] == conf[:flavor]}.nil?
|
||||
end
|
||||
|
||||
image_in_filter = false
|
||||
self.title = TITLE + "Check #{name} filter"
|
||||
run_test_with_block "filter image list #{name} --format json" do |i|
|
||||
images = JSON.parse(i)
|
||||
image_in_filter = !images.index(conf[:image]).nil?
|
||||
true
|
||||
end
|
||||
|
||||
if image_in_filter
|
||||
puts_warn "Image '#{conf[:image]}' for '#{name}' already in filter"
|
||||
else
|
||||
self.title = TITLE + "Add #{name} filter"
|
||||
run_tests [ "filter image add #{name} #{conf[:image]}" ]
|
||||
end
|
||||
|
||||
image_created = false
|
||||
self.title = TITLE + "Check image for #{name}"
|
||||
run_test_with_block "image list #{name} --format json" do |s|
|
||||
images = JSON.parse s
|
||||
image_created = !images.detect{|i| i["id"] == conf[:image]}.nil?
|
||||
true
|
||||
end
|
||||
|
||||
if image_created
|
||||
puts_warn "Image '#{conf[:image]}' for '#{name}' already created"
|
||||
else
|
||||
self.title = TITLE + "Create image for #{name}"
|
||||
run_tests [ "image create --image #{conf[:image]} --ssh_user #{conf[:ssh_user]} --provider #{name} --no_bootstrap_template -y" ]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def create_server project, env, conf
|
||||
|
||||
self.title = TITLE + "Create server '#{conf[:server_name]}'"
|
||||
run_tests [ "server create #{project} #{env} -N #{conf[:server_name]}" ]
|
||||
|
||||
self.title = TITLE + "Is server '#{conf[:server_name]}' created"
|
||||
run_test_with_block "server list --format json" do |l|
|
||||
servers = JSON.parse l
|
||||
!servers.detect{|s| s["chef_node_name"] == conf[:server_name].to_s }.nil?
|
||||
end
|
||||
|
||||
self.title = TITLE + "Pause server '#{conf[:server_name]}'"
|
||||
run_tests [ "server pause #{conf[:server_name]}" ]
|
||||
delay = (conf[:name] == "openstack" ? 5 : 90)
|
||||
puts "Sleeping for #{delay} seconds"
|
||||
sleep(delay)
|
||||
|
||||
self.title = TITLE + "Check server '#{conf[:server_name]}' state"
|
||||
run_test_with_block "server list #{conf[:name]} --format json" do |s|
|
||||
servers = JSON.parse s
|
||||
state = servers.detect{|o| o["name"] == conf[:server_name]}["state"]
|
||||
if state == conf[:states][:pause]
|
||||
true
|
||||
else
|
||||
puts_error "State should be '#{conf[:states][:pause]}' but it is '#{state}'"
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
self.title = TITLE + "Unpause server '#{conf[:server_name]}'"
|
||||
run_tests [ "server unpause #{conf[:server_name]}" ]
|
||||
delay = (conf[:name] == "openstack" ? 5 : 90)
|
||||
puts "Sleeping for #{delay} seconds"
|
||||
sleep(delay)
|
||||
|
||||
self.title = TITLE + "Check server '#{conf[:server_name]}' state"
|
||||
run_test_with_block "server list #{conf[:name]} --format json" do |s|
|
||||
servers = JSON.parse s
|
||||
state = servers.detect{|o| o["name"] == conf[:server_name]}["state"]
|
||||
if state == conf[:states][:unpause]
|
||||
true
|
||||
else
|
||||
puts_error "State should be '#{conf[:states][:unpause]}' but it is '#{state}'"
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
tag = "tag_" + conf[:name]
|
||||
self.title = TITLE + "Add tag '#{tag}' to server '#{conf[:server_name]}'"
|
||||
run_tests [
|
||||
"tag create #{conf[:server_name]} #{tag}",
|
||||
"tag create #{conf[:server_name]} #{tag}"
|
||||
]
|
||||
self.title = TITLE + "Check tag '#{tag}' for server '#{conf[:server_name]}'"
|
||||
run_test_with_block "tag list #{conf[:server_name]} --format json" do |t|
|
||||
JSON.parse(t).include?(tag)
|
||||
end
|
||||
|
||||
tag2 = tag + "_2"
|
||||
self.title = TITLE + "Check deploy with tag '#{tag2}' for server '#{conf[:server_name]}'"
|
||||
run_tests ["deploy #{conf[:server_name]} -t #{tag2}"]
|
||||
|
||||
self.title = TITLE + "Check tag '#{tag}' for server '#{conf[:server_name]}'"
|
||||
run_test_with_block "tag list #{conf[:server_name]} --format json" do |t|
|
||||
JSON.parse(t).include?(tag)
|
||||
!JSON.parse(t).include?(tag2)
|
||||
end
|
||||
|
||||
self.title = TITLE + "Delete tag '#{tag}' from server '#{conf[:server_name]}'"
|
||||
run_tests [
|
||||
"tag delete #{conf[:server_name]} #{tag} -y",
|
||||
"tag delete #{conf[:server_name]} #{tag} -y"
|
||||
]
|
||||
|
||||
self.title = TITLE + "Delete server '#{conf[:server_name]}'"
|
||||
run_tests [ "server delete #{conf[:server_name]} -y" ]
|
||||
|
||||
end
|
||||
|
||||
def clear conf
|
||||
name = conf[:name]
|
||||
self.title = TITLE + "Delete image for #{name}"
|
||||
run_tests [ "image delete #{conf[:image]} -y" ]
|
||||
|
||||
self.title = TITLE + "Check image for #{name}"
|
||||
run_test_with_block "image list #{name} --format json" do |s|
|
||||
images = JSON.parse s
|
||||
images.detect{|i| i["id"] == conf[:image]}.nil?
|
||||
end
|
||||
|
||||
self.title = TITLE + "Delete #{name} filter"
|
||||
run_tests [ "filter image delete #{name} #{conf[:image]} -y" ]
|
||||
|
||||
self.title = TITLE + "Check #{name} filter"
|
||||
run_test_with_block "filter image list #{name} --format json" do |i|
|
||||
images = JSON.parse(i)
|
||||
images.index(conf[:image]).nil?
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
5
devops-client/tests/devops-client-test.conf
Normal file
5
devops-client/tests/devops-client-test.conf
Normal file
@ -0,0 +1,5 @@
|
||||
api=v2.0
|
||||
host=<devops_host>
|
||||
username=<devops_user>
|
||||
password=<devops_password>
|
||||
|
||||
21
devops-client/tests/flavor.rb
Normal file
21
devops-client/tests/flavor.rb
Normal file
@ -0,0 +1,21 @@
|
||||
require "./base_test"
|
||||
|
||||
class Flavor < BaseTest
|
||||
TITLE = "Flavor tests"
|
||||
|
||||
def run
|
||||
self.title = TITLE
|
||||
run_tests [
|
||||
"flavor list ec2",
|
||||
"flavor list openstack",
|
||||
"flavor list ec2 --format json",
|
||||
"flavor list openstack --format json"
|
||||
]
|
||||
self.title = TITLE + " invalid "
|
||||
run_tests_invalid [
|
||||
"flavor list",
|
||||
"flavor"
|
||||
]
|
||||
end
|
||||
|
||||
end
|
||||
20
devops-client/tests/group.rb
Normal file
20
devops-client/tests/group.rb
Normal file
@ -0,0 +1,20 @@
|
||||
class Group < BaseTest
|
||||
TITLE = "Group tests"
|
||||
|
||||
def run
|
||||
self.title = TITLE
|
||||
run_tests [
|
||||
"group list ec2",
|
||||
"group list openstack",
|
||||
"group list ec2 --format json",
|
||||
"group list openstack --format json"
|
||||
]
|
||||
self.title = TITLE + " invalid "
|
||||
run_tests_invalid [
|
||||
"group list",
|
||||
"group"
|
||||
]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
25
devops-client/tests/image.rb
Normal file
25
devops-client/tests/image.rb
Normal file
@ -0,0 +1,25 @@
|
||||
require "./base_test"
|
||||
class Image < BaseTest
|
||||
TITLE = "Image tests"
|
||||
|
||||
def run
|
||||
self.title = TITLE
|
||||
image_o = {
|
||||
:provider => "openstack",
|
||||
:id => "89ecfe3f-9f25-4982-a0cf-b9b3814c02d6"
|
||||
}
|
||||
run_tests [ "image list",
|
||||
"image list ec2",
|
||||
"image list openstack",
|
||||
"image list provider ec2",
|
||||
"image list provider openstack"
|
||||
# "image create --image RHEL-6.4_GA-x86_64-7-Hourly2 --ssh_user root --no_bootstrap_template -y --provider ec2",
|
||||
# "image create --image ubuntu-12.04-qcow-amd64 --ssh_user root --no_bootstrap_template -y --provider openstack",
|
||||
# "image show cirros",
|
||||
# "image update cirros ./image_update_test_file",
|
||||
# "image delete cirros"
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
# test/test
|
||||
6
devops-client/tests/image_update_test_file
Normal file
6
devops-client/tests/image_update_test_file
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"provider": "openstack",
|
||||
"image_id": "89ecfe3f-9f25-4982-a0cf-b9b3814c02d6",
|
||||
"remote_user": "root",
|
||||
"id": "cirros"
|
||||
}
|
||||
44
devops-client/tests/key.rb
Normal file
44
devops-client/tests/key.rb
Normal file
@ -0,0 +1,44 @@
|
||||
require "./base_test"
|
||||
|
||||
class Key < BaseTest
|
||||
TITLE = "Key tests - "
|
||||
|
||||
def run
|
||||
self.title = TITLE
|
||||
run_tests [
|
||||
"key list"
|
||||
]
|
||||
|
||||
key = "test_key"
|
||||
self.title = TITLE + "add"
|
||||
run_tests [
|
||||
"key add #{key} key_file"
|
||||
]
|
||||
self.title = TITLE + "add, invalid"
|
||||
run_tests_invalid [
|
||||
"key add #{key} key_file"
|
||||
]
|
||||
self.title = TITLE + "check"
|
||||
run_test_with_block "key list --format json" do |k|
|
||||
!JSON.parse(k).detect{|jk| jk["id"] == key and jk["scope"] == "user"}.nil?
|
||||
end
|
||||
|
||||
self.title = TITLE + "delete"
|
||||
run_tests [
|
||||
"key delete #{key} -y"
|
||||
]
|
||||
self.title = TITLE + "delete, invalid"
|
||||
run_tests_invalid [
|
||||
"key delete #{key} -y"
|
||||
]
|
||||
self.title = TITLE + "invalid"
|
||||
run_tests_invalid [
|
||||
"key",
|
||||
"key add",
|
||||
"key add #{key}",
|
||||
"key delete"
|
||||
]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
1
devops-client/tests/key_file
Normal file
1
devops-client/tests/key_file
Normal file
@ -0,0 +1 @@
|
||||
this is the test file
|
||||
23
devops-client/tests/network.rb
Normal file
23
devops-client/tests/network.rb
Normal file
@ -0,0 +1,23 @@
|
||||
require "./base_test"
|
||||
|
||||
class Network < BaseTest
|
||||
TITLE = "Network tests"
|
||||
|
||||
def run
|
||||
self.title = TITLE
|
||||
run_tests [
|
||||
"network list openstack",
|
||||
"network list ec2",
|
||||
"network list openstack --format json",
|
||||
"network list ec2 --format json"
|
||||
]
|
||||
self.title = TITLE + " invalid "
|
||||
run_tests_invalid [
|
||||
"network list",
|
||||
"network"
|
||||
]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
35
devops-client/tests/output.rb
Normal file
35
devops-client/tests/output.rb
Normal file
@ -0,0 +1,35 @@
|
||||
require "./base_test"
|
||||
|
||||
class Output < BaseTest
|
||||
TITLE = "Output tests"
|
||||
|
||||
def run
|
||||
tests = {
|
||||
:server => ["list"],
|
||||
:flavor => ["list ec2", "list openstack"],
|
||||
:network => ["list ec2", "list openstack"],
|
||||
:group => ["list ec2", "list openstack"],
|
||||
:templates => ["list"],
|
||||
:provider => ["list"],
|
||||
:filter => ["image list ec2", "image list openstack"],
|
||||
:image => ["list", "list provider", "list provider ec2", "list provider openstack"],
|
||||
:key => ["list"],
|
||||
:project => ["list"],
|
||||
:script => ["list"],
|
||||
:server => ["list"],
|
||||
:tag => ["list"],
|
||||
:user => ["list"]
|
||||
}
|
||||
["table", "json", "csv"].each do |f|
|
||||
self.title = TITLE + ", format '#{f}'"
|
||||
c = []
|
||||
tests.each do |k,v|
|
||||
v.each do |cmd|
|
||||
c.push "#{k} #{cmd}"
|
||||
end
|
||||
end
|
||||
run_tests c, false
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
16
devops-client/tests/project.rb
Normal file
16
devops-client/tests/project.rb
Normal file
@ -0,0 +1,16 @@
|
||||
require "./base_test"
|
||||
|
||||
class Project < BaseTest
|
||||
TITLE = "Project tests"
|
||||
|
||||
def run
|
||||
self.title = TITLE
|
||||
run_tests ["project list"]
|
||||
run_tests ["project create endtest --groups default --deploy_env dev --flavor c1.small --image cirros --run_list role[devops_service_dev] -y"]
|
||||
run_tests ["project show test"]
|
||||
run_tests ["project servers test"]
|
||||
run_tests ["project set run_list test dev role[devops_service_dev]"]
|
||||
run_tests ["project update test project_update_test_file"]
|
||||
run_tests ["project delete test"]
|
||||
end
|
||||
end
|
||||
19
devops-client/tests/provider.rb
Normal file
19
devops-client/tests/provider.rb
Normal file
@ -0,0 +1,19 @@
|
||||
require "./base_test"
|
||||
|
||||
class Provider < BaseTest
|
||||
TITLE = "Provider tests"
|
||||
|
||||
def run
|
||||
self.title = TITLE
|
||||
run_tests [
|
||||
"provider list",
|
||||
"provider list --format json"
|
||||
]
|
||||
self.title = TITLE + " invalid "
|
||||
run_tests_invalid [
|
||||
"provider"
|
||||
]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
44
devops-client/tests/run_tests.rb
Normal file
44
devops-client/tests/run_tests.rb
Normal file
@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
dir = File.dirname(__FILE__)
|
||||
tests = nil
|
||||
if ARGV.empty?
|
||||
tests = ["flavor", "group", "network", "provider", "user", "key", "script", "image", "server", "project", "create_server"]
|
||||
else
|
||||
tests = ARGV
|
||||
end
|
||||
|
||||
classes = []
|
||||
tests.each do |f|
|
||||
require "#{dir}/#{f}.rb"
|
||||
case f
|
||||
when "flavor"
|
||||
classes.push Flavor.new
|
||||
when "group"
|
||||
classes.push Group.new
|
||||
when "network"
|
||||
classes.push Network.new
|
||||
when "provider"
|
||||
classes.push Provider.new
|
||||
when "user"
|
||||
classes.push User.new
|
||||
when "key"
|
||||
classes.push Key.new
|
||||
when "script"
|
||||
classes.push Script.new
|
||||
when "image"
|
||||
classes.push Image.new
|
||||
when "project"
|
||||
classes.push Project.new
|
||||
when "server"
|
||||
classes.push Server.new
|
||||
when "output"
|
||||
classes.push Output.new
|
||||
when "create_server"
|
||||
classes.push CreateServer.new
|
||||
end
|
||||
end
|
||||
|
||||
classes.each do |c|
|
||||
c.run
|
||||
end
|
||||
47
devops-client/tests/script.rb
Normal file
47
devops-client/tests/script.rb
Normal file
@ -0,0 +1,47 @@
|
||||
require "./base_test"
|
||||
|
||||
class Script < BaseTest
|
||||
TITLE = "Script tests - "
|
||||
|
||||
def run
|
||||
self.title = TITLE
|
||||
run_tests [
|
||||
"script list"
|
||||
]
|
||||
|
||||
script = "test_script"
|
||||
self.title = TITLE + "add"
|
||||
run_tests [
|
||||
"script add #{script} script_file.sh"
|
||||
]
|
||||
self.title = TITLE + "add, invalid"
|
||||
run_tests_invalid [
|
||||
"script add #{script} script_file.sh"
|
||||
]
|
||||
self.title = TITLE + "check"
|
||||
run_test_with_block "script list --format json" do |s|
|
||||
JSON.parse(s).include?(script)
|
||||
end
|
||||
|
||||
self.title = TITLE + "delete"
|
||||
run_tests [
|
||||
"script delete #{script} -y"
|
||||
]
|
||||
self.title = TITLE + "delete, invalid"
|
||||
run_tests_invalid [
|
||||
"script delete #{script} -y"
|
||||
]
|
||||
|
||||
self.title = TITLE + "invalid"
|
||||
run_tests_invalid [
|
||||
"script",
|
||||
"script create",
|
||||
"script create #{script}",
|
||||
"script delete",
|
||||
"script run",
|
||||
"script run #{script}"
|
||||
]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
3
devops-client/tests/script_file.sh
Normal file
3
devops-client/tests/script_file.sh
Normal file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "Hello! I'm a test file"
|
||||
12
devops-client/tests/server.rb
Normal file
12
devops-client/tests/server.rb
Normal file
@ -0,0 +1,12 @@
|
||||
class Server < BaseTest
|
||||
TITLE = "Image tests"
|
||||
|
||||
def run
|
||||
self.title = TITLE
|
||||
run_tests [
|
||||
"server list",
|
||||
"server list openstack",
|
||||
"server list ec2"
|
||||
]
|
||||
end
|
||||
end
|
||||
74
devops-client/tests/user.rb
Normal file
74
devops-client/tests/user.rb
Normal file
@ -0,0 +1,74 @@
|
||||
require "./base_test"
|
||||
|
||||
class User < BaseTest
|
||||
TITLE = "User tests - "
|
||||
|
||||
def run
|
||||
user = "test_user"
|
||||
psw = "test"
|
||||
self.title = TITLE + "list"
|
||||
run_tests [
|
||||
"user list"
|
||||
]
|
||||
self.title = TITLE + "create"
|
||||
run_tests [
|
||||
"user create #{user} --password #{psw}"
|
||||
]
|
||||
self.title = TITLE + "create, invalid"
|
||||
run_tests_invalid [
|
||||
"user create #{user} --password #{psw}"
|
||||
]
|
||||
|
||||
run_test_with_block "user list --format json" do |o|
|
||||
!JSON.parse(o).detect{|u| u["id"] == user}.nil?
|
||||
end
|
||||
|
||||
self.title = TITLE + "grant"
|
||||
cmds = %w{flavor group image project server key user filter network provider script}
|
||||
p = %w{r w rw}
|
||||
cmds.each do |c|
|
||||
p.each do |pr|
|
||||
self.title = TITLE + "grant #{c} #{pr}"
|
||||
run_tests ["user grant #{user} #{c} #{pr}"]
|
||||
run_test_with_block "user list --format json" do |o|
|
||||
u = JSON.parse(o).detect{|u| u["id"] == user}
|
||||
u["privileges"][c] == pr
|
||||
end
|
||||
end
|
||||
end
|
||||
p.push("")
|
||||
p.each do |pr|
|
||||
self.title = TITLE + "grant all #{pr}"
|
||||
run_tests ["user grant #{user} all #{pr}"]
|
||||
run_test_with_block "user list --format json" do |o|
|
||||
u = JSON.parse(o).detect{|u| u["id"] == user}
|
||||
u["privileges"].each do |cmd, p|
|
||||
unless p == pr
|
||||
puts_error "#{cmd} should be equals '#{pr}' but it is '#{p}'"
|
||||
end
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self.title = TITLE + "delete"
|
||||
run_tests [
|
||||
"user delete #{user} -y"
|
||||
]
|
||||
self.title = TITLE + "delete invalid"
|
||||
run_tests_invalid [
|
||||
"user delete #{user} -y"
|
||||
]
|
||||
|
||||
self.title = TITLE + "invalid"
|
||||
run_tests_invalid [
|
||||
"user",
|
||||
"user create",
|
||||
"user delete",
|
||||
"user grant",
|
||||
"user password"
|
||||
]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
14
devops-service/Gemfile
Normal file
14
devops-service/Gemfile
Normal file
@ -0,0 +1,14 @@
|
||||
source 'https://rubygems.org'
|
||||
|
||||
gem "thin", "~>1.5.1"
|
||||
gem "mime-types", "~>1.25.1"
|
||||
gem "sinatra", "1.4.3"
|
||||
gem "sinatra-contrib", "1.4.1"
|
||||
gem "sinatra-websocket", "~>0.3.0"
|
||||
gem "fog", "~>1.20"
|
||||
gem "mixlib-shellout"
|
||||
gem "chef", ">=11"
|
||||
gem "mongo"
|
||||
gem "bson_ext"
|
||||
gem "multi_json"
|
||||
gem "rufus-scheduler", "2.0.24"
|
||||
148
devops-service/Gemfile.lock
Normal file
148
devops-service/Gemfile.lock
Normal file
@ -0,0 +1,148 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
addressable (2.3.5)
|
||||
atomic (1.1.14)
|
||||
backports (3.6.0)
|
||||
bson (1.9.2)
|
||||
bson_ext (1.9.2)
|
||||
bson (~> 1.9.2)
|
||||
builder (3.2.2)
|
||||
chef (11.10.4)
|
||||
chef-zero (~> 1.7, >= 1.7.2)
|
||||
diff-lcs (~> 1.2, >= 1.2.4)
|
||||
erubis (~> 2.7)
|
||||
highline (~> 1.6, >= 1.6.9)
|
||||
json (>= 1.4.4, <= 1.8.1)
|
||||
mime-types (~> 1.16)
|
||||
mixlib-authentication (~> 1.3)
|
||||
mixlib-cli (~> 1.4)
|
||||
mixlib-config (~> 2.0)
|
||||
mixlib-log (~> 1.3)
|
||||
mixlib-shellout (~> 1.3)
|
||||
net-ssh (~> 2.6)
|
||||
net-ssh-multi (~> 1.1)
|
||||
ohai (~> 6.0)
|
||||
pry (~> 0.9)
|
||||
puma (~> 1.6)
|
||||
rest-client (>= 1.0.4, < 1.7.0)
|
||||
yajl-ruby (~> 1.1)
|
||||
chef-zero (1.7.3)
|
||||
hashie (~> 2.0)
|
||||
json
|
||||
mixlib-log (~> 1.3)
|
||||
moneta (< 0.7.0)
|
||||
rack
|
||||
coderay (1.1.0)
|
||||
daemons (1.1.9)
|
||||
diff-lcs (1.2.5)
|
||||
em-websocket (0.3.8)
|
||||
addressable (>= 2.1.1)
|
||||
eventmachine (>= 0.12.9)
|
||||
erubis (2.7.0)
|
||||
eventmachine (1.0.3)
|
||||
excon (0.31.0)
|
||||
fog (1.20.0)
|
||||
builder
|
||||
excon (~> 0.31.0)
|
||||
formatador (~> 0.2.0)
|
||||
mime-types
|
||||
multi_json (~> 1.0)
|
||||
net-scp (~> 1.1)
|
||||
net-ssh (>= 2.1.3)
|
||||
nokogiri (>= 1.5.11)
|
||||
formatador (0.2.4)
|
||||
hashie (2.0.5)
|
||||
highline (1.6.20)
|
||||
ipaddress (0.8.0)
|
||||
json (1.8.1)
|
||||
method_source (0.8.2)
|
||||
mime-types (1.25.1)
|
||||
mini_portile (0.5.2)
|
||||
mixlib-authentication (1.3.0)
|
||||
mixlib-log
|
||||
mixlib-cli (1.4.0)
|
||||
mixlib-config (2.1.0)
|
||||
mixlib-log (1.6.0)
|
||||
mixlib-shellout (1.3.0)
|
||||
moneta (0.6.0)
|
||||
mongo (1.9.2)
|
||||
bson (~> 1.9.2)
|
||||
multi_json (1.8.4)
|
||||
net-scp (1.1.2)
|
||||
net-ssh (>= 2.6.5)
|
||||
net-ssh (2.8.0)
|
||||
net-ssh-gateway (1.2.0)
|
||||
net-ssh (>= 2.6.5)
|
||||
net-ssh-multi (1.2.0)
|
||||
net-ssh (>= 2.6.5)
|
||||
net-ssh-gateway (>= 1.2.0)
|
||||
nokogiri (1.6.1)
|
||||
mini_portile (~> 0.5.0)
|
||||
ohai (6.20.0)
|
||||
ipaddress
|
||||
mixlib-cli
|
||||
mixlib-config
|
||||
mixlib-log
|
||||
mixlib-shellout
|
||||
systemu (~> 2.5.2)
|
||||
yajl-ruby
|
||||
pry (0.9.12.6)
|
||||
coderay (~> 1.0)
|
||||
method_source (~> 0.8)
|
||||
slop (~> 3.4)
|
||||
puma (1.6.3)
|
||||
rack (~> 1.2)
|
||||
rack (1.5.2)
|
||||
rack-protection (1.5.2)
|
||||
rack
|
||||
rack-test (0.6.2)
|
||||
rack (>= 1.0)
|
||||
rest-client (1.6.7)
|
||||
mime-types (>= 1.16)
|
||||
rufus-scheduler (2.0.24)
|
||||
tzinfo (>= 0.3.22)
|
||||
sinatra (1.4.3)
|
||||
rack (~> 1.4)
|
||||
rack-protection (~> 1.4)
|
||||
tilt (~> 1.3, >= 1.3.4)
|
||||
sinatra-contrib (1.4.1)
|
||||
backports (>= 2.0)
|
||||
multi_json
|
||||
rack-protection
|
||||
rack-test
|
||||
sinatra (~> 1.4.0)
|
||||
tilt (~> 1.3)
|
||||
sinatra-websocket (0.3.0)
|
||||
em-websocket (~> 0.3.6)
|
||||
eventmachine
|
||||
thin (>= 1.3.1)
|
||||
slop (3.4.7)
|
||||
systemu (2.5.2)
|
||||
thin (1.5.1)
|
||||
daemons (>= 1.0.9)
|
||||
eventmachine (>= 0.12.6)
|
||||
rack (>= 1.0.0)
|
||||
thread_safe (0.1.3)
|
||||
atomic
|
||||
tilt (1.4.1)
|
||||
tzinfo (1.1.0)
|
||||
thread_safe (~> 0.1)
|
||||
yajl-ruby (1.2.0)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
bson_ext
|
||||
chef (>= 11)
|
||||
fog (~> 1.20)
|
||||
mime-types (~> 1.25.1)
|
||||
mixlib-shellout
|
||||
mongo
|
||||
multi_json
|
||||
rufus-scheduler (= 2.0.24)
|
||||
sinatra (= 1.4.3)
|
||||
sinatra-contrib (= 1.4.1)
|
||||
sinatra-websocket (~> 0.3.0)
|
||||
thin (~> 1.5.1)
|
||||
32
devops-service/client.rb
Normal file
32
devops-service/client.rb
Normal file
@ -0,0 +1,32 @@
|
||||
require 'sinatra/base'
|
||||
|
||||
class Client < Sinatra::Base
|
||||
|
||||
def initialize config
|
||||
super()
|
||||
@@config = config
|
||||
end
|
||||
|
||||
# Route to download devops client
|
||||
get "/devops-client.gem" do
|
||||
begin
|
||||
send_file @@config[:client_file]
|
||||
rescue
|
||||
msg = "No file '#{@@config[:client_file]}' found"
|
||||
logger.error msg
|
||||
return [404, msg]
|
||||
end
|
||||
end
|
||||
|
||||
# Route to get client documentation
|
||||
get "/ru/index.html" do
|
||||
file = File.join(@@config[:public_dir], "ru_index.html")
|
||||
if File.exist? file
|
||||
File.read(file)
|
||||
else
|
||||
logger.error "File '#{file}' does not exist"
|
||||
return [404, "File '/ru/index.html' does not exist"]
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
31
devops-service/commands/deploy.rb
Normal file
31
devops-service/commands/deploy.rb
Normal file
@ -0,0 +1,31 @@
|
||||
module DeployCommands
|
||||
|
||||
def deploy_server out, server, cert_path
|
||||
out << "\nRun chef-client on '#{server.chef_node_name}'"
|
||||
cmd = (server.remote_user == "root" ? "chef-client" : "sudo chef-client")
|
||||
ip = if server.public_ip.nil?
|
||||
server.private_ip
|
||||
else
|
||||
out << "Public IP detected\n"
|
||||
server.public_ip
|
||||
end
|
||||
cmd = "ssh -t -i #{cert_path} #{server.remote_user}@#{ip} \"#{cmd}\""
|
||||
out << "\nCommand: '#{cmd}'\n"
|
||||
status = nil
|
||||
IO.popen(cmd + " 2>&1") do |c|
|
||||
buf = ""
|
||||
while line = c.gets do
|
||||
out << line
|
||||
buf = line
|
||||
end
|
||||
c.close
|
||||
status = $?.to_i
|
||||
r = buf.scan(/exit\scode\s([0-9]{1,3})/)[0]
|
||||
unless r.nil?
|
||||
status = r[0].to_i
|
||||
end
|
||||
end
|
||||
return status
|
||||
end
|
||||
|
||||
end
|
||||
49
devops-service/commands/deploy_env.rb
Normal file
49
devops-service/commands/deploy_env.rb
Normal file
@ -0,0 +1,49 @@
|
||||
require "db/exceptions/invalid_record"
|
||||
require "commands/image"
|
||||
|
||||
module DeployEnvCommands
|
||||
|
||||
include ImageCommands
|
||||
|
||||
def check_expires! val
|
||||
raise InvalidRecord.new "Parameter 'expires' is invalid" if val.match(/^[0-9]+[smhdw]$/).nil?
|
||||
end
|
||||
|
||||
def check_flavor! p, val
|
||||
f = p.flavors.detect{|f| f["id"] == val}
|
||||
raise InvalidRecord.new "Invalid flavor '#{val}'" if f.nil?
|
||||
end
|
||||
|
||||
def check_image! p, val
|
||||
images = get_images(DevopsService.mongo, p.name)
|
||||
raise InvalidRecord.new "Invalid image '#{val}'" unless images.map{|i| i["id"]}.include?(val)
|
||||
end
|
||||
|
||||
def check_subnets_and_groups! p, subnets, groups
|
||||
networks = p.networks
|
||||
n = subnets - networks.map{|n| n["name"]}
|
||||
raise InvalidRecord.new "Invalid networks '#{n.join("', '")}'" unless n.empty?
|
||||
|
||||
filter = nil
|
||||
if p.name == ::Version2_0::Provider::Ec2::PROVIDER
|
||||
unless subnets.empty?
|
||||
subnets = [ subnets[0] ] if subnets.size > 1
|
||||
filter = {"vpc-id" => networks.detect{|n| n["name"] == subnets[0]}["vpcId"] }
|
||||
end
|
||||
elsif p.name == ::Version2_0::Provider::Openstack::PROVIDER
|
||||
if subnets.empty?
|
||||
raise InvalidRecord.new "Subnets array can not be empty"
|
||||
end
|
||||
end
|
||||
|
||||
g = groups - p.groups(filter).keys
|
||||
raise InvalidRecord.new "Invalid groups '#{g.join("', '")}'" unless g.empty?
|
||||
end
|
||||
|
||||
def check_users! val
|
||||
users = DevopsService.mongo.users_names(val)
|
||||
buf = val - users
|
||||
raise InvalidRecord.new("Invalid users: '#{buf.join("', '")}'") unless buf.empty?
|
||||
end
|
||||
|
||||
end
|
||||
13
devops-service/commands/image.rb
Normal file
13
devops-service/commands/image.rb
Normal file
@ -0,0 +1,13 @@
|
||||
require "providers/provider_factory"
|
||||
|
||||
module ImageCommands
|
||||
|
||||
def get_images mongo, provider
|
||||
filters = mongo.available_images(provider)
|
||||
if filters.empty?
|
||||
[]
|
||||
else
|
||||
::Version2_0::Provider::ProviderFactory.get(provider).images(filters)
|
||||
end
|
||||
end
|
||||
end
|
||||
69
devops-service/commands/knife_commands.rb
Normal file
69
devops-service/commands/knife_commands.rb
Normal file
@ -0,0 +1,69 @@
|
||||
require "json"
|
||||
|
||||
class KnifeCommands
|
||||
|
||||
def self.chef_node_list
|
||||
knife("node list")[0].split.map{|c| c.strip}
|
||||
end
|
||||
|
||||
def self.chef_client_list
|
||||
knife("client list")[0].split.map{|c| c.strip}
|
||||
end
|
||||
|
||||
def self.chef_node_delete name
|
||||
o = knife("node delete #{name} -y")[0]
|
||||
(o.nil? ? o : o.strip)
|
||||
end
|
||||
|
||||
def self.chef_client_delete name
|
||||
o = knife("client delete #{name} -y")[0]
|
||||
(o.nil? ? o : o.strip)
|
||||
end
|
||||
|
||||
def self.tags_list name
|
||||
knife("tag list #{name}")[0].split.map{|c| c.strip}
|
||||
end
|
||||
|
||||
def self.tags_create name, tagsStr
|
||||
knife("tag create #{name} #{tagsStr}")
|
||||
end
|
||||
|
||||
def self.tags_delete name, tagsStr
|
||||
knife("tag delete #{name} #{tagsStr}")
|
||||
end
|
||||
|
||||
def self.create_role project, env
|
||||
file = "/tmp/new_role.json"
|
||||
File.open(file, "w") do |f|
|
||||
f.puts <<-EOH
|
||||
{
|
||||
"name" : "#{project}_#{env}",
|
||||
"description": "",
|
||||
"json_class": "Chef::Role",
|
||||
"default_attributes": {
|
||||
"project": "#{project}",
|
||||
"env": "#{env}"
|
||||
},
|
||||
"override_attributes": {},
|
||||
"chef_type": "role",
|
||||
"run_list": [],
|
||||
"env_run_lists": {}
|
||||
}
|
||||
EOH
|
||||
end
|
||||
out = `knife role from file #{file}`
|
||||
raise "Cannot create role '#{project}_#{env}': #{out}" unless $?.success?
|
||||
true
|
||||
end
|
||||
|
||||
def self.roles
|
||||
o, s = knife("role list --format json")
|
||||
return (s ? JSON.parse(o) : nil)
|
||||
end
|
||||
|
||||
def self.knife cmd
|
||||
o = `knife #{cmd} 2>&1`
|
||||
return o, $?.success?
|
||||
end
|
||||
|
||||
end
|
||||
190
devops-service/commands/server.rb
Normal file
190
devops-service/commands/server.rb
Normal file
@ -0,0 +1,190 @@
|
||||
require "commands/knife_commands"
|
||||
require "db/exceptions/record_not_found"
|
||||
|
||||
module ServerCommands
|
||||
|
||||
def extract_servers provider, project, env, params, user, mongo
|
||||
flavors = provider.flavors
|
||||
projects = {}
|
||||
env_name = env.identifier
|
||||
project_name = project.id
|
||||
servers_info = []
|
||||
if project.multi?
|
||||
#TODO: fix multi project
|
||||
images = {}
|
||||
env.servers.each do |name, server|
|
||||
images[server["image"]] = mongo.image(server["image"]) unless images.has_key?(server["image"])
|
||||
flavor = flavors.detect {|f| f["name"] == server["flavor"]}
|
||||
raise RecordNotFound.new("Flavor with name '#{server["flavor"]}' not found") if flavor.nil?
|
||||
run_list = []
|
||||
project_ids = server["subprojects"].map{|sp| sp["project_id"]}
|
||||
db_subprojects = mongo.projects project_ids
|
||||
ids = project_ids - db_subprojects.map{|sp| sp.id}
|
||||
unless ids.empty?
|
||||
return [400, "Subproject(s) '#{ids.join("', '")}' is/are not exists"]
|
||||
end
|
||||
server["subprojects"].each do |sp|
|
||||
p = db_subprojects.detect{|db_sp| db_sp.id == sp["project_id"]}
|
||||
run_list += p.deploy_env(sp["project_env"]).run_list
|
||||
end
|
||||
o = {
|
||||
:image => images[server["image"]],
|
||||
:name => "#{name}_#{Time.now.to_i}",
|
||||
:flavor => flavor["id"],
|
||||
:groups => server["groups"],
|
||||
:run_list => run_list
|
||||
}
|
||||
servers_info.push(o)
|
||||
end
|
||||
else
|
||||
i = mongo.image env.image
|
||||
flavor = flavors.detect {|f| f["id"] == env.flavor}
|
||||
raise RecordNotFound.new("Flavor with id '#{env.flavor}' not found") if flavor.nil?
|
||||
o = {
|
||||
:image => i,
|
||||
:name => params["name"],
|
||||
:flavor => flavor["id"],
|
||||
:groups => params["groups"] || env.groups,
|
||||
:run_list => env.run_list,
|
||||
:subnets => env.subnets,
|
||||
:key => params["key"]
|
||||
}
|
||||
servers_info.push(o)
|
||||
end
|
||||
|
||||
servers = []
|
||||
servers_info.each do |info|
|
||||
image = info[:image]
|
||||
s = Server.new
|
||||
s.provider = provider.name
|
||||
s.project = project_name
|
||||
s.deploy_env = env_name
|
||||
s.remote_user = image.remote_user
|
||||
s.chef_node_name = info[:name] || "#{provider.ssh_key}-#{project_name}-#{env_name}-#{Time.now.to_i}"
|
||||
s.key = info[:key] || provider.ssh_key
|
||||
s.options = {
|
||||
:image => image.id,
|
||||
:flavor => info[:flavor],
|
||||
:name => info[:name],
|
||||
:groups => info[:groups],
|
||||
:run_list => info[:run_list],
|
||||
:bootstrap_template => image.bootstrap_template,
|
||||
:subnets => info[:subnets]
|
||||
}
|
||||
s.created_by = user
|
||||
servers.push s
|
||||
end
|
||||
return servers
|
||||
end
|
||||
|
||||
def delete_from_chef_server node_name
|
||||
{
|
||||
:chef_node => KnifeCommands.chef_node_delete(node_name),
|
||||
:chef_client => KnifeCommands.chef_client_delete(node_name)
|
||||
}
|
||||
end
|
||||
|
||||
def check_server s
|
||||
KnifeCommands.chef_node_list.include?(s.chef_node_name) and KnifeCommands.chef_client_list.include?(s.chef_node_name)
|
||||
end
|
||||
|
||||
def bootstrap s, out, cert_path, logger
|
||||
if s.private_ip.nil?
|
||||
out << "Error: Private IP is null"
|
||||
return false
|
||||
end
|
||||
ja = {
|
||||
:provider => s.provider,
|
||||
:devops_host => `hostname`.strip
|
||||
}
|
||||
bootstrap_options = [
|
||||
"-x #{s.remote_user}",
|
||||
"-i #{cert_path}",
|
||||
"--json-attributes '#{ja.to_json}'"
|
||||
]
|
||||
bootstrap_options.push "--sudo" unless s.remote_user == "root"
|
||||
bootstrap_options.push "-N #{s.chef_node_name}" if s.chef_node_name
|
||||
bootstrap_options.push "-d #{s.options[:bootstrap_template]}" if s.options[:bootstrap_template]
|
||||
bootstrap_options.push "-r #{s.options[:run_list].join(",")}" unless s.options[:run_list].empty?
|
||||
ip = s.private_ip
|
||||
unless s.public_ip.nil? || s.public_ip.strip.empty?
|
||||
ip = s.public_ip
|
||||
out << "\nPublic IP is present\n"
|
||||
end
|
||||
out << "\nWaiting for SSH..."
|
||||
i = 0
|
||||
begin
|
||||
sleep(1)
|
||||
`ssh -i #{cert_path} -q #{s.remote_user}@#{ip} exit`
|
||||
i += 1
|
||||
if i == 300
|
||||
res = `ssh -i #{cert_path} #{s.remote_user}@#{ip} "exit" 2>&1`
|
||||
out << "\nCan not connect to #{s.remote_user}@#{ip}"
|
||||
out << "\n" + res
|
||||
logger.error "Can not connect with command 'ssh -i #{cert_path} #{s.remote_user}@#{ip}':\n#{res}"
|
||||
return false
|
||||
end
|
||||
raise unless $?.success?
|
||||
rescue
|
||||
retry
|
||||
end
|
||||
|
||||
bootstrap_cmd = "knife bootstrap #{bootstrap_options.join(" ")} #{ip}"
|
||||
out << "\nExecuting '#{bootstrap_cmd}' \n\n"
|
||||
status = nil
|
||||
IO.popen(bootstrap_cmd + " 2>&1") do |bo|
|
||||
while line = bo.gets do
|
||||
out << line
|
||||
end
|
||||
bo.close
|
||||
status = $?.to_i
|
||||
end
|
||||
return status
|
||||
end
|
||||
|
||||
def unbootstrap s, cert_path
|
||||
i = 0
|
||||
begin
|
||||
`ssh -i #{cert_path} -q #{s.remote_user}@#{s.private_ip} rm -Rf /etc/chef`
|
||||
raise unless $?.success?
|
||||
rescue => e
|
||||
logger.error "Unbootstrap eeror: " + e.message
|
||||
i += 1
|
||||
sleep(1)
|
||||
retry unless i == 5
|
||||
end
|
||||
end
|
||||
|
||||
def delete_server s, mongo, logger
|
||||
if s.chef_node_name.nil?
|
||||
mongo.server_delete s.id
|
||||
msg = "Added server '#{s.id}' is removed"
|
||||
logger.info msg
|
||||
return msg, nil
|
||||
end
|
||||
r = delete_from_chef_server(s.chef_node_name)
|
||||
info = if s.static
|
||||
cert = mongo.key(s.key).path
|
||||
unbootstrap(s, cert)
|
||||
mongo.server_delete s.id
|
||||
msg = "Static server '#{s.id}' with name '#{s.chef_node_name}' for project '#{s.project}-#{s.deploy_env}' is removed"
|
||||
logger.info msg
|
||||
msg
|
||||
else
|
||||
provider = ::Version2_0::Provider::ProviderFactory.get(s.provider)
|
||||
begin
|
||||
r[:server] = provider.delete_server s.id
|
||||
rescue Fog::Compute::OpenStack::NotFound, Fog::Compute::AWS::NotFound
|
||||
r[:server] = "Server with id '#{s.id}' not found in '#{provider.name}' servers"
|
||||
logger.warn r[:server]
|
||||
end
|
||||
mongo.server_delete s.id
|
||||
msg = "Server '#{s.id}' with name '#{s.chef_node_name}' for project '#{s.project}-#{s.deploy_env}' is removed"
|
||||
logger.info msg
|
||||
msg
|
||||
end
|
||||
r.each{|key, log| logger.info("#{key} - #{log}")}
|
||||
return info, r
|
||||
end
|
||||
|
||||
end
|
||||
30
devops-service/commands/status.rb
Normal file
30
devops-service/commands/status.rb
Normal file
@ -0,0 +1,30 @@
|
||||
module StatusCommands
|
||||
|
||||
def create_status status
|
||||
s = if status.empty?
|
||||
1
|
||||
else
|
||||
b = 0
|
||||
status.each{|s| b |= s}
|
||||
b
|
||||
end
|
||||
return "\n-- Status: #{s} --"
|
||||
end
|
||||
|
||||
def time_diff_milli start, finish
|
||||
((finish - start) * 1000.0).to_i
|
||||
end
|
||||
|
||||
def time_diff_milli_s start, finish
|
||||
time_diff_milli(start, finish).to_s + "ms"
|
||||
end
|
||||
|
||||
def time_diff start, finish
|
||||
(finish - start).to_i
|
||||
end
|
||||
|
||||
def time_diff_s start, finish
|
||||
time_diff(start, finish).to_s + "s"
|
||||
end
|
||||
|
||||
end
|
||||
36
devops-service/config.rb
Normal file
36
devops-service/config.rb
Normal file
@ -0,0 +1,36 @@
|
||||
# path to log file
|
||||
config[:log_file] = "/path/to/log"
|
||||
# path to chef knife.rb file
|
||||
config[:knife_config_file] = "/path/to/.chef/knife.rb"
|
||||
# role name separator
|
||||
config[:role_separator] = "_"
|
||||
|
||||
# mongodb settings
|
||||
config[:mongo_host] = "localhost"
|
||||
config[:mongo_port] = 27017
|
||||
config[:mongo_db] = "devops"
|
||||
config[:mongo_user] = "user"
|
||||
config[:mongo_password] = "pass"
|
||||
|
||||
# devops port
|
||||
config[:port] = 7070
|
||||
|
||||
# path to devops-client.gem file
|
||||
config[:client_file] = "/path/to/public/devops-client.gem"
|
||||
# path to devops public directory
|
||||
config[:public_dir] = "/path/to/public"
|
||||
|
||||
# openstack settings
|
||||
config[:openstack_username] = "openstack_username"
|
||||
config[:openstack_api_key] = "openstack_pass"
|
||||
config[:openstack_auth_url] = "http://openstack.host:5000/v2.0/tokens"
|
||||
config[:openstack_tenant] = "tenant"
|
||||
config[:openstack_ssh_key] = "ssh_key"
|
||||
config[:openstack_certificate] = "/path/to/.ssh/openstack.pem"
|
||||
|
||||
# aws settings
|
||||
config[:aws_access_key_id] = "access_key_id"
|
||||
config[:aws_secret_access_key] = "secret_access_key"
|
||||
config[:aws_ssh_key] = "ssh_key"
|
||||
config[:aws_certificate] = "/path/to/.ssh/ec2.pem"
|
||||
config[:aws_availability_zone] = "aws_zone"
|
||||
23
devops-service/config.ru
Normal file
23
devops-service/config.ru
Normal file
@ -0,0 +1,23 @@
|
||||
# To run devops you can use command
|
||||
# `bundle exec thin -R $devops_home/config.ru -e $env -d -p $port -t 600 -u $user --pid $pid_file --log $log_file start`
|
||||
require 'rubygems'
|
||||
require 'bundler/setup'
|
||||
|
||||
root = File.dirname(__FILE__)
|
||||
require File.join(root, "devops-service")
|
||||
require File.join(root, "client")
|
||||
|
||||
# Read configuration file
|
||||
config_file = File.join(root, "config.rb")
|
||||
config = {}
|
||||
if File.exists? config_file
|
||||
eval File.read config_file
|
||||
else
|
||||
raise "No config file '#{config_file}' found"
|
||||
end
|
||||
|
||||
# URL map for API v2.0
|
||||
run Rack::URLMap.new({
|
||||
"/v2.0" => DevopsService.new(config),
|
||||
"/client" => Client.new(config)
|
||||
})
|
||||
3
devops-service/db/exceptions/invalid_record.rb
Normal file
3
devops-service/db/exceptions/invalid_record.rb
Normal file
@ -0,0 +1,3 @@
|
||||
class InvalidRecord < Exception
|
||||
|
||||
end
|
||||
3
devops-service/db/exceptions/record_not_found.rb
Normal file
3
devops-service/db/exceptions/record_not_found.rb
Normal file
@ -0,0 +1,3 @@
|
||||
class RecordNotFound < Exception#StandardError
|
||||
|
||||
end
|
||||
85
devops-service/db/mongo/models/deploy_env.rb
Normal file
85
devops-service/db/mongo/models/deploy_env.rb
Normal file
@ -0,0 +1,85 @@
|
||||
require "db/mongo/models/mongo_model"
|
||||
require "db/exceptions/invalid_record"
|
||||
require "providers/provider_factory"
|
||||
require "commands/deploy_env"
|
||||
|
||||
class DeployEnv < MongoModel
|
||||
|
||||
include DeployEnvCommands
|
||||
|
||||
attr_accessor :identifier, :flavor, :image, :run_list, :subnets, :expires, :provider, :groups, :users
|
||||
|
||||
types :identifier => {:type => String, :empty => false},
|
||||
:image => {:type => String, :empty => false},
|
||||
:flavor => {:type => String, :empty => false},
|
||||
:provider => {:type => String, :empty => false},
|
||||
:expires => {:type => String, :empty => false, :nil => true},
|
||||
:run_list => {:type => Array, :empty => true},
|
||||
:users => {:type => Array, :empty => true},
|
||||
:subnets => {:type => Array, :empty => true},
|
||||
:groups => {:type => Array, :empty => false}
|
||||
|
||||
def initialize d={}
|
||||
self.identifier = d["identifier"]
|
||||
self.flavor = d["flavor"]
|
||||
self.image = d["image"]
|
||||
b = d["subnets"] || []
|
||||
self.subnets = (b.is_a?(Array) ? b.uniq : b)
|
||||
b = d["run_list"] || []
|
||||
self.run_list = (b.is_a?(Array) ? b.uniq : b)
|
||||
self.expires = d["expires"]
|
||||
self.provider = d["provider"]
|
||||
b = d["groups"] || ["default"]
|
||||
self.groups = (b.is_a?(Array) ? b.uniq : b)
|
||||
b = d["users"] || []
|
||||
self.users = (b.is_a?(Array) ? b.uniq : b)
|
||||
end
|
||||
|
||||
def validate!
|
||||
super
|
||||
e = DeployEnv.validate_run_list(self.run_list)
|
||||
raise InvalidRecord.new "Invalid run list elements: '#{e.join("', '")}'" unless e.empty?
|
||||
|
||||
unless self.expires.nil?
|
||||
check_expires!(self.expires)
|
||||
end
|
||||
|
||||
p = ::Version2_0::Provider::ProviderFactory.get(self.provider)
|
||||
check_flavor!(p, self.flavor)
|
||||
check_image!(p, self.image)
|
||||
check_subnets_and_groups!(p, self.subnets, self.groups)
|
||||
check_users!(self.users)
|
||||
|
||||
true
|
||||
rescue InvalidRecord => e
|
||||
raise InvalidRecord.new "Deploy environment '#{self.identifier}'. " + e.message
|
||||
end
|
||||
|
||||
def to_hash
|
||||
{
|
||||
"flavor" => self.flavor,
|
||||
"identifier" => self.identifier,
|
||||
"image" => self.image,
|
||||
"run_list" => self.run_list,
|
||||
"subnets" => self.subnets,
|
||||
"expires" => self.expires,
|
||||
"provider" => self.provider,
|
||||
"groups" => self.groups,
|
||||
"users" => self.users
|
||||
}
|
||||
end
|
||||
|
||||
def self.create_from_bson d
|
||||
DeployEnv.new(d)
|
||||
end
|
||||
|
||||
def self.create hash
|
||||
DeployEnv.new(hash)
|
||||
end
|
||||
|
||||
def self.validate_run_list list
|
||||
rl = /\Arole|recipe\[[\w-]+(::[\w-]+)?\]\Z/
|
||||
list.select {|l| (rl =~ l).nil?}
|
||||
end
|
||||
|
||||
end
|
||||
118
devops-service/db/mongo/models/deploy_env_multi.rb
Normal file
118
devops-service/db/mongo/models/deploy_env_multi.rb
Normal file
@ -0,0 +1,118 @@
|
||||
require "db/mongo/models/mongo_model"
|
||||
require "db/exceptions/invalid_record"
|
||||
require "commands/deploy_env"
|
||||
|
||||
class DeployEnvMulti < MongoModel
|
||||
|
||||
include DeployEnvCommands
|
||||
|
||||
attr_accessor :identifier, :servers, :expires, :users
|
||||
|
||||
types :identifier => {:type => String, :empty => false},
|
||||
:expires => {:type => String, :empty => false, :nil => true},
|
||||
:users => {:type => Array, :empty => true},
|
||||
:servers => {:type => Array, :empty => false, :value_type => Hash}
|
||||
|
||||
def initialize d={}
|
||||
self.identifier = d["identifier"]
|
||||
self.expires = d["expires"]
|
||||
self.servers = d["servers"]
|
||||
b = d["users"] || []
|
||||
self.users = (b.is_a?(Array) ? b.uniq : b)
|
||||
end
|
||||
|
||||
def validate!
|
||||
super
|
||||
e = []
|
||||
check_users!(self.users)
|
||||
unless self.expires.nil?
|
||||
check_expires!(self.expires)
|
||||
end
|
||||
self.servers.each_with_index do |server, i|
|
||||
begin
|
||||
if server["priority"].nil?
|
||||
server["priority"] = 100
|
||||
else
|
||||
begin
|
||||
Integer(server["priority"])
|
||||
rescue ArgumentError, TypeError
|
||||
raise InvalidRecord.new("Parameter 'priority' should be an integer")
|
||||
end
|
||||
end
|
||||
|
||||
if !server["subprojects"].is_a?(Array) or server["subprojects"].empty?
|
||||
raise InvalidRecord.new("Parameter 'subprojects' must be a not empty array")
|
||||
end
|
||||
if server["subprojects"].size > 1
|
||||
check_provider(server["provider"])
|
||||
# strings
|
||||
%w{image flavor provider}.each do |p|
|
||||
begin
|
||||
check_string!(server[p])
|
||||
rescue ArgumentError
|
||||
raise InvalidRecord.new("Parameter '#{p}' must be a not empty string")
|
||||
end
|
||||
end
|
||||
# arrays
|
||||
%w{subnets groups}.each do |p|
|
||||
begin
|
||||
raise ArgumentError if !server[p].is_a?(Array) or server[p].empty?
|
||||
server[p].each do |v|
|
||||
raise ArgumentError unless v.is_a?(String)
|
||||
end
|
||||
rescue ArgumentError
|
||||
raise InvalidRecord.new("Parameter '#{p}' must be a not empty array of strings")
|
||||
end
|
||||
end
|
||||
|
||||
p = ::Version2_0::Provider::ProviderFactory.get(server["provider"])
|
||||
check_flavor!(p, server["flavor"])
|
||||
check_image!(p, server["image"])
|
||||
check_subnets_and_groups!(p, server["subnets"], server["groups"])
|
||||
end
|
||||
names = {}
|
||||
server["subprojects"].each_with_index do |sp, spi|
|
||||
begin
|
||||
raise InvalidRecord.new("Parameter 'subprojects' must contains objects only") unless sp.is_a?(Hash)
|
||||
%w{name env}.each do |p|
|
||||
begin
|
||||
check_string!(sp[p])
|
||||
rescue ArgumentError
|
||||
raise InvalidRecord.new("Parameter '#{p}' must be a not empty string")
|
||||
end
|
||||
end
|
||||
rescue InvalidRecord => e
|
||||
raise InvalidRecord.new("Subproject '#{spi}'. #{e.message}")
|
||||
end
|
||||
end
|
||||
pdb = DevopsService.mongo.project_names_with_envs(server["subprojects"].map{|sp| sp["name"]})
|
||||
server["subprojects"].each_with_index do |sp, spi|
|
||||
raise InvalidRecord.new("Subproject '#{spi}'. Project '#{sp["name"]}' with env '#{sp["env"]}' does not exist") if pdb[sp["name"]].nil? or !pdb[sp["name"]].include?(sp["env"])
|
||||
end
|
||||
rescue InvalidRecord => e
|
||||
raise InvalidRecord.new("Server '#{i}'. #{e.message}")
|
||||
end
|
||||
end
|
||||
true
|
||||
rescue InvalidRecord => e
|
||||
raise InvalidRecord.new "Deploy environment '#{self.identifier}'. " + e.message
|
||||
end
|
||||
|
||||
def to_hash
|
||||
{
|
||||
"identifier" => self.identifier,
|
||||
"expires" => self.expires,
|
||||
"users" => self.users,
|
||||
"servers" => self.servers
|
||||
}
|
||||
end
|
||||
|
||||
def self.create_from_bson d
|
||||
DeployEnvMulti.new(d)
|
||||
end
|
||||
|
||||
def self.create hash
|
||||
DeployEnvMulti.new(hash)
|
||||
end
|
||||
|
||||
end
|
||||
50
devops-service/db/mongo/models/image.rb
Normal file
50
devops-service/db/mongo/models/image.rb
Normal file
@ -0,0 +1,50 @@
|
||||
require "db/exceptions/invalid_record"
|
||||
require "db/mongo/models/mongo_model"
|
||||
require "commands/image"
|
||||
|
||||
class Image < MongoModel
|
||||
|
||||
include ImageCommands
|
||||
|
||||
attr_accessor :id, :provider, :remote_user, :name, :bootstrap_template
|
||||
types :id => {:type => String, :empty => false},
|
||||
:provider => {:type => String, :empty => false},
|
||||
:remote_user => {:type => String, :empty => false},
|
||||
:name => {:type => String, :empty => true},
|
||||
:bootstrap_template => {:type => String, :empty => false, :nil => true}
|
||||
|
||||
def validate!
|
||||
super
|
||||
images = get_images(DevopsService.mongo, self.provider)
|
||||
raise InvalidRecord.new "Invalid image id '#{self.id}' for provider '#{self.provider}', please check image filters" unless images.map{|i| i["id"]}.include?(self.id)
|
||||
end
|
||||
|
||||
def initialize p={}
|
||||
self.id = p["id"]
|
||||
self.provider = p["provider"]
|
||||
self.remote_user = p["remote_user"]
|
||||
self.name = p["name"] || ""
|
||||
self.bootstrap_template = p["bootstrap_template"]
|
||||
end
|
||||
|
||||
def self.create_from_bson args
|
||||
image = Image.new(args)
|
||||
image.id = args["_id"]
|
||||
image
|
||||
end
|
||||
|
||||
def to_hash_without_id
|
||||
o = {
|
||||
"provider" => self.provider,
|
||||
"name" => self.name,
|
||||
"remote_user" => self.remote_user
|
||||
}
|
||||
o["bootstrap_template"] = self.bootstrap_template
|
||||
o
|
||||
end
|
||||
|
||||
def self.create_from_json! json
|
||||
Image.new( JSON.parse(json) )
|
||||
end
|
||||
|
||||
end
|
||||
50
devops-service/db/mongo/models/key.rb
Normal file
50
devops-service/db/mongo/models/key.rb
Normal file
@ -0,0 +1,50 @@
|
||||
require "db/exceptions/invalid_record"
|
||||
require "db/mongo/models/mongo_model"
|
||||
require "json"
|
||||
|
||||
class Key < MongoModel
|
||||
|
||||
SYSTEM = "system"
|
||||
USER = "user"
|
||||
|
||||
attr_accessor :id, :path, :scope
|
||||
types :id => {:type => String, :empty => false},
|
||||
:path => {:type => String, :empty => false},
|
||||
:scope => {:type => String, :empty => false}
|
||||
|
||||
def initialize p={}
|
||||
self.id = p["id"]
|
||||
self.path = p["path"]
|
||||
self.scope = p["scope"] || USER
|
||||
end
|
||||
|
||||
def self.create_from_bson s
|
||||
key = Key.new s
|
||||
key.id = s["_id"]
|
||||
key
|
||||
end
|
||||
|
||||
def self.create_from_json json
|
||||
Key.new( JSON.parse(json) )
|
||||
end
|
||||
|
||||
def filename
|
||||
File.basename(self.path)
|
||||
end
|
||||
|
||||
def to_hash_without_id
|
||||
o = {
|
||||
"path" => self.path,
|
||||
"scope" => self.scope
|
||||
}
|
||||
o
|
||||
end
|
||||
|
||||
def validate!
|
||||
super
|
||||
raise InvalidRecord.new "File does not exist" unless File.exist?(self.path)
|
||||
raise InvalidRecord.new "Key parameter 'scope' is invalid" unless [SYSTEM, USER].include?(self.scope)
|
||||
true
|
||||
end
|
||||
|
||||
end
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user