 All right, so I think we effectively sync back up with the rest of the schedule. So I'm David Lyle. I am the project technical lead for Horizon. I've been doing this for three releases now as the PTO. I'm going to talk about extending and styling Horizon. So what's Horizon again? Hopefully this is not new news, but it's the integrated dashboard in OpenStack. It's out of the box. It supports, well, out of the box. And the tree, it supports all the integrated OpenStack services. Obviously that definition is changing, and so our support level is going to change a little bit, too. That's still kind of in flux. But as of now, any service that's integrated into OpenStack, we support. It's a Django-based application, and it utilizes a lot of Coven Open frameworks. Bootstrap, Angular, jQuery, DJ3 for some visualization, Rickshaw, SCSS. And it's built for extensibility. So that hints the talk. And if you want to look at the source, it's there. This is the gorgeous UI that we have. It's not gorgeous, and that's okay. And that's part of the talk. If we made a really gorgeous UI and we put it out there, nobody wants to sell the same-looking UI. So this gives you lots of freedom to make it pretty and put your company's effects on there. It's a Django application, again, although it's a weird use of Django. So Django has this great ORM model, and it ties into database back end. We threw all that away. We threw all that good stuff away, and we connect to the APIs of the services between those various Python clients. So the model's kind of implied via API. One thing I want to point out before I start talking about extensions is we have a little bit different comp setup than a lot of the OpenStack services. We don't have comp files. We have settings and local settings files. That's a Django thing. We're actually going to move away from that, but as of now, that's what we have. And this local settings PyExample gets overlooked a lot, and there's a lot of configuration that we've already built into Horizon. So if you're deploying Horizon and you're wondering if you can change something, all this should be documented in the Horizon docs, but have a look in there. Things pop up at different times. And so there's a lot of stuff that we already support and that you don't need to go and do extensions for. What's missing? So Horizon supports the integrated services, but anybody running a cloud in production knows there's a lot of things that OpenStack doesn't have built in. I mean, it doesn't have a unified monitoring solution. There's not a centralized logging solution. If you're billing people, there's no billing built in. There's nothing to view support cases. Anything proprietary that you're wanting to build on, that you're selling to your customers, it's obviously not there. And even hardware management. So all that would be new content that you'd have to add to Horizon. There's also the possibility you want to change some stuff the way it works in Horizon now. You want users to be able to self-register, support CDNs, different business rules. I don't want certain users to be able to upload images to the cloud or I don't want to enable volume backups for certain classes of users. These are things you can change with some of these customization techniques. And then obviously everybody wants a custom theme. So first I'm going to go through a little nomenclature. Horizon built on Django. Django doesn't have the navigation components that we were looking for when this was built. So these are some custom UI toolkit things inside of Horizon. There's a dashboard which is a high-level of aggregation, a panel group which is a smaller level of aggregation and a panel which is what actually contains the content. It's just a nested tree. I mean it's just a three-level tree essentially of navigation although there's only content on the lowest level. And then a couple other custom widgets that we have in Horizon that I'll refer back to is there's a tab up at the top and tables. So, Plugable Extensions. This is actually semi-new to Horizon. The former technique was just hack the code or customize the app and build on top of it. So we added some framework for being able to plug in extensions without having to modify code. Obviously it's a tremendous benefit. Not only can you add in the dashboards, the panel groups, the panels, but you can also reorder things in the navigation. You can disable parts of the navigation. And then you can also set defaults where the user drops into when they log in. So going back to some of that custom content on how you might go about adding it, fortunately we have a fairly easy tool to do that. Inside the Horizon source code, you can actually run this command and it will build out this structure for you. That's a typical Django format. You need a model, the panel is part of Horizon, but the URLs, the views, and the templates. And it pre-populates all that stuff for you. So you basically pick a name. The second part of that line is telling you what dashboard you want it to live in. That's no longer necessary. I am going through the preparation for this. I've discovered that and I'll have a fix for that soon. So that's kind of extra chaff that we'll get rid of. And so we generated that and it actually produces a viewable panel in the UI. And all you need to do to plug that in is create a file. It's kind of like a sys5 init file in the fact that the numbering forces ordering. And you plug that into Horizon. Horizon goes through, reads through all those and initializes these panels based on what's in that file. So in this case, all we would need to do to run that is... So we generated the content in the base directory of Horizon. We just need to move it to the appropriate dashboard, add this enabled file. All this is saying is that I have a new panel called LogsPanel. I want it in the admin dashboard, and here's the path to it. Now, this is a case where we're doing it actually in the source code tree. We're about a second moving out of the Horizon source, but this is just to get an example. And it pops up this beautiful panel with much information. But it works. It works out of the box. You run the command and you have something there that you can build on. So from there, we obviously want to add some content to that. In this case, we want to add some tabs across the top. So this is going to be an example of a logging panel. Maybe you have a centralized log drain somewhere and you want to go look at the host logs for different services. And so we want to have different tabs for the different services and the different logs. So to do that, it's really simple code. The top thing is where the actual data request happens. I kind of hid that out. But the rest of these are just the tab classes. And they all have the duplicate code other than the values change, so I just condensed them. And then I defined a tab group. That's actually all the code that it takes to generate those. And then I had to modify the view class that was generated just to say I want to use the tabs instead of what was, I think it's just an API view that's populated, which was blank. And then I added a template. And this is the entire template that basically says I just want to dump the logs out in a pre-block. And this is the Django style of template. And that's the result. I mean, I have the source code posted up to GitHub. It's just, this is a foolish implementation in the fact that it's just going and tailing the last 300 lines of the horizon log on the horizon server. Obviously, however you're doing logging, you'd have to implement it to that. And that's a problem. That's why we can't support it. We don't know how you want to do logging. So this is why it's up to you. We can't provide a framework to say, you know, to build out this kind of functionality just because there's so many different solutions. There's not a unified choice. So in those pluggable settings, so we saw a couple of them where we just specified the panel and the dashboard that was going into. But there's a bunch more settings you can do. You can add exception types. So if you're calling custom client that's communicating with a custom service and has its own exception types, you can add those in. So horizon's unified handler can handle those. You can add Angular modules into horizon and use those JavaScript files. Just, you know, there's a lot of choices in here. And some of this is about reorganizing and disabling panels as well or dashboards. And those are all documented in horizon settings. So we were doing this in the source tree before, but now we can pull it out of the source tree, which is the whole point of the pluggable solution. But we want to pull it out into its own source tree. So I built up a little bit more. Not only do I have a log panel, but I also have a monitoring panel because, you know, I want a unified UI framework for my support and my knock to use. And they don't want to have to do 27 different logins to be able to use stuff, and they don't want to go through 27 different tools. So we want to centralize that in horizon. So we're building up this. So yeah, so these are the two panels we're going to add. These enabled files are, we saw this example of those before, but one of these is just building up a panel group. I think that's on the next slide. Again, very simple. We're just going to stub out this panel group. This is basically just a place for that, these new panels to land and then specify the log panel in there. And this one, we have to add it to the installed app. So this is actually how we're going to... It has to be in the Python path. So we created that package before. It was an installable package, right? And we just installed that on the horizon system. And now we're saying we need to include log panel in the installed apps for horizon. So, and that's all it really takes to create these pluggable solutions. And, you know, why do we want to do this? Because honestly, we want to isolate the code. Selective deployment. So I may have a horizon node that's doing administrative work. I may have a horizon node that's handling end users. I don't necessarily want to put all my admin plug-ins on the end user system. So we can selectively play there. And this is the easiest upgrade path. So when I go through... When I go sync up with master later on or with the next release, this goes all isolated off of the complex moving forward. Here's a couple of examples of more mature extensions in the wild. Tusker UI, which is building on top of Tusker in triple O and doing infrastructure management. That's actually part of the horizon program, but it's not fully graduated out of... Again, that's changing. But it's not part of the integrated release yet. And then Manasca is actually a monitoring solution that's out there in the wild. They built a very nice plug-in for horizon as well. So maybe I don't want to just add content. Maybe I need to change some things inside or horizon. Again, maybe for a business rule or something along those lines. So there's a technique called a customization module. And it becomes... Excuse me. It's between the discovery mechanisms and the setup routines in horizon. So that basically says it's one of the last things in horizon when you specify this. This is something you specify in the settings file. It's very powerful, but really all it is is a bunch of monkey patching. And from that, it can be easily abused and it's kind of scary to use. But it's ideal for very targeted changes. So I don't really want to mess with the source tree. I don't want to go modify a table class in one of the dashboards, but I do want to turn something off. And this is an easy way to do that without being responsive. And this is... One of the downsides of this is it's harder to track. So when I update the source tree, it's going to be a little more difficult to know that I haven't gotten out of sync. And so there we are specifying the customization module. In this case, we just have it living in the horizon source tree. It's just a file you added in there and you specify that in the settings. And that's all it takes. Now, the back-end that we're using in Cinder doesn't support extending the volume. So all this code is turning that action off in the table. It's basically on all of our actions you can specify policy or specify if it's allowed based on certain conditions. And this is just saying it's always disabled. So the traditional master up till now was the custom Django application. And this is what the form... We recently updated the tutorial, but this was what was documented in the tutorial in the horizon code base. It's just taking the horizon code and pulling out some of the... I got to have it in there. It's pulling out the Django application specification and re-implementing it outside and also re-implementing the settings. It's a good way to... You can actually do this in vendor horizon and open stack dashboard in a separate repository and just pull it in as a dependency. And so in that way, it's a nice way to isolate a lot of code too and change the behavior of the application itself. And this is an example of the structure of how that application might look. You're adding a bunch of custom content that's pretty consistent. And I want to add some custom style sheets. This is part where theming might come in. I want to add some JavaScript to every page. So the base HTML in there is the template that every page uses. So basically I create my own, inherit from horizon, and then I change the organization of the page. And then in the requirements, I would require horizon. So you just pull it in and then you've changed out the application itself. And you can also do a combined approach where you're actually putting a customization module inside of your custom app. And so it seems weird that you would do that, but this is where you're trying to actually override behavior in the original horizon in a targeted way rather than fully re-implementing it on the custom app side. And then when all else fails, there's always editing horizon directly. I must say I've done all of these in production environments and all depending on what you're doing with horizon you may have to touch all of these. And so our version of horizon we had edits in because that was the only place you could really change it without getting too complicated. But you try to minimize those, but when you do, then you have the benefit that you have to get tooling to say, well am I getting out of sync or are the conflicts? So at least you have some tools to track it. But if you get too many of those changes it's very time consuming to track, so use it as minimally as you possibly can and in some cases it's necessary. Branding and style. So we talked about it a little bit earlier. I wish I had a better story around this, but I don't. There are a few things that you can easily change without altering the horizon code and that's the name of the site what happens when you click on the company logo and the two company logos. Those are very easy to change. They just require dropping some files in place in your post config scripts when you deploy horizon and changing subsetting files. But you probably need more and in that case you're probably going to want to custom my custom app, custom Django app around horizon. That's the best way to do it. You can add another CSS file inside of the horizon directory and use that, but I don't know it gets complicated. All right, that's really all I had. There's a lot of techniques for extending horizon. You can extend it at just about any point template, level, JavaScript level, outside the source code. All those are intended to work. So hopefully one of those works for you. Any questions?