ocp_registry 0.0.1.alpha

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. data/.gitignore +25 -0
  2. data/Gemfile +17 -0
  3. data/Gemfile.lock +65 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +29 -0
  6. data/bin/ocp_registry +28 -0
  7. data/config/example-mysql.yml +40 -0
  8. data/config/example-sqlite.yml +33 -0
  9. data/lib/ocp_registry.rb +29 -0
  10. data/lib/ocp_registry/api_controller.rb +146 -0
  11. data/lib/ocp_registry/application_manager.rb +213 -0
  12. data/lib/ocp_registry/cloud_manager/mock.rb +5 -0
  13. data/lib/ocp_registry/cloud_manager/mock/mock.rb +191 -0
  14. data/lib/ocp_registry/cloud_manager/mock/model.rb +22 -0
  15. data/lib/ocp_registry/cloud_manager/openstack.rb +4 -0
  16. data/lib/ocp_registry/cloud_manager/openstack/cinder_helper.rb +27 -0
  17. data/lib/ocp_registry/cloud_manager/openstack/keystone_helper.rb +57 -0
  18. data/lib/ocp_registry/cloud_manager/openstack/nova_helper.rb +37 -0
  19. data/lib/ocp_registry/cloud_manager/openstack/openstack.rb +126 -0
  20. data/lib/ocp_registry/common.rb +54 -0
  21. data/lib/ocp_registry/config.rb +96 -0
  22. data/lib/ocp_registry/db/001_db_initialize.rb +16 -0
  23. data/lib/ocp_registry/error.rb +16 -0
  24. data/lib/ocp_registry/mail_client.rb +141 -0
  25. data/lib/ocp_registry/models.rb +7 -0
  26. data/lib/ocp_registry/models/registry_application.rb +10 -0
  27. data/lib/ocp_registry/runner.rb +47 -0
  28. data/lib/ocp_registry/version.rb +3 -0
  29. data/lib/ocp_registry/yaml_helper.rb +23 -0
  30. data/mail_template/approve_admin.erb +14 -0
  31. data/mail_template/approve_user.erb +22 -0
  32. data/mail_template/refuse_admin.erb +16 -0
  33. data/mail_template/refuse_user.erb +17 -0
  34. data/mail_template/request_admin.erb +15 -0
  35. data/mail_template/request_user.erb +16 -0
  36. data/ocp_registry.gemspec +34 -0
  37. data/public/bootstrap/css/bootstrap-responsive.css +1109 -0
  38. data/public/bootstrap/css/bootstrap-responsive.min.css +9 -0
  39. data/public/bootstrap/css/bootstrap.css +6167 -0
  40. data/public/bootstrap/css/bootstrap.min.css +9 -0
  41. data/public/bootstrap/img/glyphicons-halflings-white.png +0 -0
  42. data/public/bootstrap/img/glyphicons-halflings.png +0 -0
  43. data/public/bootstrap/js/bootstrap.js +2280 -0
  44. data/public/bootstrap/js/bootstrap.min.js +6 -0
  45. data/public/common.css +104 -0
  46. data/public/favicon.ico +0 -0
  47. data/public/images/loading.gif +0 -0
  48. data/public/jquery-1.10.2.min.js +6 -0
  49. data/public/jquery-1.10.2.min.map +1 -0
  50. data/public/jquery.json-2.4.min.js +23 -0
  51. data/public/noty/.gitignore +8 -0
  52. data/public/noty/LICENSE.txt +20 -0
  53. data/public/noty/README.markdown +22 -0
  54. data/public/noty/demo/allLayouts.html +91 -0
  55. data/public/noty/demo/allTypes.html +86 -0
  56. data/public/noty/demo/api.html +103 -0
  57. data/public/noty/demo/buttons.css +320 -0
  58. data/public/noty/demo/consumingAlert.html +78 -0
  59. data/public/noty/demo/customContainer.html +90 -0
  60. data/public/noty/demo/index.html +66 -0
  61. data/public/noty/demo/jquery-1.7.2.min.js +4 -0
  62. data/public/noty/demo/usingMaxVisible.html +104 -0
  63. data/public/noty/demo/usingWithButtons.html +104 -0
  64. data/public/noty/demo/usingWithButtons2.html +98 -0
  65. data/public/noty/demo/usingWithModal.html +87 -0
  66. data/public/noty/demo/usingWithOldOptions.html +110 -0
  67. data/public/noty/js/jquery.noty.js +547 -0
  68. data/public/noty/js/layouts/bottom.js +34 -0
  69. data/public/noty/js/layouts/bottomCenter.js +41 -0
  70. data/public/noty/js/layouts/bottomLeft.js +43 -0
  71. data/public/noty/js/layouts/bottomRight.js +43 -0
  72. data/public/noty/js/layouts/center.js +56 -0
  73. data/public/noty/js/layouts/centerLeft.js +61 -0
  74. data/public/noty/js/layouts/centerRight.js +61 -0
  75. data/public/noty/js/layouts/inline.js +31 -0
  76. data/public/noty/js/layouts/top.js +34 -0
  77. data/public/noty/js/layouts/topCenter.js +41 -0
  78. data/public/noty/js/layouts/topLeft.js +43 -0
  79. data/public/noty/js/layouts/topRight.js +43 -0
  80. data/public/noty/js/promise.js +432 -0
  81. data/public/noty/js/themes/default.js +156 -0
  82. data/views/apply.erb +134 -0
  83. data/views/base.erb +25 -0
  84. data/views/list.erb +41 -0
  85. data/views/review.erb +141 -0
  86. data/views/show.erb +96 -0
  87. data/views/view.erb +7 -0
  88. metadata +294 -0
@@ -0,0 +1,57 @@
1
+ module Ocp::Registry
2
+
3
+ class CloudManager
4
+
5
+ class Openstack
6
+
7
+ module KeystoneHelper
8
+ #{
9
+ # "name": "ACME corp",
10
+ # "description": "A description ...",
11
+ # "enabled": true
12
+ #}
13
+ def create_tenant(name, description, enabled = true)
14
+ with_openstack do
15
+ keystone.tenants.create( :name => name ,
16
+ :description => description ,
17
+ :enabled => enabled )
18
+ end
19
+ end
20
+
21
+ #{
22
+ # "username": "jqsmith",
23
+ # "email": "john.smith@example.org",
24
+ # "enabled": true,
25
+ # "OS-KSADM:password": "secrete"
26
+ #}
27
+ def create_user(name, tenant_id, password, email = '')
28
+ with_openstack do
29
+ keystone.users.create( :name => name,
30
+ :tenant_id => tenant_id,
31
+ :password => password,
32
+ :email => email)
33
+ end
34
+ end
35
+
36
+ def get_tenant_by_name(name)
37
+ with_openstack { keystone.tenants.find {|tenant| tenant.name == name} }
38
+ end
39
+
40
+ def tenant_add_user_with_role(tenant, user_id, role_id)
41
+ with_openstack { tenant.grant_user_role(user_id, role_id) }
42
+ end
43
+
44
+ def get_role_by_name(name)
45
+ with_openstack { keystone.roles.find {|role| role.name == name} }
46
+ end
47
+
48
+ def find_user_by_name(name)
49
+ with_openstack { keystone.users.find {|user| user.name == name} }
50
+ end
51
+ end
52
+
53
+ end
54
+
55
+ end
56
+
57
+ end
@@ -0,0 +1,37 @@
1
+ module Ocp::Registry
2
+
3
+ class CloudManager
4
+
5
+ class Openstack
6
+
7
+ module NovaHelper
8
+
9
+ NOVA_QUOTA_FIELDS = ["metadata_items",
10
+ "cores",
11
+ "instances",
12
+ "injected_files",
13
+ "injected_file_content_bytes",
14
+ "ram",
15
+ "floating_ips",
16
+ "fixed_ips",
17
+ "security_groups",
18
+ "security_group_rules"]
19
+
20
+ def default_compute_quota
21
+ with_openstack { compute.get_quota_defaults(nil).body["quota_set"] }
22
+ end
23
+
24
+ def set_compute_quota(tenant_id, hash)
25
+ with_openstack do
26
+ settings = Ocp::Registry::Common.hash_filter(hash, NOVA_QUOTA_FIELDS)
27
+ compute.update_quota(tenant_id,settings).body["quota_set"]
28
+ end
29
+ end
30
+
31
+ end
32
+
33
+ end
34
+
35
+ end
36
+
37
+ end
@@ -0,0 +1,126 @@
1
+
2
+ module Ocp::Registry
3
+
4
+ class CloudManager
5
+
6
+ class Openstack < CloudManager
7
+
8
+ require 'openstack/cinder_helper'
9
+ require 'openstack/nova_helper'
10
+ require 'openstack/keystone_helper'
11
+
12
+ include KeystoneHelper
13
+ include NovaHelper
14
+ include CinderHelper
15
+
16
+ def initialize(cloud_config)
17
+ validate_options(cloud_config)
18
+
19
+ @logger = Ocp::Registry.logger
20
+
21
+ @openstack_properties = cloud_config["openstack"]
22
+
23
+ unless @openstack_properties["auth_url"].match(/\/tokens$/)
24
+ @openstack_properties["auth_url"] = @openstack_properties["auth_url"] + "/tokens"
25
+ end
26
+
27
+ @openstack_options = {
28
+ :provider => "OpenStack",
29
+ :openstack_auth_url => @openstack_properties["auth_url"],
30
+ :openstack_username => @openstack_properties["username"],
31
+ :openstack_api_key => @openstack_properties["api_key"],
32
+ :openstack_tenant => @openstack_properties["tenant"],
33
+ :openstack_endpoint_type => @openstack_properties["endpoint_type"]
34
+ }
35
+ @default_role_name = cloud_config["default_role"]
36
+ end
37
+
38
+ def logger
39
+ @logger
40
+ end
41
+
42
+ def compute
43
+ @compute ||= Fog::Compute.new(@openstack_options)
44
+ end
45
+
46
+ def keystone
47
+ @keystone ||= Fog::Identity.new(@openstack_options)
48
+ end
49
+
50
+ def volume
51
+ @volume ||= Fog::Volume.new(@openstack_options)
52
+ end
53
+
54
+ def validate_options(cloud_config)
55
+ unless cloud_config.has_key?("openstack") &&
56
+ cloud_config["openstack"].is_a?(Hash) &&
57
+ cloud_config["openstack"]["auth_url"] &&
58
+ cloud_config["openstack"]["username"] &&
59
+ cloud_config["openstack"]["api_key"] &&
60
+ cloud_config["openstack"]["tenant"] &&
61
+ cloud_config["default_role"]
62
+ raise ConfigError, "Invalid OpenStack configuration parameters"
63
+ end
64
+ end
65
+
66
+ def set_tenant_quota(tenant_id, settings={})
67
+ result = nil
68
+ with_openstack do
69
+ compute_quota = set_compute_quota(tenant_id, settings)
70
+ volume_quota = set_volume_quota(tenant_id, settings)
71
+ result = compute_quota.merge (volume_quota) if (compute_quota && volume_quota)
72
+ end
73
+ cloud_error "Quota for #{tenant_id} has not been set" unless result
74
+ result
75
+ end
76
+
77
+ def default_quota
78
+ return @default_quota if @default_quota
79
+ with_openstack do
80
+ compute_quota = default_compute_quota
81
+ volume_quota = default_volume_quota
82
+ @default_quota = compute_quota.merge (volume_quota)
83
+ end
84
+ cloud_error "Default Quota is not found" unless @default_quota
85
+ @default_quota
86
+ end
87
+
88
+ def default_role
89
+ return @default_role if @default_role
90
+ with_openstack do
91
+ @default_role ||= get_role_by_name(@default_role_name)
92
+ end
93
+ cloud_error "Default Role [#{cloud_config["default_role"]}] is not found" unless @default_role
94
+ @logger.info("Default Role #{@default_role.name} - #{@default_role.id}")
95
+ @default_role
96
+ end
97
+
98
+ def with_openstack
99
+ retried = false
100
+ begin
101
+ yield
102
+ rescue Excon::Errors::Unauthorized => e
103
+ unless retried
104
+ retried = true
105
+ @compute = nil
106
+ @volume = nil
107
+ @keystone = nil
108
+ retry
109
+ end
110
+ cloud_error "Unable to connect to OpenStack API: #{e.message}", e
111
+ rescue Excon::Errors::InternalServerError => e
112
+ cloud_error "OpenStack API Internal Server error. Check debug log for details.", e
113
+ end
114
+ end
115
+
116
+ def cloud_error(message, exception = nil)
117
+ @logger.error(message) if @logger
118
+ @logger.error(exception) if @logger && exception
119
+ raise Ocp::Registry::CloudError, message
120
+ end
121
+
122
+ end
123
+
124
+ end
125
+
126
+ end
@@ -0,0 +1,54 @@
1
+ module Ocp::Registry::Common
2
+
3
+ class << self
4
+
5
+ def uuid
6
+ SecureRandom.uuid
7
+ end
8
+
9
+ def gen_password
10
+ SecureRandom.base64
11
+ end
12
+
13
+ def parse_email(email)
14
+ return unless email =~ /[a-z0-9_.-]+@[a-z0-9-]+\.[a-z.]+/
15
+ email =~ /([a-z0-9_.-]+)@([a-z0-9-]+\.[a-z.]+)/
16
+ {
17
+ :name => $1 ,
18
+ :domain => $2
19
+ }
20
+ end
21
+
22
+ def hash_filter(hash, fields = [])
23
+ copy = deep_copy(hash)
24
+ do_hash_filter(copy, fields)
25
+ end
26
+
27
+ def deep_copy(o)
28
+ Marshal.load(Marshal.dump(o))
29
+ end
30
+
31
+ def json_merge(a,b,reverse = false)
32
+ hash_a = Yajl::load a
33
+ hash_b = Yajl::load b
34
+ if reverse
35
+ b.merge a
36
+ else
37
+ a.merge b
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def do_hash_filter(hash, fields = [])
44
+ hash.keep_if do |k,v|
45
+ if v.is_a? Hash
46
+ do_hash_filter(v, fields)
47
+ !v.empty?
48
+ else
49
+ fields.include? k
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,96 @@
1
+
2
+ module Ocp::Registry
3
+
4
+ class << self
5
+
6
+ attr_accessor :logger
7
+ attr_accessor :base_url
8
+ attr_accessor :http_port
9
+ attr_accessor :http_user
10
+ attr_accessor :http_password
11
+ attr_accessor :db
12
+ attr_accessor :application_manager
13
+ attr_accessor :cloud_login_url
14
+
15
+ def configure(config)
16
+ validate_config(config)
17
+
18
+ @logger ||= Logger.new(config["logfile"] || STDOUT)
19
+ if config["loglevel"].kind_of?(String)
20
+ @logger.level = Logger.const_get(config["loglevel"].upcase)
21
+ end
22
+
23
+ @base_url = config["http"]["base_url"] || "127.0.0.1"
24
+ @http_port = config["http"]["port"]
25
+ @http_user = config["http"]["user"]
26
+ @http_password = config["http"]["password"]
27
+
28
+ @db = connect_db(config["db"])
29
+
30
+ migrate_db if @db
31
+
32
+ @cloud_login_url = config["cloud"]["login_url"]
33
+
34
+ plugin = config["cloud"]["plugin"]
35
+ begin
36
+ require "ocp_registry/cloud_manager/#{plugin}"
37
+ rescue LoadError
38
+ raise ConfigError, "Could not find Provider Plugin: #{plugin}"
39
+ end
40
+ @cloud_manager = Ocp::Registry::CloudManager.const_get(plugin.capitalize).new(config["cloud"])
41
+
42
+ @mail_client = init_mail_client(config["mail"]) if config["mail"]
43
+
44
+ @application_manager = Ocp::Registry::ApplicationManager.new(@cloud_manager,@mail_client)
45
+ end
46
+
47
+ def init_mail_client(mail_config)
48
+ require "ocp_registry/mail_client"
49
+ MailClient.new(mail_config)
50
+ end
51
+
52
+ def migrate_db
53
+ Sequel.extension :migration
54
+ Sequel::Migrator.apply(@db,File.expand_path('./lib/ocp_registry/db'))
55
+ end
56
+
57
+ def connect_db(db_config)
58
+ connection_options = db_config.delete('connection_options') {{}}
59
+ db_config.delete_if { |_, v| v.to_s.empty? }
60
+ db_config = db_config.merge(connection_options)
61
+
62
+ db = Sequel.connect(db_config)
63
+ if logger
64
+ db.logger = @logger
65
+ db.sql_log_level = :debug
66
+ end
67
+
68
+ db
69
+ end
70
+
71
+ def validate_config(config)
72
+ unless config.is_a?(Hash)
73
+ raise ConfigError, "Invalid config format, Hash expected, " \
74
+ "#{config.class} given"
75
+ end
76
+
77
+ unless config.has_key?("http") && config["http"].is_a?(Hash)
78
+ raise ConfigError, "HTTP configuration is missing from config file"
79
+ end
80
+
81
+ unless config.has_key?("db") && config["db"].is_a?(Hash)
82
+ raise ConfigError, "Database configuration is missing from config file"
83
+ end
84
+
85
+ unless config.has_key?("cloud") && config["cloud"].is_a?(Hash)
86
+ raise ConfigError, "Cloud configuration is missing from config file"
87
+ end
88
+
89
+ if config["cloud"]["plugin"].nil?
90
+ raise ConfigError, "Cloud plugin is missing from config file"
91
+ end
92
+ end
93
+
94
+ end
95
+
96
+ end
@@ -0,0 +1,16 @@
1
+
2
+ Sequel.migration do
3
+ change do
4
+ create_table(:registry_applications) do
5
+ primary_key :id ,:auto_increment => false, :type => String
6
+ String :email , :null => false
7
+ String :project , :null => false
8
+ String :description , :text => true
9
+ String :state , :null => false , :default => 'PENDING'
10
+ String :created_at , :null => false
11
+ String :updated_at
12
+ String :comments , :text => true
13
+ String :settings , :null => false , :text => true
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ module Ocp::Registry
2
+
3
+ class Error < StandardError
4
+ def self.code(code = 500)
5
+ define_method(:code) { code }
6
+ end
7
+ end
8
+
9
+ class FatalError < Error; end
10
+
11
+ class ConfigError < Error; end
12
+ class ConnectionError < Error; end
13
+
14
+ class CloudError < Error ; end
15
+
16
+ end
@@ -0,0 +1,141 @@
1
+ module Ocp::Registry
2
+ class MailClient
3
+
4
+ require "net/smtp"
5
+ require "thread"
6
+
7
+ DEFAULT_WORKER = 1
8
+ DEFAULT_PORT = '25'
9
+ DEFAULT_HELO = 'ocp.com'
10
+ TEMPLATES_PATH = 'mail_template'
11
+ DEFAULT_FROM = 'registry@ocp.com'
12
+ DEFAULT_TLS = false
13
+ DEFAULT_ADMIN_EMAIL = 'admin@ocp.com'
14
+
15
+ def initialize(mail_config)
16
+ validate_opinions(mail_config)
17
+
18
+ @logger = Ocp::Registry.logger
19
+
20
+ @worker = mail_config["worker"] || DEFAULT_WORKER
21
+ @enable_tls = mail_config["enable_tls"] || DEFAULT_TLS
22
+
23
+ @mail_opinions = {
24
+ :address => mail_config["smtp_server"] ,
25
+ :port => mail_config["port"] || DEFAULT_PORT ,
26
+ :helo => mail_config["helo"] || DEFAULT_HELO ,
27
+ :user => mail_config["username"] || nil ,
28
+ :secret => mail_config["password"] || nil ,
29
+ :authtype => mail_config["authentication"] || nil
30
+ }
31
+
32
+ @admin_emails = mail_config["admin_emails"] || DEFAULT_ADMIN_EMAIL
33
+
34
+ @mail_queue = Queue.new
35
+
36
+ setup_senders
37
+
38
+ end
39
+
40
+ def admin_emails
41
+ @admin_emails
42
+ end
43
+
44
+ def send_mail(mail_info)
45
+ mail_info.merge!({ :from => DEFAULT_FROM })
46
+ @mail_queue << mail_info if mail_validated?(mail_info)
47
+ end
48
+
49
+ def validate_opinions(mail_config)
50
+ unless mail_config["smtp_server"] && mail_config["admin_emails"]
51
+ raise ConfigError, "Invalid Mail configuration"
52
+ end
53
+ end
54
+
55
+ def mail_validated?(mail_info)
56
+ unless mail_info[:from] && mail_info[:to] && mail_info[:template] && has_template?(mail_info[:template])
57
+ @logger.waring "Mail is ignored because less of necessary fields "
58
+ return false
59
+ end
60
+ true
61
+ end
62
+
63
+ def has_template?(template)
64
+ File.exists? File.expand_path("#{TEMPLATES_PATH}/#{template}.erb")
65
+ end
66
+
67
+ def prepare_message(mail)
68
+ template = mail.delete(:template)
69
+ template = File.read(File.expand_path("#{TEMPLATES_PATH}/#{template}.erb"))
70
+ @logger.debug "Starting prepare message for mail - #{mail.to_s}"
71
+ begin
72
+ info = mail[:msg]
73
+ message = ERB.new(template).result binding
74
+ rescue Exception => e
75
+ @logger.error "Load email template failed - #{e.message}"
76
+ @logger.debug(e.backtrace.join("\n"))
77
+ end
78
+ return message , mail[:from] , mail[:to]
79
+ end
80
+
81
+ private
82
+
83
+ # Here we use multi-thread to realize a provider-consumer mode with a block-queue
84
+ # TODO: Realize with Fiber
85
+ def setup_senders
86
+ @worker.times do |index|
87
+
88
+ thread = Thread.new { create_sender }
89
+ thread.run
90
+
91
+ @logger.info("Mail sender thread #{index} is running ")
92
+
93
+ end
94
+
95
+ end
96
+
97
+
98
+ def create_sender
99
+ loop do
100
+ mail = @mail_queue.pop
101
+ begin
102
+ if @enable_tls
103
+ require "tlsmail"
104
+ Net::SMTP.enable_tls(OpenSSL::SSL::VERIFY_NONE)
105
+ end
106
+ @logger.info("Starting SMTP with opinions #{@mail_opinions}")
107
+ Net::SMTP.start(@mail_opinions[:address] ,
108
+ @mail_opinions[:port] ,
109
+ @mail_opinions[:helo] ,
110
+ @mail_opinions[:user] ,
111
+ @mail_opinions[:secret] ,
112
+ @mail_opinions[:authtype]) do |smtp|
113
+ until mail.nil? do
114
+ msg, from, to = prepare_message(mail)
115
+ smtp.send_message(msg, from, to)
116
+ @logger.debug("Mail is sent with message \n#{msg}")
117
+ begin
118
+ mail = @mail_queue.pop(:non_block => true )
119
+ rescue ThreadError => e
120
+ mail = nil
121
+ end
122
+ end
123
+ end
124
+ rescue Net::SMTPAuthenticationError => e
125
+ @logger.error("Mail server authentication failed - #{e.message}", e)
126
+ @logger.debug(e.backtrace.join("\n"))
127
+ rescue Net::SMTPError => e
128
+ @logger.error("Mail is not sent because of SMTP error - #{e.message}")
129
+ @logger.debug(e.backtrace.join("\n"))
130
+ rescue Exception => e
131
+ @logger.error(e.message)
132
+ @logger.debug(e.backtrace.join("\n"))
133
+ end
134
+ end
135
+ end
136
+
137
+
138
+
139
+
140
+ end
141
+ end