ocp_registry 0.0.1.alpha

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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