On 10/19/2016 12:07 PM, Matt Riedemann wrote: > On 10/19/2016 10:32 AM, Brian Curtin wrote: >> I'm currently facing what looks more and more like an impossible >> problem in determining the root of each service on a given cloud. It >> is apparently a free-for-all in how endpoints can be structured, and I >> think we're out of ways to approach it that catch all of the ways that >> all people can think of. >> >> In openstacksdk, we can no longer use the service catalog for >> determining each service's endpoints. Among other things, this is due >> to a combination of some versions of some services not actually being >> listed, and with things heading the direction of version-less services >> anyway. Recently we changed to using the service catalog as a pointer >> to where services live and then try to find the root of that service >> by stripping the path down and making some extra requests on startup >> to find what's offered. Despite a few initial snags, this now works >> reasonably well in a majority of cases. >> >> We have seen endpoints structured in the following ways: >> A. subdomains, e.g., https://service.cloud.com/v2 >> B. paths, e.g., https://cloud.com/service/v2 (sometimes there are >> more paths in between the root and /service/) >> C. service-specific ports, e.g., https://cloud.com:1234/v2 >> D. both A and B plus ports >> >> Within all of these, we can find the root of the given service just >> fine. We split the path and build successively longer paths starting >> from the root. In the above examples, we need to hit the path just >> short of the /v2, so in B it actually takes two requests as we'd make >> one to cloud.com which fails, but then a second one to >> cloud.com/service gives us what we need. >> >> However, another case came up: the root of all endpoints is itself >> another service. That makes it look like this: >> >> E. https://cloud.com:9999/service/v2 >> F. https://cloud.com:9999/otherservice >> >> In this case, https://cloud.com:9999 is keystone, so trying to get E's >> base by going from the root and outward will give me a versions >> response I can parse properly, but it points to keystone. We then end >> up building requests for 'service' that go to keystone endpoints and >> end up failing. We're doing this using itertools.accumulate on the >> path fragments, so you might think 'just throw it through >> `reversed()`' and go the other way. If we do that, we'll also get a >> versions response that we can parse, but it's the v2 specific info, >> not all available versions. >> >> So now that we can't reliably go from the left, and we definitely >> can't go from the right, how about the middle? >> >> This sounds ridiculous, and if it sounds familiar it's because they >> devise a "middle out" algorithm on the show Silicon Valley, but in >> most cases it'd actually work. In E above, it'd be fine. However, >> depending on the number of path fragments and which direction we chose >> to move first, we'd sometimes hit either a version-specific response >> or another service's response, so it's not reliable. >> >> Ultimately, I would like to know how something like this can be solved. >> >> 1. Is there any reliable, functional, and accurate programmatic way to >> get the versions and endpoints that all services on a cloud offer? >> >> 2. Are there any guidelines, rules, expectations, or other >> documentation on how services can be installed and their endpoints >> structured that are helpful to people build apps that use them, not in >> those trying to install and operate them? I've looked around a few >> times and found nothing useful. A lot of what I've found has >> referenced suggestions for operators setting them up behind various >> load balancing tools. >> >> 3. If 1 and 2 won't actually help me solve this, do you have any other >> suggestions that will? We already go left, right, and middle of each >> URI, so I'm out of directions to go, and we can't go back to the >> service catalog. >> >> Thanks, >> >> Brian >> >> __________________________________________________________________________ >> >> OpenStack Development Mailing List (not for usage questions) >> Unsubscribe: >> openstack-dev-requ...@lists.openstack.org?subject:unsubscribe >> http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-dev >> > > That's a tricky one. Just yesterday I was looking into how Tempest > creates the service clients it uses and lists versions, e.g. for compute: > > https://github.com/openstack/tempest/blob/13.0.0/tempest/lib/services/compute/versions_client.py#L27 > > > I was trying to figure out where that base_url value came from and in > the case of Tempest it's from an auth provider class, and I think the > services inside that thing are created from this code at some point: > > https://github.com/openstack/tempest/blob/13.0.0/tempest/config.py#L1405 > > So at a basic level that builds the client with config options for the > service type (e.g. compute) and endpoint type (e.g. publicURL), so then > it can lookup the publicURL for the 'compute' service/endpoint in the > service catalog and then start parsing the endpoint URL to do a GET on > the root endpoint for compute to get the available API versions. > > It doesn't sound like the same case with the SDK though since you don't > have a config file telling you what exact things to lookup in the > service catalog...which seems like the main issue.
In os-client-config, we defer all of this to keystoneauth1. Basically, we collect service_type, endpoint_type and a pile of other parameters (service_name, cert, key, favorite color) and pass them to a few different keystoneauth calls to wind up with a keystoneauth1.adapter.Adapter, which is a thing which behaves like a requests Session but is rooted on a particular endpoint from the catalog by name and knows about tokens. For instance: client = os_client_config.make_rest_client('compute', cloud='vexxhost') Gets you one of those. With it, you can do: client.get('/servers') Nova doesn't have more than one major version in reality, so the / endpoint isnt' a thing for nova. for glance: client = os_client_config.make_rest_client('image', cloud='vexxhost') client.get('/v1/images') # or client.get('/v2/images') you can, with glance, typically do: client.get('/') and get the version endpoint. In case you're wondering, the output of that last line is: {u'versions': [{u'status': u'CURRENT', u'id': u'v2.3', u'links': [{u'href': u'http://image-ca-ymq-1.vexxhost.net/v2/', u'rel': u'self'}]}, {u'status': u'SUPPORTED', u'id': u'v2.2', u'links': [{u'href': u'http://image-ca-ymq-1.vexxhost.net/v2/', u'rel': u'self'}]}, {u'status': u'SUPPORTED', u'id': u'v2.1', u'links': [{u'href': u'http://image-ca-ymq-1.vexxhost.net/v2/', u'rel': u'self'}]}, {u'status': u'SUPPORTED', u'id': u'v2.0', u'links': [{u'href': u'http://image-ca-ymq-1.vexxhost.net/v2/', u'rel': u'self'}]}, {u'status': u'SUPPORTED', u'id': u'v1.1', u'links': [{u'href': u'http://image-ca-ymq-1.vexxhost.net/v1/', u'rel': u'self'}]}, {u'status': u'SUPPORTED', u'id': u'v1.0', u'links': [{u'href': u'http://image-ca-ymq-1.vexxhost.net/v1/', u'rel': u'self'}]}]} (yes, all of these lines are live) Similar with neutron: >>> client = os_client_config.make_rest_client('network', cloud='vexxhost') >>> client.get('/').json() {u'versions': [{u'status': u'CURRENT', u'id': u'v2.0', u'links': [{u'href': u'http://network-ca-ymq-1.vexxhost.net/v2.0', u'rel': u'self'}]}]} Volumes is more like nova: >>> client = os_client_config.make_rest_client('volume', cloud='vexxhost') >>> client.get('/volumes').json() {u'volumes': []} with get('/') throwing a 404. __________________________________________________________________________ OpenStack Development Mailing List (not for usage questions) Unsubscribe: openstack-dev-requ...@lists.openstack.org?subject:unsubscribe http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-dev