]> git.donarmstrong.com Git - dsa-puppet.git/blobdiff - 3rdparty/modules/neutron/lib/puppet/provider/nova_admin_tenant_id_setter/ini_setting.rb
try again, with puppetforge modules, correctly included now
[dsa-puppet.git] / 3rdparty / modules / neutron / lib / puppet / provider / nova_admin_tenant_id_setter / ini_setting.rb
diff --git a/3rdparty/modules/neutron/lib/puppet/provider/nova_admin_tenant_id_setter/ini_setting.rb b/3rdparty/modules/neutron/lib/puppet/provider/nova_admin_tenant_id_setter/ini_setting.rb
new file mode 100644 (file)
index 0000000..fbef699
--- /dev/null
@@ -0,0 +1,196 @@
+## NB: This must work with Ruby 1.8!
+
+# This providers permits the nova_admin_tenant_id paramter in neutron.conf
+# to be set by providing a nova_admin_tenant_name to the Puppet module and
+# using the Keystone REST API to translate the name into the corresponding
+# UUID.
+#
+# This requires that tenant names be unique.  If there are multiple matches
+# for a given tenant name, this provider will raise an exception.
+
+require 'rubygems'
+require 'net/http'
+require 'net/https'
+require 'json'
+require 'puppet/util/inifile'
+
+class KeystoneError < Puppet::Error
+end
+
+class KeystoneConnectionError < KeystoneError
+end
+
+class KeystoneAPIError < KeystoneError
+end
+
+# Provides common request handling semantics to the other methods in
+# this module.
+#
+# +req+::
+#   An HTTPRequest object
+# +url+::
+#   A parsed URL (returned from URI.parse)
+def handle_request(req, url)
+    begin
+        # There is issue with ipv6 where address has to be in brackets, this causes the
+        # underlying ruby TCPSocket to fail. Net::HTTP.new will fail without brackets on
+        # joining the ipv6 address with :port or passing brackets to TCPSocket. It was
+        # found that if we use Net::HTTP.start with url.hostname the incriminated code
+        # won't be hit.
+        use_ssl = url.scheme == "https" ? true : false
+        http = Net::HTTP.start(url.hostname, url.port, {:use_ssl => use_ssl})
+        res = http.request(req)
+
+        if res.code != '200'
+            raise KeystoneAPIError, "Received error response from Keystone server at #{url}: #{res.message}"
+        end
+    rescue Errno::ECONNREFUSED => detail
+        raise KeystoneConnectionError, "Failed to connect to Keystone server at #{url}: #{detail}"
+    rescue SocketError => detail
+        raise KeystoneConnectionError, "Failed to connect to Keystone server at #{url}: #{detail}"
+    end
+
+    res
+end
+
+# Authenticates to a Keystone server and obtains an authentication token.
+# It returns a 2-element +[token, authinfo]+, where +token+ is a token
+# suitable for passing to openstack apis in the +X-Auth-Token+ header, and
+# +authinfo+ is the complete response from Keystone, including the service
+# catalog (if available).
+#
+# +auth_url+::
+#   Keystone endpoint URL.  This function assumes API version
+#   2.0 and an administrative endpoint, so this will typically look like
+#   +http://somehost:35357/v2.0+.
+#
+# +username+::
+#   Username for authentication.
+#
+# +password+::
+#   Password for authentication
+#
+# +tenantID+::
+#   Tenant UUID
+#
+# +tenantName+::
+#   Tenant name
+#
+def keystone_v2_authenticate(auth_url,
+                             username,
+                             password,
+                             tenantId=nil,
+                             tenantName=nil)
+
+    post_args = {
+        'auth' => {
+            'passwordCredentials' => {
+                'username' => username,
+                'password' => password
+            },
+        }}
+
+    if tenantId
+        post_args['auth']['tenantId'] = tenantId
+    end
+
+    if tenantName
+        post_args['auth']['tenantName'] = tenantName
+    end
+
+    url = URI.parse("#{auth_url}/tokens")
+    req = Net::HTTP::Post.new url.path
+    req['content-type'] = 'application/json'
+    req.body = post_args.to_json
+
+    res = handle_request(req, url)
+    data = JSON.parse res.body
+    return data['access']['token']['id']
+end
+
+# Queries a Keystone server to a list of all tenants.
+#
+# +auth_url+::
+#   Keystone endpoint.  See the notes for +auth_url+ in
+#   +keystone_v2_authenticate+.
+#
+# +token+::
+#   A Keystone token that will be passed in requests as the value of the
+#   +X-Auth-Token+ header.
+#
+def keystone_v2_tenants(auth_url,
+                        token)
+
+    url = URI.parse("#{auth_url}/tenants")
+    req = Net::HTTP::Get.new url.path
+    req['content-type'] = 'application/json'
+    req['x-auth-token'] = token
+
+    res = handle_request(req, url)
+    data = JSON.parse res.body
+    data['tenants']
+end
+
+Puppet::Type.type(:nova_admin_tenant_id_setter).provide(:ruby) do
+    @tenant_id = nil
+
+    def authenticate
+        keystone_v2_authenticate(
+          @resource[:auth_url],
+          @resource[:auth_username],
+          @resource[:auth_password],
+          nil,
+          @resource[:auth_tenant_name])
+    end
+
+    def find_tenant_by_name (token)
+        tenants  = keystone_v2_tenants(
+            @resource[:auth_url],
+            token)
+
+        tenants.select{|tenant| tenant['name'] == @resource[:tenant_name]}
+    end
+
+    def exists?
+      ini_file  = Puppet::Util::IniConfig::File.new
+      ini_file.read("/etc/neutron/neutron.conf")
+      ini_file['DEFAULT'] && ini_file['DEFAULT']['nova_admin_tenant_id'] && ini_file['DEFAULT']['nova_admin_tenant_id'] == tenant_id
+    end
+
+    def create
+        config
+    end
+
+    def tenant_id
+      @tenant_id ||= get_tenant_id
+    end
+
+    # This looks for the tenant specified by the 'tenant_name' parameter to
+    # the resource and returns the corresponding UUID if there is a single
+    # match.
+    #
+    # Raises a KeystoneAPIError if:
+    #
+    # - There are multiple matches, or
+    # - There are zero matches
+    def get_tenant_id
+        token = authenticate
+        tenants = find_tenant_by_name(token)
+
+        if tenants.length == 1
+            return tenants[0]['id']
+        elsif tenants.length > 1
+            raise KeystoneAPIError, 'Found multiple matches for tenant name'
+        else
+            raise KeystoneAPIError, 'Unable to find matching tenant'
+        end
+    end
+
+    def config
+        Puppet::Type.type(:neutron_config).new(
+            {:name => 'DEFAULT/nova_admin_tenant_id', :value => "#{tenant_id}"}
+        ).create
+    end
+
+end
+