 All right. Hi, everyone. My name is Brian Curtin. I work for Rackspace. I'm Terry Allen. I work for HP. And we're the two, one of the two of the main developers of the OpenStack SDK project. It's a project that was started, I think December or so. The conversations were started about December of 2013, so it's been around for a little over a year now. The first time we kind of got together and talked about it with a bunch of folks was in the Atlanta Summit, had a nice design session to get together and figure out what we all really wanted to do to improve the user and developer experience within OpenStack. And that's where we are today, so why? So the talk is going to cover a couple of things. I guess who is familiar with the OpenStack SDK today? Ed, one of the developers who had worked on it for a while, a little bit. So we'll talk a bit about the current set of things, why we wanted to do this, and then we'll dive into how things are structured, how they're set up, and how we're going to move forward with this project and how things will be better. So when you look at what's out there today, each OpenStack server offers its own client. So there's one library per service, and you're probably familiar with these. These are the Nova Client, Glance Client, Swift Client, all those clients. If you look on between the Stackforge and the OpenStack repos, there's something like 30 or so clients. No one's really building an application that uses all 30 of these, but you can imagine when each library, each service has its own library. Each of those provides basically their own user experience. They're all structured a little bit differently. They're all developed a little bit differently. They all have different dependencies. That kind of mounts up, and all of a sudden you're looking at a pretty complex application. It's a distributed system that's already adding complexity by being multiple services, and that complexity by itself is already daunting. And to look at all the different APIs, all the different return contracts, all that kind of stuff is not a great thing. So if we look deeper beyond just the fact that each thing has its own client, you start to see a lot of issues right away. There's duplication in the way a lot of things are created. There's re-implementation of things. A lot of different clients have their own HTTP layer. They have their own connection pooling. There's a couple of duplicated efforts there. If you see some of the more recent clients, you can kind of see their inspiration or the place they copy and pasted from another one. Dependency is a much better solution today in all of this. A year or two years ago, this was a pretty much a mess. Especially when you look at, okay, I want to use Keystone Client. I want to use Glance Client. I want to use a couple of these clients. It was at times tough to have them in the same virtual environment. So you're going to have different segregations of where you're writing your code just based on the fact that you have to do it at all. And the APIs are different in a lot of these things. Some of that's gotten better over time. If you look at things like Swift Client, it is structured massively differently than any of the others. And especially for some of these, you really have to have an knowledge of how the server is doing things and what it's calling things. And there's no really consistent view across all of this. So again, once even if you're using two or three services, there's a lot of differences. And there's really no way to just talk to OpenStack. There's a way to talk to Nova. There's a way to talk to Glance. There's a way to talk to all these other projects, but you can't just talk to the cloud. You can't talk to, I'm a Rackspace guy. You can't talk to just Rackspace. You can't talk to just HP. Peace together a bunch of things and make your own experience on top of that. A little example I'm going to show is how this all adds up, all these differences. So the basic operation of just listing resources for a particular service. And the examples I chose are that I'm going to list some servers, list some images and list containers in Nova, Glance, and Swift. And before I do this, I'm not trying to shame anyone. I'm not trying to make fun of this code. It's an example of the experience we have today. All these clients are created with the express consent, the goal of talking to that service. They're not trying to be anything that's consistent, but we end up seeing that this is what we live with. So to list servers, you create a client with some authentication arguments. You call your connection.servers.list, iterate over that, and you print out the dot name attribute of a server object. Glance looks kind of similar. It takes different arguments to clients to the way it's created, but it looks similar enough already. Now we're doing images.list. Looks very similar to what we had in the Nova one, but now we're printing what we're giving back from that list call is a dictionary. So it's a little bit different there. Look at what we do to list containers in Swift. It's called a different thing. It's not called a client. Now it's called a connection class. It takes different arguments again. And again, for different reasons, Swift can be run independently, so it has its own auth system. So it does things differently, and there's reasons for these things. But to now to get my containers, I'm calling get underscore count, and I'm getting back a tuple iterating through that and looking at the name, key, and the dictionary. Let's spot the differences. A lot of things add up. I think it's like 13 or 14 differences in that. I mean, they're called different things. When you look at that list call, whether it was servers that list in the Nova client, or the images that list in Glance, one of those, and I can't remember which one it is now, one returns an actual list, one is a generator, then the Swift client one is returning a tuple. Those are all very different. What they're returning, whether it's an object that has the attributes that correspond to the resource or dictionary keys, all that kind of stuff adds up. And this is only three things. Add two more services in there. You're going to get somehow more types of returns, more types of things that are coming out of it. And it's especially difficult when you add in the Swift client one, which requires you to know how the REST API is structured. It's called Git underscore account because it's doing an HTTP Git on your account URL. If you want to get the metadata, it's head underscore account. That's not something that an application developer, a consumer of a cloud from any of the number of vendors out there, wants to have to know. So after seeing this and having experiences talking to customers at things like OpenStack Summit and at other conferences and trying to understand what people want to build and why they want to build it and how they want to build it, they don't want to see those differences. They don't want to have to care about how the REST API is structured. They just want to know they want to get servers, they want to get images, they want to get containers. And we decided that's time to build one thing that can do that. So in the end, all of these libraries are REST clients. There's no need to have 30 REST clients to talk to 30 REST APIs when they all do roughly the same thing. They all have resources that we're talking to and we can have one representation of what a resource is and then use that from Python. One set of APIs, we don't need to have different method names for the same things in different services. We can provide a nice consistent look across the board and one set of dependencies. Maintaining an application that would use all three of those, the examples of the sort of client-to-client glance client, they all depend on different things and that kind of starts to add up, especially when you have your own application dependencies. All of a sudden you're looking at your dependencies list of 50 things to just do talk to a REST API. It's not really necessary. And the SDK itself being one project becomes one dependency for you to worry about. You don't have to worry about three or five or whatever services. So the goal with this is to get down to one thing. It was originally called the Unified SDK to provide a unified view, but we moved away from being unified as part of the name. So what the SDK is, is one package that currently is focused on application developers. These are the people who are going to be consuming an open-stack cloud from whatever vendor it is, from DevStack, from the private cloud, from something you've built yourself, something you're consuming from someone else. And you're trying to build applications, web apps, whatever you're doing. It's also one source of documentation and examples. It's the one place to get what it takes through Python to talk to an open-stack cloud. One thing I'm sure some people are familiar with, is anyone familiar with open-stack clients? So if you're not, open-stack clients. So what I'm talking about so far with SDK is that it's a library. It does not have anything to do with command line tools. So if you look at, like, clients, client, all these other clients, they're both libraries and command line tools. Open-stack client is currently a command line tool that wraps a lot of those other clients to provide one consistent, nice view on top of those. Open-stack client will remain as a command line tool, and SDK will be a library, his library, and there's a goal over time to make open-stack client consume SDK for its communication to other services instead of working through all those other clients. So they're related, they'll work together, but they're not the same thing. And so Terry's going to talk about kind of how we're going to do this. Yeah. You can hold on to that, I guess. So part of how we're going to do this is we kind of have, like, a stack, a kind of protocol stack on top of our cloud, starting with the transport, actually, then with a session on top of that, which actually holds the authentication information, and then there's a connection on top of that, which kind of ties it all together and makes it convenient for your services. The transport kind of derives off the request library, so it's just doing the HTTP part. So we have one base resource class, and it derives off of, I forget exactly what the class is, but it's a mutable map, and it's basically like a dictionary. It acts, can act like a dictionary, but we have a lot of methods on top of that to actually do the art gets and creates, et cetera, on top of that for that resource, more specific to the transport. Then we have a base proxy, and the proxy itself is supposed to act as a higher level, and within the connection class, the proxies will provide the interface that the user will actually see, so for each service there's a proxy, and the proxy itself would define all the methods for creating the particular resources, whatever they are. In order to install the SDK right now, you just pip install python open stack SDK. We released 0.5 yesterday, and it's been 24 hours, and there hasn't been a single bug fix release yet, so we're pretty happy about that, and that's about it. Don't deploy the production yet. This is kind of what the connection class looks like as far as using it. Basically it's taking just your, at a minimum, your authentication information. There's more parameters you could pass here. You could potentially pass an authenticator or a session to this thing, but if you don't pass it, it'll make one for you based on your auth parameters. I guess the next slide. One of the most important parameters that you might want to pass other than your auth parameters is the user profile, and the user profile is the way that it defines exactly what your preferences are for this connection, and one of the preferences could be the region that you want to access. So this is where we kind of handle the defining exactly which service we want to access, and there's several parameters to this. Region is given in this example, but there's also the service name. You could use a particular name that you want. The visibility of the URL, if you wanted a public URL, internal URL or admin URL, and then the version would be the other big thing, if you wanted, you know, V2 or V3, that kind of thing. And so, yeah, once you're connected, then you can actually access the services, and I'm not exactly sure what you want. The plug-in one. Oh, right, okay, yeah, that's the... Yeah, so within this connection class, there's also the ability to provide a name for an auth plug-in, and this is kind of supposed to show that, you know, you could provide your own authentication plug-in, and you just provide the name here, and it'll load that plug-in. We support, you know, we took our auth plug-ins from KSC, and they're very similar, and we've been trying to keep in sync with KSC, meaning Keystone Client, and they're very similar to what's out there in Keystone Client now, but there's also the capability to create your own plug-in to authenticate in any way that you might find convenient, and you can pass that in there on the connection. This is something where vendors who have different authentication systems or have other things to provide will do this. So right now I have worked for Rackspace and we have different auth, we're not Keystone auth, and so I have a very simple plug-in that will allow you to just say, once you've imported that, it creates an entry point, and then I would just say auth plug-in equals Rackspace, and then any credentials I pass through there know how to go to the Rackspace identity service and then opt me back and then we'll move on. I expect other vendors will have to do, or will provide similar things, and you don't have to worry about your auth URL that's taken care of inside the plug-in, so it's nice to just say auth to HP, auth to Rackspace, auth to Morantis or whatever it is, and they'll take care of the details of that. So the authentication plug-ins, as I mentioned before, we're based on the Keystone auth plug-ins, and we've been keeping in sync with their changes. I guess later on we're going to talk about some other stuff as far as authentication plug-ins, but that's moving forward. One of the major differences between our auth plug-ins and what's out in Keystone Client is the Keystone Client ones contain a lot of CLI options, which don't make a lot of sense for the SDK because we're not trying to describe what a CLI looks like and we don't want to tell the CLI folks what their options should be called or anything like that. So we've kind of removed that and just had, well, these are the arguments that you need, but we have a valid options list instead of this CLI options that KSC uses. Another part of taking that out for us was it decreased a dependency. Brian was talking before about the number of dependencies and what we've tried to do is really keep our set of dependencies down to a very minimum that you need to run and the Keystone Client was using Oslo Config, which is great, but it added a lot of dependencies to the SDK, which we didn't want to add anything that wasn't absolutely necessary. So as far as the basic service layout, I was talking about the proxies before and this is kind of where they play in. When you create your connection, the proxy objects, depending on what services you've requested, get loaded into these service names and so rather than use the service names, that we're kind of all familiar with, Nova, Glance, Cinder, et cetera, our service names are based on the actual service they provide, which should give a more intuitive for the user, for most users anyway, not necessarily open stack users. So we're using compute, image, object store instead of Nova, Glance, and Swift here and then within that, that's how you get down to access the methods for that particular service through the connection and so we're supporting, have some support, at least some level of support for 13 services currently. Some of it's pretty good and some of it's just a bare minimum, but adding the new services and the new resources tends to be fairly easy. And while we were building this out, in order to come up with some nice APIs and get an understanding of what we're actually doing there, we chose the path of go as wide as we can and as deep as we need to. So we ended up with these 13 services and we figured out, looking at a couple things to see implementing this service is the same as when we've already done, so maybe put that off later and find something that looks a little bit different. If you look at the Swift APIs, they're very different than anything else so we went pretty deep on that one to make sure we understand how that is and we've come across a number of differences and so we try to dive in and do these things and come up with enough of an exercise to have created something, a good solid base to build on. So that's why you'll see if you look at something like the compute stuff for Nova is quite complete but if you look at something like Barbecan, that's one we only got, I think there's two or three calls out of that one. Again, like we were just saying, it's pretty easy at this point now that we've gotten enough of those methods implemented to go add new stuff is not too hard, especially adding a new service. We're going to start building up documentation for a lot of that stuff. If you work on any of the servers and want to have your stuff in SDK, we'd love to help you. It's not too hard to get started. So if we look at what we had before with those, the Glantz, Nova, and Swift examples, we could just cut it down to one set of connection arguments passed to one connection and then just call compute.servers, image.images, object store the containers, and it's going to return each of those generators, each of those returns, resources that correspond directly to the resource you get from the server. So servers is going to give you a server object that has a dot name, that has a dot IP, stuff like that. Image is the same thing. They'll have a name. They'll look roughly the same, and they correspond pretty closely to what's on the rest API. And then so for one particular resource, if you're looking at any given service, assuming the resource is able to support these methods, there are some things like if you look at flavors, you can't do all of these operations on a flavor unless you're like an admin or something like that. So it's not typically there, but in the general CRUD operations, everywhere they're structured the same way so that's a consistent view. Whether it's create, update, delete, find, get, whatever it is. If you have a resource that follows all these, they'll follow the same convention everywhere. And when we're talking about servers, all of these are in there. So create server takes the attributes that it would take to create a server. Update takes an existing server and then can update whatever attributes. Delete just takes a server and deletes it. Find will do, and find we're going to build out, I'll talk about that a little bit later, but find will do some type of searching to find pattern matching based on name or based on image or based on other things. Get will do another get on that server. So sometimes when you do listing servers, you don't get a full detailed look. You get some higher level view where you can get just the name, image and flavor. If you do a get again on that, you get the full details of when it was updated, all kinds of stuff like that. And for listing multiple resources per that type of API, we've chosen to be named after. So it's the plural version of that resource instead of being something like list underscore servers. Because we're working with generators, the list name looks a little confusing. It's sure it is listing things off of the rest API, but it's not creating a list. So we chose this way. Basically, people like it. If not, we're not locked into anything. This is pretty early as you saw 0.5. So we want to make things that people want to use so if that's not a clear thing, certainly say so. We're open to building things people want to use, obviously. Because it is a generator and the way generators are typically used and iteration and filters and stuff like that. In a lot of the tests we've been doing for usability and stuff like that, it ends up working pretty well for us. But we're five people that are working on it. So we obviously can't speak for everybody. And then if you look at apply that everywhere else, you see the same thing. So where you would do create server, you would do create everything. And hopefully that leads to a much easier experience to use. There's less on your mind when you don't have to think about... Like in the Swift example you would have to do, it's either put or post underscore container to do one. So you have to know how it's doing it, the rest of the API structure to do those operations. And so getting to one consistent prefix for these hopefully is a much better experience for everyone. So this is an example of a little bit more detailed example of how you would actually create a server. And in order to do this, we're doing a find basically by name on our image and flavor first. And that find is returning an image resource and a flavor resource. And within that we're taking that and putting it in our create server command. And I guess one of the, you know, Brian worked on this, but this is one of the interesting kind of features that we have that this particular property of the server could either be an ID for that image or it could be a resource. So that's fairly powerful. Like if you knew the ID of your image that you wanted to use, you could just provide the ID as a string and you would save the extra rest call. But in this case we don't know, we just have a name that we're kind of looking for. Well, we're looking for a particular, you know, operating system of some sort. So we're going to just try and find one. And so that's what we're doing here. And so these named arguments, as I said below, and I was kind of alluding to, I refer directly to the attributes within the server resource class itself. And updating is kind of an extension to that. It does a lot of the same things in that it's, the arguments consist of just a, the server you want to update, or really any of the other APIs, the objects you want to update. And the keyword arguments beyond that are the fields that would be sent in the call. So for servers all you can do is really update the name. So given a server that already exists, set name equals something else, call update, and it's going to send that along. That was pretty easy to grasp, I hope. I guess as far as the update too, you could also, you know, do server.name and set it to something else there and then call update without the parameter. So it's kind of flexible that way. Kind of on the same theme that I was talking about before, where you can use a string or a resource, here we're showing that you could, when you're doing a get server, you could pass a string with the actual ID of the server that you want to get and, or you could actually have the server, or a server resource maybe that you got out of a find or out of a create, perhaps, and you want to get more details on it, you could pass that resource to the get command and get the server details. So it kind of makes that mapping between IDs and a resource somewhat transparent to the user. And especially if you look at something like the, here in the create server, those keyword arguments to that, making it so, it is transparent so it doesn't matter whether you have an ID or you have that, we don't have that fleshed out everywhere because we've been trying to get the APIs right and everything, and so we have a bunch of places where that works to where some places take, you'll see later there's going to be a script we show that does network stuff and it takes an ID, you have to pass the ID right now. We're going to catch up on a lot of the stuff to where you'll never have to worry about if it's an ID or the resource because as we were kind of just saying, the IDs, everything is an ID and so these resource classes are a wrapper on that, it's almost like a subclass of int or string or whatever it is in a way that they're interchangeable. So if you look at deletes, it is a pretty basic thing. Again, it takes an ID slash a resource and the one thing we've kind of added here is the fact that you can recover from a delete if it's a 404, it doesn't really matter so much or it may not matter if you're trying to delete something and it's already been deleted or you've passed the wrong ID or something like that, you may want to just be able to ignore that rather than always have to do a try-catch based on if it's a 404. So that's one little additional parameter now that we allow you to say, just ignore it, it always succeeds whether or not the operation did. And the same thing, we do a method on the list we kind of talked about earlier where the list is going to return a generator that will handle pagination. So it will handle whether or not it's paginated and it will properly keep yielding as many objects as you have. There are a lot of APIs in most of the up-to-date services that are in list form and a bunch of them are configurable on the server to be paginated and there are a couple that are just always, they're done by default. So when you're calling this, an additional parameter that's in there is to say to paginate it or not, there are going to be times where, depending on how your server is configured, if it's your own private cloud and you know that you guys always turn on pagination, you always turn it off, we can only really configure so much of this to know, we know based on the defaults that are in the documentation to say, hey, this is something that supports pagination and it's turned on and handle that. And so what we end up doing there is make a request for something that we know is paginated and go and then we set the limit and marker based on, whatever the default is, say it tries to get 100 items, do another request to see if we get less than 100, we got less than that page size, we got 150 things, stop requesting. If you get another 100, you got a full page, try again, try again, and it's transparent to the user that we're making multiple requests, we certainly are, but you never know because it's a generator, so you're making a request and it just keeps iterating until it's over. Additionally, this is one of the areas we've been working on lately that's want to be able to limit what's coming back. A lot of these ones that are paginated take query parameters in the REST API. So this is something where, in this example, we're gonna look for servers that start with my server and they run the image that we've already found. And so if we had 10,000 servers, this is gonna come up with anyone that's got that prefix and that image and hopefully at that point we're not paginating through 10,000 servers and filtering the client, we're gonna get back 100 and then we can do our unfiltering from there. So fine, we still have some work to do on this one. It works off, currently it's working off of GET, so it tries to kind of determine whether you're passing in a name or parameter and this method is very handy for, before I was working on the SDK, I was doing a lot of work on the OpenStack client and it's a constant battle where you're passed in a parameter from the command line and you're not sure if it's a name or an ID and you can kind of make a guess but still it's not a sure thing so this is something we use all the time over there so what this find does that we've implemented is it does a GET on the whatever's passed in and if it finds it then it assumes it was an ID and then if that doesn't work it uses the list command that we're talking before where it tries to search for that name within all of the different attributes of that or resources and we still need to do some work because part of the problem is that not all the services provide a decent query filter that we can actually use so we may say pass in name equals whatever it is and some services that just doesn't work and it's not actually across services either it can be varied by resource that we found so trying to put something in there it's a little bit smarter than what we have because if you have 10,000 containers you know we don't want to have to iterate through all of 10,000 of them and then find something if we can find it iteratively and add some smarts that containers is a bad example because it does support some of this stuff but anything that's giving you a huge amount of data anything we can minimize the amount of rest calls we're making is obviously a good thing it would be faster so kind of moving forward what we've got going on there's been a lot of talk lately about writing a Keystone auth package and it started and the idea of this package is it's going to just contain well there's a couple ideas about this package but one of the ideas it's going to contain all the auth plugins and that will free up the SDK from being responsible for trying to support all these auth plugins and it'll also allow other applications that don't necessarily want an SDK to use they just want auth plugins so hopefully we're going to convert over get rid of our auth plugin copies and go to this Keystone auth another thing that we have going forward is this idea of service plugins and we've got there's a change request out there that's got a service plugin capability where if you had a proprietary service with your particular provider or maybe you were working on a Stackforge project that wasn't necessarily a popular project yet you could write your own plugin to the SDK and take advantage of a lot of the higher level interface the authentication, the session handling, the transport all that kind of stuff that we've been working on and so that service plugin would work using entry points and that kind of thing and this service plugin it could be a good way to discover how to structure an API for a newer service rather than coming into SDK and all of a sudden you're releasing it and now you have backwards compatibility to think about if someone's building a new service it's probably better for them to live outside of the SDK in a more provisional state be a plugin everybody on your own time figure out exactly how you want to build your API and how you want to the experience you want to build and all the features do that outside and then at some point integrate your service and your support into the SDK because right now we're not closed off to any particular type of service you don't have to be integrated or incubated stuff like that there are Stackforge projects that are currently in there we have minimal support for the CDN service OpenStack Poppy is what it's called there's also Barbican is still yeah, Barbican is still newish there's one of the telemetry I don't remember the I forget it's metric it's one of the telemetry projects so some of those are in there they already have their APIs are baked in so they're not going to move but if you're not to that point yet service plugin stuff whether it's a proprietary vendor thing or a new project would make sense to consider this actually before we do that let's switch back to this it's kind of a more full example of how this looks and so we're not just showing one function at a time it's an example of it's in our example is a slash network slash create I think is what it is a bunch of our examples and a bunch of our we have there's a script out there that does that's of a Jenkins instance and that's what this is kind of pulled from to kind of show how a lot of this stuff works together you know walk through it Terry sure yeah you know as anyone who's worked with Neutron knows it's very complicated to set up a complete network I'm actually creating a network is easy but if you wanted to actually work it's very complicated so this kind of goes through the steps and what we've tried to do is kind of make this method kind of item potent so that you can run it over and over again and it's not going to mess up and if some part of it blows up you can just rerun it and so what we're doing here is we're first trying to find a network with that particular name that we're looking for and the way find works is if it doesn't find anything it doesn't it just returns none and so if we get if there's no network out there with that particular name we create one and then you know we go on and create our subnet in the same kind of way we're doing a find and then a create and you know here we're passing in I guess this is what Brian was talking about before a particular example of where we're not doing our ID to resource mapping here when we're creating that subnet we're passing in network ID equals network ID you know we need to make a change in the subnet resource so that it would accept either you know an ID or a resource and that would clean that up a little bit and at this point that's like a two-line change that just kind of slipped by as we were doing this it'll just it'll be called network and the type instead of being a string which is actually an ID it'll become type of resource property it'll be a network dot network class and that'll again take you to one of those so we're going on and you know down below here we're querying our external network so that we can route packets outside of our private network that we're creating and we're creating a router with that external that knows about that external network and then we're setting up some security groups and opening up some ports on that security group for that network so that we can access I think up above it you know we're opening up 80 and 22 or something like that just so that we can get some basic access to some services and we use this this method quite a bit just as kind of like a you know testing stuff we have a whole process that you know creates a server and a network and fires up a Jenkins server is kind of one of our little examples using this network that created here and so this method is actually something we might add as an even higher level something like this to take on the task of actually setting up a network so right now the current highest level is you know it takes care of your session it keeps taking a lot of stuff and that's these create, update, delete all these methods the ability to say create a server and work whether it's a Rackspace server or a HP server or whatever it is be kind of agnostic between vendors because with Rackspace you get a public IP other ones you don't you get an IT IP attach it there's a multiple step process so we do want to have I'm not sure exactly where this is going to live yet you may be familiar with a project by Monty Taylor called Shade which kind of tries to do some of this stuff where it says just give me a server give me a network give me you know anything that's a multi-step process and try to make that easier and abstract away a lot of the differences and make it into a one step process for users it's something we're exploring soon and something like this is the basis for a lot of that type of stuff and that is basically it we have if you look in our code is on Stackforce right now I'm sure at some point that will change to be under the OpenStack umbrella but it's called Python-OpenStack SDK that's the PIP name for as well our docs currently are not under OpenStack.org they're on read the docs under again the same name check out our IRC channel OpenStack-SDK's it's kind of a multi-purpose one it's not just us it's also the the go and dotnet and a couple other things. Yeah OpenStack client as well is there a lot of the same people are there and every Tuesday at 1900 UTC is our meeting really goes for the full hour it's usually pretty good time trying to make this stuff better and keep going with it. Any questions? If you have questions if you can come up to the mic that'd be best or I can repeat it for you. I'm going to close it. Okay. Thank you. I like this project very much. I have tried it in Sendlin and also in heat for heat but I do have two pain points I think maybe I'm not the only unlucky guy so I'd like to bring it up here. The first thing is about listing resources today we are assuming that all resources can be paginated but that is not true for heat the resource type is not from database there's no logic there to understand limit or marker. So maybe this is a generic thing we need to fix. I think our most recent change is at least somewhat addressed your concern there as far as the pagination I think Brian was touching on before. We don't assume anything that's paginated we have support for it and actually if you don't pass and some of these the proxy methods when they're implemented is when we figure out if it's paginated or not and so we set a paginate equals true flag or a false flag so we may have it mixed up in heat to where we either pass the wrong flag or whatever it is but that's certainly a bug and that's certainly something we could fix because most things are not paginated but we do want the ones that are paginated to be supported so it should be transparent to anyone whether or not it's paginated. My second question is about the service plugin idea but my understanding so far is that that is mostly about inclusion or inclusion of some services but I was expecting that maybe there will be some additional logic we will provide for for example for heat there are something done from the client side for example the guide file function it is actually passing some other files to be included for stack creation but today that may be not possible we don't have that logic in SDK. So you could do two things if that's a well defined thing if the method you have that would do that that could certainly be added to the proxy or in the lower the resource level. For the files to be included there's nowhere to find it's on your disk there's a local file to be included there are some parsing logic from the client side. We could provide an API where they could pass the file name we do have some we kind of went over just the basic create update kind of commands but we also have some kind of convenience methods thrown in there at places we have like a find available IP method for instance and maybe what you're talking about kind of falls into that category where we need to have a special method that would for that particular service the orchestration proxy would have or the heat proxy whatever you want to call it would have a particular method that would do what you're looking for. If that's part of orchestration we should do it. So we're at time right now we'll certainly stick around but we got to shut the video off so thanks everyone for coming hope you had a good time.