Merge pull request #7056 from kubernetes-ui/kubernetes-ui-dashboard
Kubernetes UI with Dashboard component
3
.gitignore
vendored
@ -50,3 +50,6 @@ network_closure.sh
|
||||
.kubeconfig
|
||||
|
||||
.tags*
|
||||
|
||||
# Web UI
|
||||
www/master/node_modules/
|
||||
|
@ -33,7 +33,7 @@ fi
|
||||
DATAFILE=pkg/ui/datafile.go
|
||||
TMP_DATAFILE=/tmp/datafile.go
|
||||
|
||||
go-bindata -nocompress -o $DATAFILE -prefix ${PWD} -pkg ui www/... third_party/swagger-ui/...
|
||||
go-bindata -nocompress -o $DATAFILE -prefix ${PWD} -pkg ui www/app/... third_party/swagger-ui/...
|
||||
|
||||
cat hooks/boilerplate.go.txt > $TMP_DATAFILE
|
||||
echo "// generated by hack/build-ui.sh; DO NOT EDIT
|
||||
|
19301
pkg/ui/datafile.go
121
www/README.md
Normal file
@ -0,0 +1,121 @@
|
||||
### Install dependencies
|
||||
|
||||
We have two kinds of dependencies in this project: tools and angular framework code. The tools help
|
||||
us manage and test the application.
|
||||
|
||||
* We get the tools we depend upon via `npm`, the [node package manager](https://www.npmjs.com/).
|
||||
* We get the angular code via `bower`, a [client-side code package manager](http://bower.io/).
|
||||
|
||||
`npm` is configured to automatically run `bower install` and `gulp`. Before you run the application for the first time, simply run this command from the `www/master` directory:
|
||||
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
To start the application, run this command from the `www/master` directory:
|
||||
|
||||
```
|
||||
npm start
|
||||
```
|
||||
|
||||
The `gulp` command will start a file watcher which will update the generated `app` code after any changes are saved. Note: gulp file watcher does not currently support adding or deleting files, this will require a restart of gulp). Two new directories will also be created in the project.
|
||||
|
||||
* `master/node_modules` - contains npm dependencies
|
||||
* `master/bower_components` - contains the angular framework files and any custom dependencies
|
||||
|
||||
Bower components should be refernced in one of the `vendor.json` files below:
|
||||
|
||||
* `master/vendor.base.json` - 3rd party vendor javascript required to start the app. JS is compiled to `base.js` and loaded before `app.js`
|
||||
* `master/vendor.json` - 3rd party vendor scripts to make the app work, usually lazy loaded. Can be js or css. Copied to `vendor/*`.
|
||||
|
||||
### Serving the app during development
|
||||
|
||||
The app can be served through `kubectl`, but for some types of review a local web server is convenient. One can be installed as follows:
|
||||
|
||||
```
|
||||
sudo npm install -g http-server
|
||||
```
|
||||
|
||||
The server can then be launched:
|
||||
|
||||
```
|
||||
cd app
|
||||
http-server -a localhost -p 8000
|
||||
```
|
||||
|
||||
### Configuration
|
||||
#### Configuration settings
|
||||
A json file can be used by `gulp` to automatically create angular constants. This is useful for setting per environment variables such as api endpoints.
|
||||
* ```www/master/shared/config/development.json``` or ```www/master/shared/config/production.json``` can be created from the ```www/master/shared/config/development.example.json``` file.
|
||||
* ```development.example.json``` should be kept up to date with default values, since ```development.json``` is not under source control.
|
||||
* Component configuration can be added to ```www/master/components/<component name>/config/development.json``` and it will be combined with the main app config files and compiled into the intermediary ```www/master/shared/config/generated-config.js``` file.
|
||||
* All ```generated-config.js``` is compiled into ```app.js```
|
||||
* Production config can be generated using ```gulp config --env production``` or ```gulp --env production```
|
||||
* The generated angular constant is named ```ENV``` with the shared root and each component having their own child configuration. For example,
|
||||
```
|
||||
www/master
|
||||
├── shared/config/development.json
|
||||
└── components
|
||||
├── dashboard/config/development.json
|
||||
├── graph/config/development.json
|
||||
└── my_component/config/development.json
|
||||
```
|
||||
produces ```www/master/shared/config/generated-config.js```:
|
||||
```
|
||||
angular.module('kubernetesApp.config', [])
|
||||
.constant('ENV', {
|
||||
'/': <www/master/shared/config/development.json>,
|
||||
'dashboard': <www/master/components/dashboard/config/development.json>,
|
||||
'graph': <www/master/components/graph/config/development.json>,
|
||||
'my_component': <www/master/components/my_component/config/development.json>
|
||||
});
|
||||
```
|
||||
|
||||
#### Kubernetes server configuration
|
||||
|
||||
**RECOMMENDED**: By default the Kubernetes api server does not support CORS,
|
||||
so the `kube-apiserver.service` must be started with
|
||||
`--cors_allowed_origins=.*` or `--cors_allowed_origins=http://<your
|
||||
host here>`
|
||||
|
||||
**HACKS**: If you don't want to/cannot restart the Kubernetes api server:
|
||||
* Or you can start your browser with web security disabled. For
|
||||
Chrome, you can [launch](http://www.chromium.org/developers/how-tos/run-chromium-with-flags) it with flag ```--disable-web-security```.
|
||||
|
||||
### Building a new visualizer or component
|
||||
|
||||
See [master/components/README.md](master/components/README.md).
|
||||
|
||||
### Testing
|
||||
Currently kuberntes-ui includes both unit-testing (run via [Karma](http://karma-runner.github.io/0.12/index.html)) and
|
||||
end-to-end testing (run via
|
||||
[Protractor](http://angular.github.io/protractor/#/)).
|
||||
|
||||
#### Unittests via Karma
|
||||
To run the existing Karma tests:
|
||||
* Install the Karma CLI: `sudo npm install -g karma-cli` (it needs to
|
||||
be installed globally, hence the `sudo` may be needed). Note that
|
||||
the other Karma packages (such as `karma`, `karma-jasmine`, and
|
||||
`karma-chrome-launcher` should be automatically installed when
|
||||
running `npm start`).
|
||||
* Go to the `www/master` directory, and run `karma start
|
||||
karma.conf.js`. The Karma configuration is defined in `karma.config.js`. The console should show the test results.
|
||||
|
||||
To write new Karma tests:
|
||||
* For testing each components, write test files (`*.spec.js`) under the
|
||||
corresponding `www/master/components/**/test/modules/` directory.
|
||||
* For testing the chrome and the framework, write test files
|
||||
(*.spec.js) under the `www/master/test/modules/*` directory.
|
||||
|
||||
#### End-to-end testing via Protractor
|
||||
To run the existing Protractor tests:
|
||||
* Install the CLIs: `sudo npm install -g protractor`.
|
||||
* Start the webdriver server: `sudo webdriver-manager start`
|
||||
* Start the kubernetes-ui app (see instructions above), assuming
|
||||
running at port 8000.
|
||||
* Go to the `www/master/protractor` directory and run `protractor
|
||||
conf.js`. The protractor configuration is in `conf.js`. The console
|
||||
should show the test results.
|
||||
|
||||
To write new protractor tests, put the test files (`*.spec.js`) in the
|
||||
corresponding `www/master/components/**/protractor/` directory.
|
1508
www/app/assets/css/app.css
Normal file
BIN
www/app/assets/img/docArrow.png
Normal file
After Width: | Height: | Size: 373 B |
BIN
www/app/assets/img/icons/arrow-back.png
Normal file
After Width: | Height: | Size: 635 B |
BIN
www/app/assets/img/icons/favicon.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
1
www/app/assets/img/icons/ic_arrow_forward_24px.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M12 4l-1.41 1.41L16.17 11H4v2h12.17l-5.58 5.59L12 20l8-8z"/></svg>
|
After Width: | Height: | Size: 158 B |
1
www/app/assets/img/icons/ic_cancel_24px.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17 12 13.41 8.41 17 7 15.59 10.59 12 7 8.41 8.41 7 12 10.59 15.59 7 17 8.41 13.41 12 17 15.59z"/></svg>
|
After Width: | Height: | Size: 276 B |
1
www/app/assets/img/icons/ic_close_24px.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
|
After Width: | Height: | Size: 202 B |
13
www/app/assets/img/icons/ic_menu.svg
Normal file
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 10 10" style="enable-background:new 0 0 10 10;" xml:space="preserve">
|
||||
<g>
|
||||
<path style="fill:#010002;" d="M9.5,4.5h-9C0.224,4.5,0,4.724,0,5s0.224,0.5,0.5,0.5h9C9.775,5.5,10,5.276,10,5
|
||||
S9.775,4.5,9.5,4.5z"/>
|
||||
<path style="fill:#010002;" d="M0.5,2.5h9C9.775,2.5,10,2.276,10,2S9.775,1.5,9.5,1.5h-9C0.224,1.5,0,1.724,0,2
|
||||
S0.224,2.5,0.5,2.5z"/>
|
||||
<path style="fill:#010002;" d="M9.5,7.5h-9C0.224,7.5,0,7.725,0,8s0.224,0.5,0.5,0.5h9C9.775,8.5,10,8.275,10,8
|
||||
S9.775,7.5,9.5,7.5z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 791 B |
23
www/app/assets/img/icons/ic_menu_24px.svg
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="24px"
|
||||
height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
|
||||
<g id="Header">
|
||||
<g>
|
||||
<rect x="-618" y="-2232" fill="none" width="1400" height="3600"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="Label">
|
||||
</g>
|
||||
<g id="Icon">
|
||||
<g>
|
||||
<rect fill="none" width="24" height="24"/>
|
||||
<path d="M3,18h18v-2H3V18z M3,13h18v-2H3V13z M3,6v2h18V6H3z" style="fill:#f3f3f3;"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="Grid" display="none">
|
||||
<g display="inline">
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 841 B |
BIN
www/app/assets/img/icons/list_control_down.png
Normal file
After Width: | Height: | Size: 309 B |
6
www/app/assets/img/kubernetes.svg
Normal file
After Width: | Height: | Size: 11 KiB |
0
www/app/assets/js/.gitkeep
Normal file
2359
www/app/assets/js/app.js
Normal file
26
www/app/assets/js/base.js
Normal file
125
www/app/components/README.md
Normal file
@ -0,0 +1,125 @@
|
||||
Components
|
||||
==========
|
||||
|
||||
A tab in the Kubernetes UI with its set of visualizations is referred to as a *component*. Components are separated from the UI chrome and base data providers to simplify the development of new visualizations. This document provides reference for creation and modification of components.
|
||||
|
||||
Each component has its own directory, which contains a manifest file, HTML views, Angular providers, CSS, Less and other assets. Below is the recommended directory structure for a component.
|
||||
```
|
||||
foo_component
|
||||
├── config
|
||||
├── css
|
||||
├── img
|
||||
├── js
|
||||
│ └── modules
|
||||
│ ├── controllers
|
||||
│ ├── directives
|
||||
│ └── services
|
||||
├── less
|
||||
├── pages
|
||||
├── views
|
||||
│ └── partials
|
||||
└── manifest.json
|
||||
```
|
||||
|
||||
###Manifest file
|
||||
|
||||
The JSON-formatted manifest file, named ```manifest.json```, is located at the root of a component. Based on the component directory name and the contents of the manifest, the Kubernetes UI automatically adds a tab to the chrome, a dependency on the component's AngularJS module to main AngularJS app and Angular routes for the component.
|
||||
|
||||
For example, consider a manifest file at ```master/components/foo_component/manifest.json```:
|
||||
```
|
||||
{
|
||||
"routes": [
|
||||
{
|
||||
"url": "/",
|
||||
"templateUrl": "/components/foo_component/pages/home.html"
|
||||
},
|
||||
{
|
||||
"url": "/kittens",
|
||||
"templateUrl": "/components/foo_component/pages/kittens.html",
|
||||
"css": "/components/foo_component/css/kittens.css"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
From the name of the component directory, the Kubernetes UI
|
||||
* creates a tab called "Foo Component",
|
||||
* adds Angular module ```kubernetesApp.components.fooComponent``` to the dependencies of ```kubernetesApp```, and
|
||||
* defines Angular routes ```/foo_component/``` and ```/foo_component/kittens```.
|
||||
|
||||
Every tab links to ```/``` relative to its component, so it is important to always define a ```/``` route.
|
||||
|
||||
###Source files
|
||||
In general, all files located in ```master/components/<component>``` are copied to ```app/components/<component>/``` on each gulp build. This includes (but is not limited to) HTML views, CSS and images. Exceptions to this copy are the ```config``` and ```less``` directories as well as all ```.js``` files.
|
||||
|
||||
The sections below describe how the exceptions are built into the UI.
|
||||
|
||||
####JavaScript
|
||||
All JavaScript files located in the ```master/components/<component>/js``` are uglified and concatenated together with the rest of the UI's JavaScript. Once aggregated, the JavaScript file is minified and written to ```app/assets/js/app.js```.
|
||||
|
||||
####Configuration
|
||||
|
||||
Similar to the [UI-wide configuration](../../README.md#configuration), components can define different configuration for each environment. The gulp task creates the constant ```ENV``` under the ```kubernetesApp.config``` module for configuration, which is an object with a property for the root UI and each component.
|
||||
|
||||
For example, a configuration for the ```development``` environment specific to ```foo_component``` would be located at ```master/components/foo_component/config/development.json```. Such a component would access its ```development.json``` configuration verbatim at ```ENV.foo_component```:
|
||||
```
|
||||
angular.module('kubernetesApp.components.fooComponent', ['kubernetesApp.config'])
|
||||
.provider('foo', ...)
|
||||
.config(['fooProvider', 'ENV', function(fooProvider, ENV) {
|
||||
// Configure fooProvider using ENV['foo_component'].
|
||||
});
|
||||
```
|
||||
|
||||
####Less
|
||||
|
||||
Like JavaScript, the component's Less files are built into one monolithic CSS file. All top-level Less files located at ```master/components/<component>/less/*.less``` are imported into the main UI's Less file. The result is then minified and copied to ```app/assets/css/app.css```.
|
||||
|
||||
Sub-directories of this path are watched for changes, but not directly imported. This is useful for defining common colors, mixins and other functions or variables used by the top-level Less files.
|
||||
|
||||
###Appendix
|
||||
|
||||
####Manifest schema
|
||||
|
||||
```
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "Very brief summary of the component. Use a README.md file for detailed descriptions."
|
||||
},
|
||||
"routes": {
|
||||
"type": "array",
|
||||
"description": "Angular routes for the component.",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "Short description of the route."
|
||||
},
|
||||
"url": {
|
||||
"type": "string",
|
||||
"description": "Route location relative to '/<component>'."
|
||||
},
|
||||
"templateUrl": {
|
||||
"type": "string",
|
||||
"description": "Absolute location of the HTML template."
|
||||
},
|
||||
"css": {
|
||||
"type": "string",
|
||||
"description": "Absolute location of CSS to use with this route."
|
||||
}
|
||||
},
|
||||
"required": ["url", "templateUrl"]
|
||||
},
|
||||
"minItems": 1
|
||||
}
|
||||
},
|
||||
"required": ["routes"]
|
||||
}
|
||||
```
|
||||
|
||||
Content available under the [CC-By 3.0
|
||||
license](http://creativecommons.org/licenses/by/3.0/)
|
1
www/app/components/dashboard/README.md
Normal file
@ -0,0 +1 @@
|
||||
Dashboard Component for Kubernetes WebUI
|
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18"><path d="M5 8l4 4 4-4z"/></svg>
|
After Width: | Height: | Size: 114 B |
@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<path d="M7 10l5 5 5-5z"/>
|
||||
<path d="M0 0h24v24h-24z" fill="none"/>
|
||||
</svg>
|
After Width: | Height: | Size: 166 B |
1
www/app/components/dashboard/img/icons/ic_close_18px.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18"><path d="M14.53 4.53l-1.06-1.06L9 7.94 4.53 3.47 3.47 4.53 7.94 9l-4.47 4.47 1.06 1.06L9 10.06l4.47 4.47 1.06-1.06L10.06 9z"/></svg>
|
After Width: | Height: | Size: 215 B |
1
www/app/components/dashboard/img/icons/ic_close_24px.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
|
After Width: | Height: | Size: 202 B |
60
www/app/components/dashboard/manifest.json
Normal file
@ -0,0 +1,60 @@
|
||||
{
|
||||
"description": "The basic kubernetes ui dashboard... ",
|
||||
"routes": [
|
||||
{
|
||||
"description": "Dashboard visualization.",
|
||||
"url": "/",
|
||||
"templateUrl": "components/dashboard/pages/home.html"
|
||||
},
|
||||
{
|
||||
"description": "Pods",
|
||||
"url": "/pods",
|
||||
"templateUrl": "components/dashboard/views/listPods.html"
|
||||
},
|
||||
{
|
||||
"description": "Pod Visualizer",
|
||||
"url": "/visualpods",
|
||||
"templateUrl": "components/dashboard/views/listPodsVisualizer.html"
|
||||
},
|
||||
{
|
||||
"description": "Services",
|
||||
"url": "/services",
|
||||
"templateUrl": "components/dashboard/views/listServices.html"
|
||||
},
|
||||
{
|
||||
"description": "Replication Controllers",
|
||||
"url": "/replicationcontrollers",
|
||||
"templateUrl": "components/dashboard/views/listReplicationControllers.html"
|
||||
},
|
||||
{
|
||||
"description": "Events",
|
||||
"url": "/events",
|
||||
"templateUrl": "components/dashboard/views/listEvents.html"
|
||||
},
|
||||
{
|
||||
"description": "Minions",
|
||||
"url": "/minions",
|
||||
"templateUrl": "components/dashboard/views/listMinions.html"
|
||||
},
|
||||
{
|
||||
"description": "Replication Controller",
|
||||
"url": "/replicationcontrollers/:replicationControllerId",
|
||||
"templateUrl": "components/dashboard/views/replication.html"
|
||||
},
|
||||
{
|
||||
"description": "Service",
|
||||
"url": "/services/:serviceId",
|
||||
"templateUrl": "components/dashboard/views/service.html"
|
||||
},
|
||||
{
|
||||
"description": "Explore",
|
||||
"url": "/groups/:grouping*?/selector/:selector*?",
|
||||
"templateUrl": "components/dashboard/views/groups.html"
|
||||
},
|
||||
{
|
||||
"description": "Pod",
|
||||
"url": "/pods/:podId",
|
||||
"templateUrl": "components/dashboard/views/pod.html"
|
||||
}
|
||||
]
|
||||
}
|
1
www/app/components/dashboard/pages/footer.html
Normal file
@ -0,0 +1 @@
|
||||
<p></p>
|
20
www/app/components/dashboard/pages/header.html
Normal file
@ -0,0 +1,20 @@
|
||||
<div layout-fill>
|
||||
<md-toolbar md-scroll-shrink class="dashboard-subnav">
|
||||
<div class="md-toolbar-tools">
|
||||
<div layout="row" flex class="fill-height">
|
||||
<div class="md-toolbar-item md-breadcrumb"></div>
|
||||
<span style="display: inline-block;">Kubernetes</span>
|
||||
<span flex></span>
|
||||
<div class="md-toolbar-item md-tools" layout="row">
|
||||
<div layout="column" class="selectSubPages">
|
||||
<md-select ng-model="page" placeholder="Views" class="selectTitle">
|
||||
<md-optgroup label="Dashboard">
|
||||
<md-option id="{{subpage.id}}" ng-value="subpage.value" ng-repeat="subpage in subpages | filter: {category: 'dashboard' }">{{subpage.name}}</md-option>
|
||||
</md-option-group>
|
||||
</md-select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</md-toolbar>
|
||||
</div>
|
7
www/app/components/dashboard/pages/home.html
Normal file
@ -0,0 +1,7 @@
|
||||
<div dashboard-header></div>
|
||||
<div class="dashboard" ng-controller="DashboardCtrl" layout-fill>
|
||||
<md-content>
|
||||
<div ng-include="'components/dashboard/views/partials/cadvisor.html'">></div>
|
||||
</md-content>
|
||||
</div>
|
||||
<div dashboard-footer></div>
|
1
www/app/components/dashboard/protractor/smoke.spec.js
Normal file
@ -0,0 +1 @@
|
||||
describe("Kubernetes UI Dashboard",function(){it("should have all the expected components loaded",function(){browser.get("http://localhost:8000"),expect(browser.getTitle()).toEqual("Kubernetes UI");var e=element(by.id("tab_001"));expect(e).toBeDefined(),e.click(),expect(browser.getLocationAbsUrl()).toBe("/dashboard/");var t=element(by.model("page"));expect(t).toBeDefined()}),it("should have the subnav view",function(){browser.get("http://localhost:8000/"),expect(by.css(".dashboard-subnav")).toBeDefined();var e=element(by.css(".selectSubPages"));expect(e).toBeDefined(),e.click();var t=element(by.model("page"));expect(t).toBeDefined(),e.click(),t.click(),expect(element(by.id("groupsView"))).toBeDefined(),expect(element(by.id("podsView"))).toBeDefined(),expect(element(by.id("minionsView"))).toBeDefined(),expect(element(by.id("rcView"))).toBeDefined(),expect(element(by.id("servicesView"))).toBeDefined(),expect(element(by.id("eventsView"))).toBeDefined(),expect(element(by.id("cAdvisorView"))).toBeDefined()}),it("should have the cAdvisor view by default",function(){browser.get("http://localhost:8000/"),expect(browser.getTitle()).toEqual("Kubernetes UI"),expect(element.all(by.css(".dashboard")).count()).toBeGreaterThan(0),expect(element.all(by.css(".server-overview")).count()).toEqual(1),expect(element(by.repeater("minion in minions.items"))).toBeDefined();var e=element(by.css("svg"));expect(e).toBeDefined()}),it("should have the correct subviews",function(){browser.get("http://localhost:8000/");for(var e=["podsView","minionsView","rcView","servicesView","eventsView"],t=0;t<e.length;t++){var o=e[t],i=element(by.model("page"));i.click();var n=element(by.id(o));expect(n).toBeDefined(),n.click(),expect(browser.getTitle()).toEqual("Kubernetes UI"),expect(by.css(".dashboard-subnav")).toBeDefined(),expect(element(by.css(".selectSubPages"))).toBeDefined();var c=element(by.model("page"));expect(c).toBeDefined(),expect(element.all(by.css(".list-pods")).count()).toEqual(1),expect(element(by.repeater("h in headers"))).toBeDefined()}}),it("should have the correct groups view",function(){browser.get("http://localhost:8000/");var e=element(by.model("page"));e.click();var t=element(by.id("groupsView"));expect(t).toBeDefined(),t.click(),expect(browser.getTitle()).toEqual("Kubernetes UI"),expect(by.css(".dashboard-subnav")).toBeDefined(),expect(element(by.css(".selectSubPages"))).toBeDefined();var o=element(by.model("page"));expect(o).toBeDefined();var e=element(by.model("selectedGroupBy"));expect(e).toBeDefined(),e.click(),expect(element(by.repeater("g in groupByOptions"))).toBeDefined()})});
|
34
www/app/components/dashboard/views/groups.html
Normal file
@ -0,0 +1,34 @@
|
||||
<div dashboard-header></div>
|
||||
<div class="dashboard">
|
||||
<div ng-controller="GroupCtrl" layout="column" class="body-wrapper groups">
|
||||
<md-content>
|
||||
<!-- page header -->
|
||||
<div class="header" layout="row">
|
||||
<div class="">Group by: </div>
|
||||
|
||||
<md-select placeholder="{{routeParams.grouping}}" class="select-group-by" ng-model="selectedGroupBy" ng-change="changeGroupBy()">
|
||||
<md-option ng-value="g.value" ng-repeat="g in groupByOptions">{{g.name}}</md-option>
|
||||
</md-select>
|
||||
|
||||
<div ng-if="selector" layout="row" class="selector-area">
|
||||
<div class="filter-label">Filter:</div>
|
||||
|
||||
<div class="filter-text">{{selector}}</div>
|
||||
<div class="filter-area" ng-if="selector && selector.length > 0">
|
||||
|
||||
<button ng-click="clearSelector(routeParams.grouping)" class="md-button cancel-button">
|
||||
|
||||
<md-icon md-svg-src="components/dashboard/img/icons/ic_close_18px.svg" class="cancel-icon" alt="Cancel"></md-icon>
|
||||
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="group-item" ng-repeat="(groupName,group) in groups.items" ng-include="'components/dashboard/views/partials/groupBox.html'"></div>
|
||||
</md-content>
|
||||
</div>
|
||||
</div>
|
||||
<div dashboard-footer></div>
|
7
www/app/components/dashboard/views/listEvents.html
Normal file
@ -0,0 +1,7 @@
|
||||
<div dashboard-header></div>
|
||||
<div class="dashboard">
|
||||
<div ng-controller="ListEventsCtrl" style="padding:25px;" class="list-pods">
|
||||
<md-table headers="headers" content="content" sortable="sortable" filters="search" custom-class="custom" thumbs="thumbs" count="count"></md-table>
|
||||
</div>
|
||||
</div>
|
||||
<div dashboard-footer></div>
|
7
www/app/components/dashboard/views/listMinions.html
Normal file
@ -0,0 +1,7 @@
|
||||
<div dashboard-header></div>
|
||||
<div class="dashboard">
|
||||
<div ng-controller="ListMinionsCtrl" style="padding:25px;" class="list-pods">
|
||||
<md-table headers="headers" content="content" sortable="sortable" filters="search" custom-class="custom" thumbs="thumbs" count="count"></md-table>
|
||||
</div>
|
||||
</div>
|
||||
<div dashboard-footer></div>
|
7
www/app/components/dashboard/views/listPods.html
Normal file
@ -0,0 +1,7 @@
|
||||
<div dashboard-header></div>
|
||||
<div class="dashboard">
|
||||
<div ng-controller="ListPodsCtrl" style="padding:25px;" class="list-pods">
|
||||
<md-table headers="headers" content="content" sortable="sortable" filters="search" custom-class="custom" thumbs="thumbs" count="count"></md-table>
|
||||
</div>
|
||||
</div>
|
||||
<div dashboard-footer></div>
|
59
www/app/components/dashboard/views/listPodsCards.html
Normal file
@ -0,0 +1,59 @@
|
||||
<div class="dashboard">
|
||||
<div ng-controller="ListPodsCtrl" style="padding:25px;" class="list-pods">
|
||||
|
||||
<md-whiteframe layout-margin class="md-whiteframe-z2">
|
||||
<md-toolbar class="">
|
||||
<div class="md-toolbar-tools">
|
||||
Pods
|
||||
</div>
|
||||
</md-toolbar>
|
||||
<md-content>
|
||||
|
||||
|
||||
<div class="pod-group">
|
||||
<md-grid-list md-cols="6" md-row-height="1:1.5" md-gutter="8px" ng-repeat="(podName, groupPods) in groupedPods">
|
||||
<md-grid-tile md-rowspan="1" md-colspan="2" class="gray">
|
||||
<md-grid-tile-footer>
|
||||
<div class="pod-title"><h2>{{podName}}</h2></div>
|
||||
</md-grid-tile-footer>
|
||||
</md-grid-tile>
|
||||
|
||||
<md-grid-tile class="purple" md-rowspan="1" md-colspan="1" ng-repeat="pod in groupPods" >
|
||||
<div>
|
||||
|
||||
<div>
|
||||
Containers:
|
||||
<span ng-repeat="container in pod.desiredState.manifest.containers">
|
||||
{{container.name}}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
Images:
|
||||
<span ng-repeat="container in pod.desiredState.manifest.containers">
|
||||
{{container.image}}
|
||||
</span>
|
||||
</div>
|
||||
<div>Internal IP: {{pod.currentState.podIP}}</div>
|
||||
<div>
|
||||
Labels:
|
||||
<span ng-repeat="(label, value) in pod.labels">{{label}}={{value}}<span ng-show="!$last">, </span></span>
|
||||
</div>
|
||||
<div>Status: {{pod.currentState.status}}</div>
|
||||
</div>
|
||||
<md-grid-tile-footer>
|
||||
|
||||
<div class="pod-host">{{pod.currentState.host}}</div>
|
||||
<div><a ng-href="/#/dashboard/pods/{{ pod.id }}"><h3>{{ pod.id }}</h3></a></div>
|
||||
|
||||
</md-grid-tile-footer>
|
||||
</md-grid-tile>
|
||||
|
||||
</md-grid-list>
|
||||
</div>
|
||||
|
||||
|
||||
</md-content>
|
||||
</md-whiteframe>
|
||||
|
||||
</div>
|
||||
</div>
|
17
www/app/components/dashboard/views/listPodsVisualizer.html
Normal file
@ -0,0 +1,17 @@
|
||||
<div dashboard-header></div>
|
||||
<div class="dashboard">
|
||||
<div ng-controller="ListPodsCtrl" style="padding:25px;" class="list-pods">
|
||||
<md-whiteframe layout-margin class="md-whiteframe-z2">
|
||||
<md-content>
|
||||
<div>
|
||||
<md-button class="md-primary" ng-click="serverView = false">By Name</md-button> |
|
||||
<md-button class="md-primary" ng-click="serverView = true">By Server</md-button>
|
||||
<div style="float:right;"><md-button class="md-primary" ng-href="/#/dashboard/pods">View Pod Listing</md-button></div>
|
||||
</div>
|
||||
<div class="pod-group" ng-show="!serverView" ng-include="'components/dashboard/views/partials/podTilesByName.html'"></div>
|
||||
<div class="pod-group" ng-show="serverView" ng-include="'components/dashboard/views/partials/podTilesByServer.html'"></div>
|
||||
</md-content>
|
||||
</md-whiteframe>
|
||||
</div>
|
||||
</div>
|
||||
<div dashboard-footer></div>
|
@ -0,0 +1,7 @@
|
||||
<div dashboard-header></div>
|
||||
<div class="dashboard">
|
||||
<div ng-controller="ListReplicationControllersCtrl" style="padding:25px;" class="list-pods">
|
||||
<md-table headers="headers" content="content" sortable="sortable" filters="search" custom-class="custom" thumbs="thumbs" count="count"></md-table>
|
||||
</div>
|
||||
</div>
|
||||
<div dashboard-footer></div>
|
7
www/app/components/dashboard/views/listServices.html
Normal file
@ -0,0 +1,7 @@
|
||||
<div dashboard-header></div>
|
||||
<div class="dashboard">
|
||||
<div ng-controller="ListServicesCtrl" style="padding:25px;" class="list-pods">
|
||||
<md-table headers="headers" content="content" sortable="sortable" filters="search" custom-class="custom" thumbs="thumbs" count="count"></md-table>
|
||||
</div>
|
||||
</div>
|
||||
<div dashboard-footer></div>
|
@ -0,0 +1,9 @@
|
||||
<div class="dashboard" ng-controller="cAdvisorController">
|
||||
<div class="server-overview">
|
||||
<md-content layout="row" layout-wrap>
|
||||
<div flex-sm="100" flex-md="50" flex-lg="33" flex-gt-lg="25" class="chart_area" ng-repeat="minion in minions.items">
|
||||
<d3-minion-bar-gauge data="activeMinionDataById[minion.id]" class="concentric" graph-width="325" graph-height="325" thickness=18 />
|
||||
</div>
|
||||
</md-content>
|
||||
</div>
|
||||
</div>
|
20
www/app/components/dashboard/views/partials/groupBox.html
Normal file
@ -0,0 +1,20 @@
|
||||
<div>
|
||||
<md-content>
|
||||
<div class="server-overview" layout="column">
|
||||
<!-- subheader -->
|
||||
<div class="group-heading" layout="row">
|
||||
<div class="label">{{routeParams.grouping | ucfirst}}: <span class="bold">{{ (groupName) || "blank" }}</span></div>
|
||||
</div>
|
||||
<!-- render group data -->
|
||||
<div ng-include="'components/dashboard/views/partials/groupItem.html'"></div>
|
||||
<div class="footer">
|
||||
<!-- Alternate box inside a box -->
|
||||
<div ng-if="group.kind == 'grouping'">
|
||||
{{group.kind}}
|
||||
<div ng-repeat="(groupName,group) in group.items" ng-include="'components/dashboard/views/partials/groupBox.html'"></div>
|
||||
</div>
|
||||
<md-divider></md-divider>
|
||||
</div>
|
||||
</div>
|
||||
</md-content>
|
||||
</div>
|
42
www/app/components/dashboard/views/partials/groupItem.html
Normal file
@ -0,0 +1,42 @@
|
||||
<div layout="row" ng-if="group.kind != 'grouping'">
|
||||
<div>
|
||||
<!-- Default box display -->
|
||||
<div layout="row" class="group-item" ng-repeat="(groupType, data) in group | groupBy: 'labels.type'">
|
||||
<!-- left image -->
|
||||
<div class="icon-area">
|
||||
<div class="group-icon" style="background-color: {{getGroupColor(groupType)}}"></div>
|
||||
</div>
|
||||
<!-- right area -->
|
||||
<div class="group-main-area" layout="column">
|
||||
<!-- type -->
|
||||
<div class="subtype">
|
||||
{{groupType | ucfirst}}s
|
||||
</div>
|
||||
<!-- links -->
|
||||
<div layout="row" layout-wrap>
|
||||
<div layout="row" ng-repeat="item in data">
|
||||
<!-- title -->
|
||||
<div ng-switch on='item.labels["type"]'>
|
||||
<div class="group-name">
|
||||
<a ng-switch-when='pod' ng-href="/#/dashboard/pods/{{ item.id }}">{{ item.id }}</a>
|
||||
<a ng-switch-when='service' ng-href="/#/dashboard/services/{{ item.id }}">{{ item.id }}</a>
|
||||
<a ng-switch-when='replicationController' ng-href="/#/dashboard/replicationcontrollers/{{ item.id }}">{{ item.id }}</a>
|
||||
<div ng-switch-default>{{item.id}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<md-select ng-model="selectedFilter" ng-change="changeFilterBy(selectedFilter)" class="selectFilter">
|
||||
<md-optgroup label="FILTER">
|
||||
<md-option ng-value="'{{key}}={{value}}'" ng-repeat="(key, value) in item.labels">{{key}}: {{value}}</md-option>
|
||||
</md-option-group>
|
||||
</md-optgroup>
|
||||
</md-select>
|
||||
<!-- This is the official button design, but requires a custom dialog. Fix up after the demo. -->
|
||||
<!-- <md-button ng-click="" class="filter-button" style="padding:0">
|
||||
<md-icon md-svg-src="components/dashboard/img/icons/ic_arrow_drop_down_18px.svg" class="filter-icon" aria-label="" alt="Filter"></md-icon>
|
||||
</md-button> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,34 @@
|
||||
|
||||
<md-grid-list class="list-color-{{$index + 1}}" md-cols="6" md-row-height="1:1" md-gutter="8px" ng-repeat="(podName, groupPods) in podsByName">
|
||||
<md-grid-tile md-rowspan="2" md-colspan="2" class="colored">
|
||||
<md-grid-tile-footer>
|
||||
<div class="pod-title"><h2>{{podName}} overview</h2></div>
|
||||
</md-grid-tile-footer>
|
||||
</md-grid-tile>
|
||||
|
||||
<md-grid-tile class="colored {{podStatusClass(pod)}}" md-rowspan="1" md-colspan="1" ng-repeat="pod in groupPods" >
|
||||
<md-grid-tile-header class="clear-bg">
|
||||
|
||||
<div layout="row">
|
||||
<div class="labels"><span ng-repeat="(label, value) in otherLabels(pod.labels)">{{label}}: {{value}}<span ng-show="!$last">, </span></span></div>
|
||||
|
||||
<div flex="20" class="restarts" ng-show="getPodRestarts(pod) > 0">
|
||||
<md-button class="md-fab restart-button">
|
||||
{{getPodRestarts(pod)}}
|
||||
</md-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</md-grid-tile-header>
|
||||
<div class="inner-box">
|
||||
|
||||
<div ng-show="podStatusClass(pod)">Status: {{pod.currentState.status}}</div>
|
||||
</div>
|
||||
<md-grid-tile-footer>
|
||||
|
||||
<div class="pod-host">{{pod.currentState.host}}</div>
|
||||
<div><a ng-href="/#/dashboard/pods/{{ pod.id }}"><h3>{{ pod.id }}</h3></a></div>
|
||||
|
||||
</md-grid-tile-footer>
|
||||
</md-grid-tile>
|
||||
</md-grid-list>
|
@ -0,0 +1,34 @@
|
||||
<md-grid-list class="" md-cols="6" md-row-height="1:1" md-gutter="8px" ng-repeat="(serverIp, groupPods) in podsByServer">
|
||||
<md-grid-tile md-rowspan="2" md-colspan="2" class="gray">
|
||||
<md-grid-tile-footer>
|
||||
<div class="pod-title"><h2>{{serverIp}} overview</h2></div>
|
||||
</md-grid-tile-footer>
|
||||
</md-grid-tile>
|
||||
|
||||
<md-grid-tile class="color-{{podIndexFromName(pod)}} {{podStatusClass(pod)}}" md-rowspan="1" md-colspan="1" ng-repeat="pod in groupPods" >
|
||||
<md-grid-tile-header class="clear-bg">
|
||||
|
||||
<div layout="row">
|
||||
<div class="labels"><span ng-repeat="(label, value) in otherLabels(pod.labels)">{{label}}: {{value}}<span ng-show="!$last">, </span></span></div>
|
||||
|
||||
<div flex="20" class="restarts" ng-show="getPodRestarts(pod) > 0">
|
||||
<md-button class="md-fab restart-button">
|
||||
{{getPodRestarts(pod)}}
|
||||
</md-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</md-grid-tile-header>
|
||||
<div class="inner-box">
|
||||
|
||||
<div ng-show="podStatusClass(pod)">Status: {{pod.currentState.status}}</div>
|
||||
</div>
|
||||
<md-grid-tile-footer>
|
||||
|
||||
<div class="pod-host">{{pod.labels.name}}</div>
|
||||
<div><a ng-href="/#/dashboard/pods/{{ pod.id }}"><h3>{{ pod.id }}</h3></a></div>
|
||||
|
||||
</md-grid-tile-footer>
|
||||
</md-grid-tile>
|
||||
|
||||
</md-grid-list>
|
97
www/app/components/dashboard/views/pod.html
Normal file
@ -0,0 +1,97 @@
|
||||
<div dashboard-header></div>
|
||||
<div class="dashboard">
|
||||
<div ng-controller="PodCtrl" layout="column" class="body-wrapper pod">
|
||||
<div class="detail">
|
||||
|
||||
<div class="back">
|
||||
<div class="nav-back">
|
||||
<a ng-click="doTheBack()">BACK</a>
|
||||
</div>
|
||||
<!-- <md-button class="md-default-theme" ng-click="doTheBack">BACK</md-button> -->
|
||||
</div>
|
||||
|
||||
<div class="heading">
|
||||
<span class="label">Pod:</span>
|
||||
<span>{{pod.id}}</span>
|
||||
</div>
|
||||
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="name">Status</td>
|
||||
<td class="value">
|
||||
{{pod.currentState.status}} on <a ng-href="/#/dashboard/groups/host/selector/host={{pod.currentState.host}}">{{pod.currentState.host}}</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="name">Created</td>
|
||||
<td class="value">
|
||||
{{pod.creationTimestamp | date:'medium'}}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="name">Pod Networking</td>
|
||||
<td class="value">
|
||||
{{pod.currentState.podIP}}
|
||||
<span ng-repeat="container in pod.desiredState.manifest.containers">
|
||||
<span ng-repeat="port in container.ports">
|
||||
: {{port.containerPort}}
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="name">Host Networking</td>
|
||||
<td class="value">
|
||||
{{pod.currentState.hostIP}}
|
||||
<span ng-repeat="container in pod.desiredState.manifest.containers">
|
||||
<span ng-repeat="port in container.ports">
|
||||
:{{port.hostPort}}
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="name">Labels</td>
|
||||
<td class="value">
|
||||
<div ng-repeat="(label, value) in pod.labels">
|
||||
{{label}}: {{value}}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="name">Containers</td>
|
||||
<td class="value">
|
||||
|
||||
<table class="containerTable">
|
||||
<tr>
|
||||
<td>Name</td>
|
||||
<td>Image</td>
|
||||
<td>Restarts</td>
|
||||
</tr>
|
||||
<tr ng-repeat="container in pod.desiredState.manifest.containers">
|
||||
<td>{{container.name}}</td>
|
||||
<td>{{container.image}}</td>
|
||||
<td>{{pod.currentState.info[container.name].restartCount}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div dashboard-footer></div>
|
82
www/app/components/dashboard/views/replication.html
Normal file
@ -0,0 +1,82 @@
|
||||
<div dashboard-header></div>
|
||||
<div class="dashboard">
|
||||
<div ng-controller="ReplicationControllerCtrl" layout="column" class="body-wrapper">
|
||||
|
||||
<div class="detail">
|
||||
|
||||
<div class="back">
|
||||
<md-button class="md-default-theme" ng-href="/#/dashboard/replicationcontrollers">‹ BACK</md-button>
|
||||
</div>
|
||||
|
||||
<div class="heading">
|
||||
<span class="label">Replication Controller: </span>
|
||||
<span>{{replicationController.id}}</span>
|
||||
</div>
|
||||
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="name">Created</td>
|
||||
<td class="value">
|
||||
{{replicationController.creationTimestamp | date:'medium'}}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="name">Desired Replicas</td>
|
||||
<td class="value">
|
||||
{{replicationController.desiredState.replicas}}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="name">Current Replicas</td>
|
||||
<td class="value">
|
||||
{{replicationController.currentState.replicas}}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="name">Labels</td>
|
||||
<td class="value">
|
||||
<div ng-repeat="(label, value) in replicationController.labels">
|
||||
{{label}}: {{value}}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="name">Related Pods</td>
|
||||
<td class="value">
|
||||
<div ng-repeat="(label, value) in replicationController.desiredState.replicaSelector">
|
||||
<a ng-href="/#/dashboard/groups/type/selector/{{label}}={{value}},type=pod">{{label}}: {{value}}</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
<tr>
|
||||
<td class="name">Related Services</td>
|
||||
<td class="value">
|
||||
<div ng-repeat="(label, value) in replicationController.desiredState.replicaSelector">
|
||||
<a ng-href="/#/dashboard/groups/type/selector/{{label}}={{value}},type=service">{{label}}: {{value}}</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div dashboard-footer></div>
|
97
www/app/components/dashboard/views/service.html
Normal file
@ -0,0 +1,97 @@
|
||||
<div dashboard-header></div>
|
||||
<div class="dashboard">
|
||||
<div ng-controller="ServiceCtrl" layout="column" class="body-wrapper service">
|
||||
|
||||
<div class="detail">
|
||||
|
||||
<div class="back">
|
||||
<md-button class="md-default-theme" ng-href="/#/dashboard/services">‹ BACK</md-button>
|
||||
</div>
|
||||
|
||||
<div class="heading">
|
||||
<span class="label">Service: </span>
|
||||
<span>{{service.id}}</span>
|
||||
</div>
|
||||
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="name">Created</td>
|
||||
<td class="value">
|
||||
{{service.creationTimestamp | date:'medium'}}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="name">Port</td>
|
||||
<td class="value">
|
||||
{{service.port}}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="name">Container Port</td>
|
||||
<td class="value">
|
||||
{{service.containerPort}}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="name">Portal IP</td>
|
||||
<td class="value">
|
||||
{{service.portalIP}}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="name">Protocol</td>
|
||||
<td class="value">
|
||||
{{service.protocol}}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="name">Session Affinity</td>
|
||||
<td class="value">
|
||||
{{service.sessionAffinity}}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="name">Labels</td>
|
||||
<td class="value">
|
||||
<div ng-repeat="(label, value) in service.labels">
|
||||
{{label}}: {{value}}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="name">Related Pods</td>
|
||||
<td class="value">
|
||||
<div ng-repeat="(label, value) in service.selector">
|
||||
<a ng-href="/#/dashboard/groups/type/selector/{{label}}={{value}},type=pod">{{label}}: {{value}}</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="name">Related Replication Controllers</td>
|
||||
<td class="value">
|
||||
<div ng-repeat="(label, value) in service.selector">
|
||||
<a ng-href="/#/dashboard/groups/type/selector/{{label}}={{value}},type=replicationController">{{label}}: {{value}}</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div dashboard-footer></div>
|
60
www/app/index.html
Normal file
@ -0,0 +1,60 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" ng-app="kubernetesApp">
|
||||
<head>
|
||||
<title>Kubernetes UI</title>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no" />
|
||||
<link rel="stylesheet" href="vendor/angular-material/angular-material.css">
|
||||
<link rel="stylesheet" href="assets/css/app.css" >
|
||||
<link rel="shortcut icon" href="assets/img/icons/favicon.png" type="image/vnd.microsoft.icon" />
|
||||
</head>
|
||||
<body layout="row">
|
||||
<md-sidenav layout="column"
|
||||
md-is-locked-open="shouldLockOpen()"
|
||||
style="overflow: hidden; display: flex;"
|
||||
class="site-sidenav md-sidenav-left md-whiteframe-z2"
|
||||
md-component-id="left"
|
||||
md-closed>
|
||||
<md-toolbar>
|
||||
<h1 class="md-toolbar-tools">
|
||||
<a ng-href="/" layout="row" flex>
|
||||
<span class="kubernetes-ui-logo"></span>
|
||||
<div style="line-height:40px; text-indent: 15px;">Kubernetes</div>
|
||||
</a>
|
||||
</h1>
|
||||
</md-toolbar>
|
||||
<md-content flex>
|
||||
<div kubernetes-ui-menu role="kubernetes-ui-menu"></div>
|
||||
<div compile="sidenavLeft"></div>
|
||||
</md-content>
|
||||
</md-sidenav>
|
||||
<div layout="column" layout-fill tabIndex="-1" role="main" flex>
|
||||
<md-toolbar>
|
||||
<div class="md-toolbar-tools">
|
||||
<h1 class="md-toolbar-tools">
|
||||
<a ng-href="/" layout="row" flex>
|
||||
<span class="kubernetes-ui-logo"></span>
|
||||
<div style="line-height:40px; text-indent: 15px;">Kubernetes</div>
|
||||
</a>
|
||||
</h1>
|
||||
</div>
|
||||
</md-toolbar>
|
||||
<md-content md-scroll-y flex>
|
||||
<md-whiteframe layout layout-align="center center">
|
||||
<div ng-controller="TabCtrl" class="tabsDefaultTabs">
|
||||
<md-tabs md-selected="0">
|
||||
<md-tab ng-repeat="tab in tabs" md-on-select="switchTab($index)" label="{{tab.title}}">
|
||||
<div class="demo-tab tab{{$index%4}}" layout="column" layout-fill></div>
|
||||
</md-tab>
|
||||
</md-tabs>
|
||||
<div ng-view layout="column" layout-fill role="main"></div>
|
||||
</div>
|
||||
</md-whiteframe>
|
||||
</md-content>
|
||||
</div>
|
||||
<script src="assets/js/base.js"></script>
|
||||
<script src="assets/js/app.js"></script>
|
||||
</body>
|
||||
</html>
|
11993
www/app/shared/assets/sampleData1.json
Normal file
0
www/app/vendor/.gitkeep
vendored
Normal file
1
www/app/vendor/angular-json-human/dist/angular-json-human.css
vendored
Normal file
@ -0,0 +1 @@
|
||||
.jh-key,.jh-root,.jh-root tr,.jh-type-array,.jh-type-object,.jh-value{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.jh-key,.jh-value{margin:0;padding:.2em}.jh-value{border-left:1px solid #ddd}.jh-type-bool,.jh-type-number{font-weight:700;text-align:center;color:#5286BC}.jh-type-string{font-style:italic;color:#839B00}.jh-array-key{font-style:italic;font-size:small;text-align:center}.jh-array-key,.jh-object-key{color:#444;vertical-align:top}.jh-type-array>tbody>tr:nth-child(odd),.jh-type-object>tbody>tr:nth-child(odd){background-color:#f5f5f5}.jh-type-array>tbody>tr:nth-child(even),.jh-type-object>tbody>tr:nth-child(even){background-color:#fff}.jh-type-array,.jh-type-object{width:100%;border-collapse:collapse}.jh-root{border:1px solid #ccc;margin:.2em}th.jh-key{text-align:left}.jh-type-array>tbody>tr,.jh-type-object>tbody>tr{border:1px solid #ddd;border-bottom:none}.jh-type-array>tbody>tr:last-child,.jh-type-object>tbody>tr:last-child{border-bottom:1px solid #ddd}.jh-type-array>tbody>tr:hover,.jh-type-object>tbody>tr:hover{border:1px solid #F99927}.jh-empty{font-style:italic;color:#999;font-size:small}
|
6
www/app/vendor/angular-material/angular-material.css
vendored
Normal file
5
www/app/vendor/d3/d3.min.js
vendored
Normal file
1
www/app/views/partials/404.html
Normal file
@ -0,0 +1 @@
|
||||
<span>The page you're looking for could not be found.</span>
|
14
www/app/views/partials/kubernetes-ui-menu.tmpl.html
Normal file
@ -0,0 +1,14 @@
|
||||
<ul class="kubernetes-ui-menu">
|
||||
<li ng-repeat="section in menu.sections" class="parent-list-item" ng-class="{'parentActive' : isSectionSelected(section)}">
|
||||
<h2 class="menu-heading" ng-if="section.type === 'heading'" id="heading_{{ section.name | nospace }}">
|
||||
{{section.name}}
|
||||
</h2>
|
||||
<menu-link section="section" ng-if="section.type === 'link'"></menu-link>
|
||||
<menu-toggle section="section" ng-if="section.type === 'toggle'"></menu-toggle>
|
||||
<ul ng-if="section.children" class="menu-nested-list">
|
||||
<li ng-repeat="child in section.children" ng-class="{'childActive' : isSectionSelected(child)}">
|
||||
<menu-toggle section="child"></menu-toggle>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
45
www/app/views/partials/md-table.tmpl.html
Normal file
@ -0,0 +1,45 @@
|
||||
<table class="md-table">
|
||||
<thead>
|
||||
<tr class="md-table-headers-row">
|
||||
<th class="md-table-header" ng-repeat="h in headers">
|
||||
<a href ng-if="handleSort(h.field)" ng-click="reverse=!reverse;order(h.field,reverse)">{{h.name}} <span class="md-table-caret" ng-show="reverse && h.field == predicate"><img src="https://google.github.io/material-design-icons/navigation/svg/ic_arrow_drop_up_24px.svg"></span><span class="md-table-caret" ng-show="!reverse && h.field == predicate"><img src="https://google.github.io/material-design-icons/navigation/svg/ic_arrow_drop_down_24px.svg"></span></a>
|
||||
<span ng-if="!handleSort(h.field)">{{h.name}}</span>
|
||||
</th>
|
||||
<th class="md-table-header"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="md-table-content-row" ng-repeat="c in content | filter:filters | startFrom:currentPage*count | limitTo: count">
|
||||
<td ng-repeat="h in headers" ng-if="h.field == thumbs" class="md-table-thumbs">
|
||||
<div ng-if="h.field == thumbs" style="background-image:url({{c.thumb}})"></div>
|
||||
</td>
|
||||
<td class="md-table-content" ng-click="go(c)" ng-repeat="h in headers" ng-class="customClass[h.field]" ng-if="h.field != thumbs">
|
||||
{{c[h.field]}}
|
||||
</td>
|
||||
<td class="md-table-td-more">
|
||||
<md-button aria-label="More">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<path d="M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/>
|
||||
</svg>
|
||||
</md-button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="md-table-footer" layout="row">
|
||||
<span class="md-table-count-info">Rows count per page : <a href ng-click="goToPage(0); count=1">1</a>, <a href ng-click="goToPage(0); count=10">10</a>, <a href ng-click="goToPage(0); count=25">25</a>, <a href ng-click="goToPage(0); count=50">50</a>, <a href ng-click="goToPage(0); count=100">100</a> (current is <strong>{{count}}</strong>)</span>
|
||||
<span flex></span>
|
||||
<span ng-show="nbOfPages() > 1">
|
||||
<md-button aria-label="Back" class="md-table-footer-item" ng-disabled="currentPage==0" ng-click="currentPage=currentPage-1">
|
||||
<img src="//google.github.io/material-design-icons/hardware/svg/ic_keyboard_arrow_left_24px.svg">
|
||||
</md-button>
|
||||
<a href ng-repeat="i in getNumber(nbOfPages()) track by $index" >
|
||||
<md-button aria-label="Next" class="md-primary md-table-footer-item" ng-click="goToPage($index)">
|
||||
<span ng-class="{ 'md-table-active-page': currentPage==$index}">{{$index+1}}</span>
|
||||
</md-button>
|
||||
</a>
|
||||
<md-button aria-label="Jump" class="md-table-footer-item" ng-disabled="currentPage==nbOfPages()-1" ng-click="currentPage=currentPage+1">
|
||||
<img src="//google.github.io/material-design-icons/hardware/svg/ic_keyboard_arrow_right_24px.svg">
|
||||
</md-button>
|
||||
</span>
|
||||
</div>
|
14
www/app/views/partials/menu-toggle.tmpl.html
Normal file
@ -0,0 +1,14 @@
|
||||
<md-button class="md-button-toggle"
|
||||
ng-click="toggle()"
|
||||
aria-controls="kubernetes-ui-menu-{{section.name | nospace}}"
|
||||
flex layout="row"
|
||||
aria-expanded="{{isOpen()}}">
|
||||
{{section.name}}
|
||||
<span aria-hidden="true" class="md-toggle-icon" ng-class="{'toggled' : isOpen()}"></span>
|
||||
<span class="visually-hidden">Toggle {{isOpen()? 'expanded' : 'collapsed'}}</span>
|
||||
</md-button>
|
||||
<ul ng-show="isOpen()" id="kubernetes-ui-menu-{{section.name | nospace}}" class="menu-toggle-list">
|
||||
<li ng-repeat="page in section.pages">
|
||||
<menu-link section="page"></menu-link>
|
||||
</li>
|
||||
</ul>
|
62
www/box.ng
@ -1,62 +0,0 @@
|
||||
<!--
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<div style="margin-top: 10px">
|
||||
<div class="k8s-title-font k8s-box">
|
||||
{{ groupName }}
|
||||
|
||||
<div ng-if="group.kind != 'grouping'">
|
||||
<div ng-if="!settings.display || settings.display=='box'">
|
||||
<div class="content k8s-item k8s-inline" ng-repeat="item in group">
|
||||
<div class="k8s-title-font k8s-font-regular">
|
||||
<div ng-switch on='item.labels["type"]'>
|
||||
<a ng-switch-when='pod' href="#/pods/{{ item.id }}">{{ item.id }}</a>
|
||||
<a ng-switch-when='service' href="#/services/{{ item.id }}">{{ item.id }}</a>
|
||||
<a ng-switch-when='replicationController' href="#/replicationControllers/{{ item.id }}">{{ item.id }}</a>
|
||||
<span ng-switch-default>{{item.id}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="settings.display=='list'">
|
||||
<table style="width: 90%; padding: 10px">
|
||||
<tr ng-repeat="item in group" ng-class-odd="'k8s-odd'" ng-class-even="'k8s-even'" valign="top">
|
||||
<td class="k8s-cell">
|
||||
<div ng-switch on='item.labels["type"]'>
|
||||
<a ng-switch-when='pod' href="#/pods/{{ item.id }}">{{ item.id }}</a>
|
||||
<a ng-switch-when='service' href="#/services/{{ item.id }}">{{ item.id }}</a>
|
||||
<a ng-switch-when='replicationController' href="#/replicationControllers/{{ item.id }}">{{ item.id }}</a>
|
||||
<span ng-switch-default>{{item.id}}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="k8s-cell">
|
||||
<div ng-repeat='(key, value) in item.labels'>
|
||||
<a href="#/groups/{{key}}/selector/{{controller.routeParams.selector}}">{{key}}</a> :
|
||||
<a href="#/groups/{{controller.routeParams.grouping}}/selector/{{key}}={{value}}">{{value}}</a> </span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="group.kind == 'grouping'">
|
||||
<div ng-repeat="(groupName,group) in group.items" ng-include="'box.ng'">
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<a ng-click="settings={display:'box'}">box</a>
|
||||
<a ng-click="settings={display:'list'}">list</a>
|
||||
<a ng-click="controller.resetGroupLayout(this);">reset</a>
|
||||
</div>
|
||||
</div>
|
@ -1,39 +0,0 @@
|
||||
<!--
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<html ng-app="k8s">
|
||||
<head>
|
||||
<title>Kubernetes</title>
|
||||
<link href='https://fonts.googleapis.com/css?family=Ubuntu%20Mono' rel='stylesheet' type='text/css'>
|
||||
<link href='https://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.25/angular.min.js"></script>
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.25/angular-route.min.js"></script>
|
||||
<!--
|
||||
For local/network free development.
|
||||
<script src="/angular.min.js"></script>
|
||||
<!-- -->
|
||||
<script src="podcontroller.js"></script>
|
||||
<link rel="stylesheet" href="k8s-style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="navbar">
|
||||
<div class="navbar-logo">
|
||||
<img src="logotext.svg" id="nav-logo-img" alt="logo">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-view></div>
|
||||
</body>
|
||||
</html>
|
@ -1,108 +0,0 @@
|
||||
.logo {
|
||||
background: url('titlelogo.svg');
|
||||
background-repeat: no-repeat;
|
||||
height: 150px;
|
||||
width: 600px;
|
||||
}
|
||||
|
||||
.navbar-logo {
|
||||
margin-left: 10px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: #fff;
|
||||
font-size: 14pt;
|
||||
height: 20px;
|
||||
padding-top: 10px;
|
||||
padding-left: 10px;
|
||||
width: 1024px;
|
||||
}
|
||||
|
||||
#nav-logo-img {
|
||||
height: 42px;
|
||||
}
|
||||
|
||||
.spread {
|
||||
width: 1024px;
|
||||
}
|
||||
|
||||
.content {
|
||||
margin-left: 20px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.bar {
|
||||
background: #DFE7FA;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
background-color: #447AE8;
|
||||
border: 0px;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.link {
|
||||
color: #22F;
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.k8s-title-font {
|
||||
font-family: 'Ubuntu Mono', arial, sans-serif;
|
||||
font-size: 14pt;
|
||||
}
|
||||
|
||||
.k8s-font-medium {
|
||||
font-size: 18pt;
|
||||
}
|
||||
|
||||
.k8s-font-regular {
|
||||
font-size: 12pt;
|
||||
}
|
||||
|
||||
.k8s-box {
|
||||
background: #CCC;
|
||||
border: 1px solid darkgray;
|
||||
padding: 10px;
|
||||
width: 90%;
|
||||
margin-left: 5%;
|
||||
}
|
||||
|
||||
.k8s-item {
|
||||
background: #DDD;
|
||||
border: 1px solid whitesmoke;
|
||||
padding: 10px;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.k8s-list {
|
||||
background: #DDD;
|
||||
border: 1px solid whitesmoke;
|
||||
padding: 10px;
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.k8s-inline {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.k8s-button {
|
||||
border: 1px solid black;
|
||||
background: lightgray;
|
||||
cursor: pointer;
|
||||
padding: 2px 4px 2px 4px;
|
||||
}
|
||||
|
||||
.k8s-odd {
|
||||
background: #EEE;
|
||||
}
|
||||
|
||||
.k8s-even {
|
||||
background: #DDD;
|
||||
}
|
||||
|
||||
.k8s-cell {
|
||||
padding: 10px;
|
||||
}
|
374
www/logotext.svg
@ -1,374 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
version="1.1"
|
||||
width="562.78589"
|
||||
height="134.62993"
|
||||
id="svg2"
|
||||
xml:space="preserve"><metadata
|
||||
id="metadata8"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs6" /><g
|
||||
transform="matrix(1.25,0,0,-1.25,-169.2546,1414.8444)"
|
||||
id="g10"><g
|
||||
transform="matrix(0,0.18092,-0.18275,0,0,0)"
|
||||
id="g12"><path
|
||||
d="m 6196.6587,-1043.6173 -94.2902,-195.4939 -211.9113,-48.3046 -169.9617,135.2607 -0.025,216.9692 169.9297,135.2974 211.9254,-48.257 94.3336,-195.4718 z"
|
||||
id="path14"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:118.52590179;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
|
||||
d="m 190.7198,1121.0876 35.725,-17.0586 8.8274,-38.3391 -24.7181,-30.7489 -39.6505,0 -24.7249,30.7434 8.8192,38.3412 35.7219,17.0665 z"
|
||||
id="path16"
|
||||
style="fill:#336ee5;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
|
||||
transform="matrix(0,0.18092,-0.18275,0,0,0)"
|
||||
id="g18"><path
|
||||
d="m 6196.6587,-1043.6173 -94.2888,-195.4939 -211.9141,-48.3046 -169.9603,135.2593 -0.025,216.9723 169.9297,135.2942 211.9237,-48.2572 94.3353,-195.4701 z"
|
||||
id="path20"
|
||||
style="fill:none;stroke:#336ee5;stroke-width:74.74790192;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><g
|
||||
transform="scale(0.18275,0.18275)"
|
||||
id="g22"><path
|
||||
d="m 1013.0746,6022.3961 c 73.5242,16.6963 146.8298,-29.4129 163.7263,-102.9881 16.9013,-73.5693 -29.0033,-146.7459 -102.5258,-163.4409 -73.5273,-16.6903 -146.8343,29.4189 -163.7325,102.9867 -16.8967,73.5769 29.0047,146.7505 102.532,163.4423 z"
|
||||
id="path24"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:30.78089905;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
|
||||
d="m 188.4186,1104.8074 2.7468,-0.01 0.1749,-20.1918 -4.0657,-0.038 1.144,20.236 z"
|
||||
id="path26"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
|
||||
transform="scale(0.17178,0.18275)"
|
||||
id="g28"><path
|
||||
d="m 1096.8024,6045.6095 15.9899,-0.036 1.0191,-110.4894 -23.6699,-0.2094 6.6609,110.7345 z"
|
||||
id="path30"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:0.41159999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
|
||||
d="m 193.0309,1104.8074 -2.7474,-0.01 -0.1703,-20.1918 4.0654,-0.037 -1.1477,20.2354 z"
|
||||
id="path32"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
|
||||
transform="scale(0.17178,0.18275)"
|
||||
id="g34"><path
|
||||
d="m 1123.6518,6045.6098 -15.993,-0.036 -0.991,-110.4894 23.6681,-0.2029 -6.6841,110.7283 z"
|
||||
id="path36"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:0.41159999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
|
||||
d="m 192.8625,1112.4315 c 0,-1.3119 -0.9576,-2.3758 -2.1382,-2.3758 -1.1806,0 -2.1379,1.0639 -2.1379,2.3752 0,1.3119 0.9564,2.3754 2.1379,2.3763 1.1806,0 2.1382,-1.0636 2.1382,-2.3757"
|
||||
id="path38"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
|
||||
transform="matrix(0.16447,2e-5,-2e-5,0.18275,0,0)"
|
||||
id="g40"><path
|
||||
d="m 1173.5053,6087.183 c -8e-4,-7.1804 -5.8238,-12.9997 -13.0019,-12.9988 -7.1785,8e-4 -12.998,5.8229 -12.9969,12.9971 0,7.1819 5.817,13.0011 13.0006,13.0031 7.1781,-6e-4 12.9994,-5.8229 12.9982,-13.0014 z"
|
||||
id="path42"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:0.2744;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
|
||||
d="m 188.5873,1112.4629 c 5e-4,-0.1876 -0.009,-0.458 -0.003,-0.6389 0.0289,-0.7566 0.1945,-1.3368 0.294,-2.0344 0.1797,-1.4922 0.3311,-2.7289 0.2381,-3.8781 -0.0851,-0.5757 -0.4184,-0.8028 -0.6959,-1.0689 l 3.3721,-2.2315 -0.5069,9.8354 -2.6988,0.016 z"
|
||||
id="path44"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
|
||||
transform="matrix(0,-0.17178,0.18275,0,0,0)"
|
||||
id="g46"><path
|
||||
d="m -6476.0579,1031.9675 c 1.0925,0 2.6666,-0.048 3.7194,-0.014 4.4045,0.1568 7.7839,1.0641 11.8431,1.6087 8.6865,0.9819 15.8862,1.8102 22.5791,1.3028 3.3483,-0.4652 4.6701,-2.2896 6.2212,-3.8095 l 12.9884,18.4555 -57.257,-2.7751 -0.094,-14.7685 z"
|
||||
id="path48"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:0.26840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
|
||||
d="m 192.8625,1112.4629 c -0.001,-0.1876 0.008,-0.458 0.002,-0.6389 -0.0289,-0.7566 -0.1942,-1.3368 -0.2934,-2.0344 -0.1797,-1.4922 -0.3311,-2.7289 -0.2384,-3.8781 0.0847,-0.5757 0.4184,-0.8028 0.6959,-1.0689 l -3.3724,-2.2315 0.5074,9.8354 2.6989,0.016 z"
|
||||
id="path50"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
|
||||
transform="matrix(0,-0.17178,0.18275,0,0,0)"
|
||||
id="g52"><path
|
||||
d="m -6476.0579,1055.3604 c 1.0925,0 2.6666,0.046 3.7194,0.011 4.4045,-0.1534 7.7839,-1.0624 11.8431,-1.6022 8.6865,-0.9867 15.8862,-1.815 22.5791,-1.3076 3.3483,0.4669 4.6701,2.291 6.2212,3.8081 l 12.9884,-18.4541 -57.257,2.7765 -0.094,14.7685 z"
|
||||
id="path54"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:0.26840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><g
|
||||
transform="scale(0.18275,0.18275)"
|
||||
id="g56"><path
|
||||
d="m 1073.7275,5865.2637 -30.1062,-14.4303 -30.1,14.438 -7.4344,32.4422 20.8395,26.0065 33.4099,0 20.8321,-26.0175 -7.4409,-32.4374 z"
|
||||
id="path58"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:30.34600067;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
|
||||
d="m 166.9153,1092.2468 1.7178,2.1436 15.8952,-12.4532 -2.5049,-3.2023 -15.1081,13.5119 z"
|
||||
id="path60"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
|
||||
transform="matrix(0.1071,0.1343,-0.14288,0.11394,0,0)"
|
||||
id="g62"><path
|
||||
d="m 5577.0313,3012.37 15.9908,-0.036 1.0134,-110.4917 -23.6665,-0.2083 6.6623,110.7357 z"
|
||||
id="path64"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:0.41159999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
|
||||
d="m 169.7905,1095.853 -1.7074,-2.1523 15.6805,-12.723 2.5636,3.1561 -16.5367,11.7192 z"
|
||||
id="path66"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
|
||||
transform="matrix(0.1071,0.1343,-0.14288,0.11394,0,0)"
|
||||
id="g68"><path
|
||||
d="m 5603.8799,3012.3729 -15.9928,-0.039 -0.9944,-110.4931 23.6693,-0.2001 -6.6821,110.7321 z"
|
||||
id="path70"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:0.41159999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
|
||||
d="m 163.7252,1100.4746 c 1.0258,-0.8183 1.2608,-2.2297 0.5247,-3.1529 -0.7368,-0.9233 -2.1651,-1.0089 -3.191,-0.1908 -1.0256,0.8181 -1.2606,2.2292 -0.5238,3.1524 0.7358,0.9235 2.1642,1.0086 3.1901,0.1913"
|
||||
id="path72"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
|
||||
transform="matrix(0.10253,0.1286,-0.14289,0.11392,0,0)"
|
||||
id="g74"><path
|
||||
d="m 5852.363,3053.3992 c 0,-7.181 -5.8216,-13.0011 -13.0009,-13.0005 -7.1815,0 -13.0025,5.8227 -13.0013,13.0025 0,7.1796 5.8198,12.9977 13.0013,12.9949 7.1799,0 12.998,-5.8198 13.0009,-12.9969 z"
|
||||
id="path76"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:0.2744;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
|
||||
d="m 161.0351,1097.1516 c 0.1474,-0.1171 0.3518,-0.2931 0.4986,-0.4005 0.6089,-0.4496 1.1656,-0.6818 1.7734,-1.0387 1.2781,-0.7894 2.3397,-1.4428 3.1807,-2.232 0.3969,-0.4249 0.3662,-0.8268 0.4008,-1.2104 l 3.8472,1.2453 -8.0047,5.7356 -1.696,-2.0993 z"
|
||||
id="path78"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
|
||||
transform="matrix(0.1343,-0.1071,0.11394,0.14288,0,0)"
|
||||
id="g80"><path
|
||||
d="m -3249.2299,5243.3232 c 1.0919,-9e-4 2.6612,-0.054 3.7205,-0.014 4.403,0.1539 7.7794,1.0602 11.8417,1.6056 8.6817,0.9844 15.8837,1.8121 22.5768,1.3042 3.3486,-0.4641 4.6681,-2.2882 6.2175,-3.8109 l 12.9912,18.4518 -57.2536,-2.7726 -0.094,-14.7639 z"
|
||||
id="path82"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:0.26840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
|
||||
d="m 163.6999,1100.4936 c 0.1472,-0.1176 0.3643,-0.278 0.5012,-0.3965 0.574,-0.4944 0.9247,-0.9851 1.4077,-1.4979 1.0548,-1.0709 1.9275,-1.9601 2.8842,-2.6039 0.5026,-0.2928 0.8876,-0.1737 1.2691,-0.1221 l -0.3586,-4.0275 -7.3732,6.5273 1.6696,2.1206 z"
|
||||
id="path84"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
|
||||
transform="matrix(0.1343,-0.1071,0.11394,0.14288,0,0)"
|
||||
id="g86"><path
|
||||
d="m -3249.2341,5266.7113 c 1.0978,-3e-4 2.6671,0.053 3.7204,0.013 4.4068,-0.1565 7.7837,-1.0596 11.8432,-1.6053 8.6876,-0.983 15.8876,-1.8099 22.5782,-1.2999 3.3503,0.462 4.6704,2.2847 6.2195,3.8072 l 12.9884,-18.4521 -57.2517,2.768 -0.098,14.7688 z"
|
||||
id="path88"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:0.26840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
|
||||
d="m 163.3717,1067.5885 -0.6052,2.6796 19.6464,4.6636 0.9419,-3.9555 -19.9831,-3.3877 z"
|
||||
id="path90"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
|
||||
transform="matrix(0.03823,-0.16747,0.17816,0.04067,0,0)"
|
||||
id="g92"><path
|
||||
d="m -5847.3595,2171.5736 -15.992,0.034 -1.017,110.4899 23.669,0.2087 -6.66,-110.7329 z"
|
||||
id="path94"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:0.41159999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
|
||||
d="m 162.3444,1072.0845 0.6182,-2.6767 19.7238,4.3271 -0.8685,3.9719 -19.4735,-5.6223 z"
|
||||
id="path96"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
|
||||
transform="matrix(0.03823,-0.16747,0.17816,0.04067,0,0)"
|
||||
id="g98"><path
|
||||
d="m -5874.2073,2171.5679 15.9931,0.04 0.9924,110.4916 -23.6673,0.203 6.6818,-110.7349 z"
|
||||
id="path100"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:0.41159999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
|
||||
d="m 154.9497,1070.2236 c 1.2787,0.2928 2.529,-0.4039 2.7921,-1.5551 0.2625,-1.1509 -0.561,-2.3213 -1.8405,-2.6127 -1.2785,-0.2928 -2.5285,0.4039 -2.7919,1.5551 -0.2625,1.1509 0.5613,2.3207 1.8403,2.6127"
|
||||
id="path102"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
|
||||
transform="matrix(0.03662,-0.16034,0.17816,0.04069,0,0)"
|
||||
id="g104"><path
|
||||
d="m -6133.9467,2130.5761 c 0,7.1782 5.8161,12.9985 12.9991,12.9988 7.1756,-3e-4 12.9985,-5.8192 12.9951,-13.0005 0.01,-7.1753 -5.8189,-12.9966 -12.9988,-12.9988 -7.1773,5e-4 -12.9963,5.8218 -12.9954,13.0005 z"
|
||||
id="path106"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:0.2744;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
|
||||
d="m 155.8706,1066.049 c 0.1829,0.042 0.4488,0.093 0.6234,0.14 0.7316,0.1962 1.2606,0.4867 1.9182,0.7387 1.4148,0.508 2.5869,0.9301 3.7281,1.0951 0.58,0.046 0.8745,-0.2282 1.1968,-0.4405 l 1.4247,3.7842 -9.4759,-2.6816 0.5847,-2.6359 z"
|
||||
id="path108"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
|
||||
transform="matrix(0.16747,0.03823,-0.04067,0.17816,0,0)"
|
||||
id="g110"><path
|
||||
d="m 2265.6285,5497.4356 c 1.0922,0 2.6666,-0.045 3.7177,-0.01 4.4093,0.1534 7.7862,1.0596 11.8448,1.603 8.6851,0.9845 15.8865,1.8117 22.5771,1.3023 3.3483,-0.4592 4.6676,-2.2825 6.2201,-3.8072 l 12.9895,18.4535 -57.2534,-2.7672 -0.096,-14.7744 z"
|
||||
id="path112"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:0.26840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
|
||||
d="m 154.9191,1070.2165 c 0.1834,0.042 0.4447,0.1106 0.6224,0.1451 0.7447,0.1404 1.3471,0.1078 2.0489,0.1661 1.4947,0.1571 2.7338,0.2849 3.8339,0.6313 0.5428,0.2106 0.6894,0.5859 0.8867,0.9165 l 2.9262,-2.791 -9.701,-1.696 -0.6171,2.628 z"
|
||||
id="path114"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
|
||||
transform="matrix(0.16747,0.03823,-0.04067,0.17816,0,0)"
|
||||
id="g116"><path
|
||||
d="m 2265.6266,5520.8273 c 1.0955,0 2.6674,0.048 3.7193,0.017 4.4096,-0.1585 7.7859,-1.0659 11.8448,-1.6093 8.6865,-0.9822 15.8843,-1.809 22.5766,-1.3005 3.3536,0.4626 4.6684,2.287 6.2195,3.8092 l 12.9917,-18.4527 -57.2533,2.7683 -0.099,14.7682 z"
|
||||
id="path118"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:0.26840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
|
||||
d="m 180.4031,1049.4196 -2.473,1.1973 8.6032,18.2685 3.6802,-1.73 -9.8104,-17.7358 z"
|
||||
id="path120"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
|
||||
transform="matrix(0.15477,-0.07453,0.07929,0.16465,0,0)"
|
||||
id="g122"><path
|
||||
d="m -1704.3131,5602.1797 -15.9959,0.035 -1.0171,110.4913 23.6718,0.2081 -6.6588,-110.7343 z"
|
||||
id="path124"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:0.41159999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
|
||||
d="m 176.2469,1051.4203 2.4784,-1.1858 8.9141,18.1179 -3.6474,1.7978 -7.7451,-18.7299 z"
|
||||
id="path126"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
|
||||
transform="matrix(0.15477,-0.07453,0.07929,0.16465,0,0)"
|
||||
id="g128"><path
|
||||
d="m -1731.1657,5602.1774 15.9936,0.038 0.9918,110.4882 -23.669,0.2044 6.6836,-110.7309 z"
|
||||
id="path130"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:0.41159999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
|
||||
d="m 173.0911,1044.4779 c 0.5695,1.1824 1.8927,1.7255 2.9568,1.2132 1.0642,-0.5122 1.4653,-1.8858 0.8964,-3.0676 -0.5687,-1.1826 -1.8933,-1.7252 -2.9569,-1.2132 -1.0644,0.5122 -1.4655,1.8856 -0.8963,3.0676"
|
||||
id="path132"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
|
||||
transform="matrix(0.14819,-0.07134,0.07926,0.16466,0,0)"
|
||||
id="g134"><path
|
||||
d="m -1806.2371,5560.6799 c 3e-4,7.1804 5.821,12.9988 13.0023,13.0011 7.1781,0 12.9974,-5.8221 12.9957,-12.9997 0.01,-7.1801 -5.821,-12.9991 -12.9974,-12.9994 -7.1822,-5e-4 -13.0014,5.819 -13.0006,12.998 z"
|
||||
id="path136"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:0.2744;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
|
||||
d="m 176.9292,1042.5954 c 0.0817,0.1695 0.207,0.4085 0.2792,0.5749 0.3033,0.6934 0.4057,1.2883 0.6183,1.9596 0.4853,1.4227 0.8858,2.6025 1.468,3.5974 0.3266,0.4819 0.7249,0.542 1.0908,0.6614 l -2.0698,3.4733 -3.8107,-9.0808 2.4242,-1.1858 z"
|
||||
id="path138"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
|
||||
transform="matrix(0.07453,0.15477,-0.16465,0.07929,0,0)"
|
||||
id="g140"><path
|
||||
d="m 5915.209,1602.9547 c 1.0953,0 2.6649,-0.05 3.7202,-0.012 4.4056,0.1519 7.7811,1.0601 11.8409,1.605 8.6862,0.9844 15.8865,1.8127 22.5763,1.3028 3.3508,-0.4666 4.6717,-2.2873 6.222,-3.8084 l 12.9909,18.4516 -57.2547,-2.7709 -0.096,-14.7685 z"
|
||||
id="path142"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:0.26840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
|
||||
d="m 173.0784,1044.4501 c 0.0813,0.169 0.1902,0.4165 0.2744,0.5766 0.354,0.6698 0.7554,1.12 1.1471,1.7059 0.8099,1.266 1.482,2.3145 1.8975,3.3905 0.1735,0.5553 -0.0286,0.904 -0.1644,1.2646 l 4.0071,0.5473 -4.7237,-8.6411 -2.438,1.1562 z"
|
||||
id="path144"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
|
||||
transform="matrix(0.07453,0.15477,-0.16465,0.07929,0,0)"
|
||||
id="g146"><path
|
||||
d="m 5915.2107,1626.3439 c 1.0917,0 2.6638,0.052 3.7171,0.016 4.4073,-0.1553 7.7822,-1.0655 11.8432,-1.6064 8.6876,-0.9845 15.8853,-1.8102 22.5771,-1.3051 3.3499,0.4646 4.6689,2.2873 6.22,3.8141 l 12.9887,-18.4575 -57.2528,2.7737 -0.093,14.7653 z"
|
||||
id="path148"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:0.26840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
|
||||
d="m 205.2142,1051.4548 -2.4778,-1.1863 -8.9186,18.1171 3.6467,1.798 7.7497,-18.7288 z"
|
||||
id="path150"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
|
||||
transform="matrix(0.15477,0.07454,-0.0793,0.16465,0,0)"
|
||||
id="g152"><path
|
||||
d="m 3732.232,4696.5288 -15.992,0.035 -1.0145,110.4942 23.669,0.2069 -6.6625,-110.7357 z"
|
||||
id="path154"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:0.41159999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
|
||||
d="m 201.0589,1049.4533 2.4729,1.1988 -8.6082,18.2661 -3.6797,-1.7314 9.815,-17.7335 z"
|
||||
id="path156"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
|
||||
transform="matrix(0.15477,0.07454,-0.0793,0.16465,0,0)"
|
||||
id="g158"><path
|
||||
d="m 3705.3822,4696.5274 15.9982,0.038 0.9873,110.4936 -23.6687,0.201 6.6832,-110.7329 z"
|
||||
id="path160"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:0.41159999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
|
||||
d="m 204.5188,1042.6575 c -0.5697,1.1821 -0.1686,2.5563 0.8952,3.0685 1.0636,0.5122 2.3879,-0.03 2.9574,-1.2124 0.5689,-1.182 0.1684,-2.5554 -0.8952,-3.0682 -1.0644,-0.5125 -2.3876,0.031 -2.9574,1.2121"
|
||||
id="path162"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
|
||||
transform="matrix(0.14817,0.07138,-0.07932,0.16464,0,0)"
|
||||
id="g164"><path
|
||||
d="m 3871.7606,4654.3567 c -9e-4,7.1818 5.8221,13.0025 13.0017,13.0022 7.1767,0 12.9999,-5.8209 12.9991,-13.002 0,-7.1784 -5.8212,-12.9977 -13,-12.9996 -7.183,0 -12.9977,5.8215 -13.0008,12.9994 z"
|
||||
id="path166"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:0.2744;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
|
||||
d="m 208.3836,1044.4844 c -0.0811,0.1698 -0.1896,0.4167 -0.2747,0.5775 -0.354,0.6698 -0.7554,1.1199 -1.1472,1.7056 -0.8095,1.2665 -1.4819,2.3142 -1.8978,3.3908 -0.1732,0.5553 0.029,0.9037 0.1636,1.2639 l -4.0059,0.5477 4.7233,-8.6411 2.4387,1.1556 z"
|
||||
id="path168"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
|
||||
transform="matrix(0.07454,-0.15477,0.16465,0.0793,0,0)"
|
||||
id="g170"><path
|
||||
d="m -4951.7391,3507.378 c -1.097,5e-4 -2.6659,0.055 -3.7224,0.017 -4.4059,-0.1556 -7.7822,-1.065 -11.844,-1.6073 -8.6868,-0.9827 -15.8817,-1.8093 -22.5771,-1.3039 -3.35,0.4646 -4.665,2.2876 -6.2153,3.8089 l -12.992,-18.4507 57.2536,2.7686 0.097,14.7677 z"
|
||||
id="path172"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:0.26840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
|
||||
d="m 204.5333,1042.6297 c -0.0825,0.1693 -0.2072,0.4091 -0.2801,0.5746 -0.3024,0.6945 -0.405,1.2895 -0.6179,1.9602 -0.4859,1.423 -0.8861,2.6022 -1.4684,3.5983 -0.3265,0.4816 -0.7248,0.5408 -1.091,0.6599 l 2.0701,3.4738 3.8112,-9.0802 -2.4239,-1.1866 z"
|
||||
id="path174"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
|
||||
transform="matrix(0.07454,-0.15477,0.16465,0.0793,0,0)"
|
||||
id="g176"><path
|
||||
d="m -4951.7379,3483.9904 c -1.0953,0 -2.6683,-0.049 -3.7196,-0.014 -4.4074,0.1565 -7.7871,1.0633 -11.8438,1.6036 -8.687,0.987 -15.8847,1.8128 -22.5799,1.3073 -3.3508,-0.4663 -4.6681,-2.2904 -6.2186,-3.8114 l -12.989,18.4538 57.2508,-2.7706 0.1001,-14.7688 z"
|
||||
id="path178"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:0.26840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
|
||||
d="m 219.1056,1072.1339 -0.6173,-2.6771 -19.7252,4.3226 0.868,3.9727 19.4745,-5.6182 z"
|
||||
id="path180"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
|
||||
transform="matrix(0.03822,0.16747,-0.17816,0.04066,0,0)"
|
||||
id="g182"><path
|
||||
d="m 6368.633,136.4414 -15.9914,0.0349 -1.0165,110.4948 23.6699,0.206 -6.662,-110.7357 z"
|
||||
id="path184"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:0.41159999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
|
||||
d="m 218.0798,1067.6375 0.6046,2.6794 -19.6481,4.659 -0.9405,-3.9555 19.984,-3.3829 z"
|
||||
id="path186"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
|
||||
transform="matrix(0.03822,0.16747,-0.17816,0.04066,0,0)"
|
||||
id="g188"><path
|
||||
d="m 6341.7858,136.44 15.9894,0.0363 0.993,110.4933 -23.6673,0.2019 6.6849,-110.7315 z"
|
||||
id="path190"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:0.41159999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
|
||||
d="m 225.5496,1066.1054 c -1.279,0.2923 -2.103,1.4613 -1.8405,2.6127 0.2631,1.1517 1.5126,1.8479 2.7913,1.5562 1.2793,-0.2914 2.1036,-1.4609 1.8411,-2.6127 -0.2631,-1.1514 -1.5123,-1.8482 -2.7919,-1.5562"
|
||||
id="path192"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
|
||||
transform="matrix(0.03657,0.16035,-0.17817,0.04063,0,0)"
|
||||
id="g194"><path
|
||||
d="m 6624.6812,93.8699 c 0,7.1821 5.8189,12.9977 13.0011,12.9988 7.1796,-0.003 12.9974,-5.8215 12.9971,-12.9986 0,-7.1795 -5.8161,-13.0019 -12.9985,-13.0013 -7.1813,0.0022 -13.0012,5.8223 -12.9997,13.0011 z"
|
||||
id="path196"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:0.2744;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
|
||||
d="m 226.531,1070.2664 c -0.1826,0.042 -0.4439,0.1105 -0.6225,0.1443 -0.7435,0.1411 -1.3462,0.1085 -2.048,0.1669 -1.4947,0.1562 -2.7346,0.2844 -3.8345,0.6305 -0.5422,0.2103 -0.6891,0.5862 -0.8869,0.9164 l -2.9251,-2.7918 9.7005,-1.6937 0.6165,2.6274 z"
|
||||
id="path198"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
|
||||
transform="matrix(0.16748,-0.03822,0.04066,0.17817,0,0)"
|
||||
id="g200"><path
|
||||
d="m -100.5092,5985.5941 c -1.0916,-2e-4 -2.6645,0.05 -3.7196,0.012 -4.4028,-0.1522 -7.7808,-1.0598 -11.8414,-1.6029 -8.684,-0.9862 -15.8879,-1.8139 -22.5803,-1.3054 -3.3477,0.4654 -4.6655,2.2895 -6.2161,3.8103 l -12.9872,-18.4544 57.2485,2.7726 0.0961,14.7679 z"
|
||||
id="path202"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:0.26840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
|
||||
d="m 225.5803,1066.0992 c -0.1834,0.042 -0.4485,0.093 -0.624,0.14 -0.731,0.1953 -1.26,0.4859 -1.9176,0.7379 -1.4148,0.5074 -2.5869,0.93 -3.7281,1.0953 -0.5794,0.046 -0.8754,-0.2288 -1.1965,-0.4405 l -1.4247,3.7839 9.4748,-2.6821 -0.5839,-2.6345 z"
|
||||
id="path204"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
|
||||
transform="matrix(0.16748,-0.03822,0.04066,0.17817,0,0)"
|
||||
id="g206"><path
|
||||
d="m -100.5077,5962.2049 c -1.0954,6e-4 -2.6652,-0.052 -3.7225,-0.013 -4.4022,0.1536 -7.7803,1.0581 -11.8358,1.6027 -8.6899,0.9836 -15.8896,1.8116 -22.5808,1.3036 -3.3505,-0.4626 -4.6715,-2.2858 -6.22,-3.8066 l -12.9867,18.4527 57.2477,-2.7737 0.0981,-14.766 z"
|
||||
id="path208"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:0.26840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
|
||||
d="m 211.6429,1095.893 1.7084,-2.1515 -15.6779,-12.7267 -2.5639,3.1547 16.5334,11.7235 z"
|
||||
id="path210"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
|
||||
transform="matrix(0.10711,-0.1343,0.14287,0.11395,0,0)"
|
||||
id="g212"><path
|
||||
d="m -4219.3791,4644.5956 15.993,-0.032 1.012,-110.4928 -23.6625,-0.208 6.6575,110.7329 z"
|
||||
id="path214"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:0.41159999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
|
||||
d="m 214.52,1092.2873 -1.7192,2.1436 -15.8913,-12.4566 2.5053,-3.2014 15.1052,13.5144 z"
|
||||
id="path216"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
|
||||
transform="matrix(0.10711,-0.1343,0.14287,0.11395,0,0)"
|
||||
id="g218"><path
|
||||
d="m -4192.5257,4644.6018 -15.9959,-0.041 -0.9899,-110.4863 23.6639,-0.2055 -6.6781,110.7329 z"
|
||||
id="path220"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:0.41159999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
|
||||
d="m 220.3747,1097.1729 c -1.0256,-0.8181 -2.4542,-0.7331 -3.1901,0.1902 -0.7364,0.9229 -0.5017,2.3349 0.5233,3.1529 1.0261,0.8181 2.4542,0.7331 3.1909,-0.1899 0.7359,-0.923 0.5018,-2.3352 -0.5241,-3.1532"
|
||||
id="path222"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
|
||||
transform="matrix(0.10257,-0.12857,0.14286,0.11397,0,0)"
|
||||
id="g224"><path
|
||||
d="m -4379.2058,4686.834 c -3e-4,-7.1787 -5.8215,-12.9999 -13,-12.9971 -7.179,-6e-4 -13,5.8218 -13.0017,12.998 0,7.1813 5.8204,13.0011 13.0009,13.0005 7.1778,-5e-4 13.0022,-5.8192 13.0008,-13.0014 z"
|
||||
id="path226"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:0.2744;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
|
||||
d="m 217.7337,1100.5347 c -0.1471,-0.1173 -0.364,-0.2786 -0.5012,-0.3962 -0.5737,-0.4938 -0.9241,-0.9859 -1.4068,-1.4976 -1.0554,-1.0718 -1.9273,-1.9613 -2.8843,-2.6053 -0.5026,-0.2925 -0.8872,-0.1735 -1.2691,-0.1216 l 0.3586,-4.028 7.3724,6.5284 -1.6696,2.1203 z"
|
||||
id="path228"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
|
||||
transform="matrix(0.1343,0.10711,-0.11395,0.14287,0,0)"
|
||||
id="g230"><path
|
||||
d="m 4985.5978,3965.3263 c -1.097,8e-4 -2.6683,0.052 -3.7208,0.016 -4.4036,-0.1551 -7.7842,-1.0647 -11.8383,-1.6067 -8.6933,-0.9845 -15.8919,-1.8139 -22.5811,-1.3048 -3.3517,0.4632 -4.6689,2.2839 -6.2198,3.8098 l -12.9886,-18.4561 57.2496,2.7757 0.099,14.7665 z"
|
||||
id="path232"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:0.26840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
|
||||
d="m 220.3994,1097.193 c -0.1474,-0.1171 -0.3532,-0.2926 -0.4984,-0.4006 -0.6088,-0.4498 -1.1664,-0.6814 -1.7733,-1.0391 -1.2784,-0.7898 -2.34,-1.4429 -3.1811,-2.2323 -0.3968,-0.4247 -0.3662,-0.8266 -0.4008,-1.2101 l -3.8472,1.2452 8.0045,5.7362 1.6963,-2.0993 z"
|
||||
id="path234"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
|
||||
transform="matrix(0.1343,0.10711,-0.11395,0.14287,0,0)"
|
||||
id="g236"><path
|
||||
d="m 4985.5986,3941.9357 c -1.0956,0 -2.6665,-0.048 -3.7216,-0.013 -4.4042,0.1528 -7.7836,1.0656 -11.8429,1.6047 -8.6839,0.9848 -15.887,1.8114 -22.5788,1.3028 -3.3499,-0.4629 -4.6672,-2.2867 -6.2169,-3.8072 l -12.9903,18.4524 57.2507,-2.7706 0.1,-14.7687 z"
|
||||
id="path238"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:0.26840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
|
||||
d="m 187.367,1086.9849 c -0.0426,-1.0035 -0.868,-1.8049 -1.8822,-1.8049 -0.4156,0 -0.7988,0.1338 -1.1101,0.3606 l -0.489,-0.2324 1.0012,-2.0696 4.3855,2.1152 -0.9958,2.0693 -0.9096,-0.4382 z"
|
||||
id="path240"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 180.2086,1080.3039 c 0.7586,-0.6588 0.8697,-1.8043 0.2379,-2.5971 -0.2591,-0.3243 -0.603,-0.5409 -0.9746,-0.6429 l -0.1227,-0.5276 2.2419,-0.5076 1.0811,4.7477 -2.2393,0.5119 -0.2243,-0.9844 z"
|
||||
id="path242"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 180.9609,1070.5462 c 0.9874,0.182 1.9525,-0.4448 2.1782,-1.4335 0.0927,-0.4045 0.0476,-0.8087 -0.1052,-1.1628 l 0.3365,-0.4243 1.7952,1.4357 -3.039,3.805 -1.7958,-1.4315 0.6301,-0.7886 z"
|
||||
id="path244"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 189.059,1065.0538 c 0.4731,0.8861 1.5653,1.2495 2.4786,0.8095 0.3739,-0.1797 0.6613,-0.4665 0.8438,-0.8064 l 0.5415,0 -0.004,2.2986 -4.8699,0 0,-2.2975 1.0097,6e-4 z"
|
||||
id="path246"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 198.3934,1067.993 c -0.3971,0.9233 -8e-4,2.003 0.9131,2.4426 0.3739,0.1809 0.777,0.2262 1.1562,0.1562 l 0.3391,0.4227 -1.7989,1.4309 -3.0339,-3.8095 1.7954,-1.4326 0.629,0.7897 z"
|
||||
id="path248"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 201.9577,1077.1194 c -0.9688,0.2648 -1.5661,1.2481 -1.3408,2.2366 0.0922,0.4045 0.3079,0.7489 0.5987,1.0023 l -0.1182,0.5284 -2.2399,-0.5148 1.0862,-4.7472 2.2394,0.5103 -0.2254,0.9844 z"
|
||||
id="path250"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 197.0442,1085.5809 c -0.811,-0.5922 -1.9525,-0.4462 -2.5844,0.3469 -0.2588,0.324 -0.394,0.7073 -0.4104,1.0928 l -0.4868,0.2369 -0.9935,-2.0726 4.388,-2.1104 0.9975,2.0695 -0.9104,0.4369 z"
|
||||
id="path252"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 281.6892,1072.3652 c 0.7671,0.8949 1.5449,1.8431 2.3332,2.8442 0.7883,1.0015 1.5554,2.035 2.3012,3.1003 0.7458,1.0653 1.4595,2.0988 2.1413,3.1003 0.682,1.0014 1.2784,1.9496 1.7898,2.8445 l 9.4603,0 c -0.767,-0.9802 -1.6407,-2.0455 -2.6206,-3.196 -0.9802,-1.1506 -1.9922,-2.3228 -3.0365,-3.5159 -1.044,-1.1931 -2.088,-2.3436 -3.132,-3.4517 -1.044,-1.1078 -1.9922,-2.1305 -2.8445,-3.0682 1.0655,-1.0228 2.2161,-2.2479 3.4517,-3.6754 1.2359,-1.4276 2.4503,-2.9084 3.6437,-4.4425 1.1931,-1.5341 2.2903,-3.0682 3.2918,-4.6023 1.0015,-1.5341 1.8003,-2.8979 2.397,-4.091 l -9.0127,0 c -0.5114,1.0653 -1.1827,2.2692 -2.0138,3.6116 -0.8308,1.3423 -1.7149,2.6632 -2.6526,3.9632 -0.9375,1.2997 -1.8856,2.5353 -2.8443,3.7074 -0.959,1.1718 -1.8431,2.1413 -2.653,2.9083 l 0,-14.1905 -7.9262,0 0,43.0189 7.9262,1.3422 0,-26.2074 z"
|
||||
id="path254"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 329.694,1055.2343 c -1.4491,-0.3836 -3.2069,-0.7671 -5.2736,-1.1506 -2.0668,-0.3835 -4.3146,-0.5752 -6.7436,-0.5752 -2.4719,0 -4.4958,0.3408 -6.0727,1.0225 -1.5766,0.682 -2.823,1.6515 -3.7392,2.9086 -0.9161,1.2572 -1.5449,2.738 -1.8859,4.4425 -0.3407,1.7045 -0.5113,3.5796 -0.5113,5.625 l 0,16.7474 7.8624,0 0,-15.7246 c 0,-2.77 0.3302,-4.826 0.9907,-6.1685 0.6605,-1.3422 1.9497,-2.0134 3.8673,-2.0134 1.1931,0 2.3862,0.1066 3.5796,0.3194 l 0,23.5871 7.9263,0 0,-29.0202 z"
|
||||
id="path256"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 355.7095,1069.4248 c 0,2.8125 -0.4581,4.9646 -1.3743,6.4559 -0.9161,1.4916 -2.3119,2.2374 -4.1867,2.2374 -0.8524,0 -1.694,-0.1174 -2.5248,-0.3518 -0.8312,-0.2341 -1.5449,-0.5219 -2.1416,-0.8629 l 0,-16.2997 c 0.5967,-0.1279 1.2251,-0.2132 1.8859,-0.2557 0.6604,-0.043 1.2251,-0.064 1.6937,-0.064 2.0454,0 3.6649,0.6925 4.858,2.0775 1.1931,1.385 1.7898,3.7395 1.7898,7.0634 z m 7.9903,-0.1919 c 0,-2.3862 -0.3198,-4.5491 -0.959,-6.488 -0.6392,-1.9389 -1.5766,-3.59 -2.8125,-4.9538 -1.2359,-1.3637 -2.7485,-2.4182 -4.5383,-3.164 -1.7898,-0.7458 -3.8353,-1.1186 -6.1364,-1.1186 -2.0455,0 -4.1338,0.1491 -6.2643,0.4473 -2.1308,0.2982 -3.9206,0.6818 -5.3694,1.1506 l 0,42.124 7.8622,1.3422 0,-15.0214 c 1.108,0.5114 2.1733,0.8629 3.196,1.0548 1.0228,0.1916 2.0455,0.2877 3.0683,0.2877 1.9604,0 3.6861,-0.373 5.1777,-1.1188 1.4913,-0.7458 2.738,-1.8111 3.7392,-3.1961 1.0015,-1.3847 1.7581,-3.0362 2.2694,-4.9538 0.5114,-1.9176 0.7671,-4.0481 0.7671,-6.3921"
|
||||
id="path258"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 367.5986,1068.9772 c 0,2.6422 0.405,4.9646 1.2146,6.9676 0.8096,2.0026 1.8751,3.6754 3.1961,5.0176 1.3209,1.3425 2.8338,2.3544 4.5382,3.0364 1.7048,0.6818 3.4518,1.0228 5.2416,1.0228 4.4319,0 7.7984,-1.3105 10.0995,-3.9314 2.3012,-2.6206 3.4518,-6.4451 3.4518,-11.4738 0,-0.5114 -0.0108,-1.044 -0.0321,-1.5979 -0.0212,-0.5542 -0.0532,-1.0015 -0.0958,-1.3425 l -19.4958,0 c 0,-1.9601 0.8096,-3.505 2.429,-4.634 1.6192,-1.1294 3.7074,-1.694 6.2643,-1.694 1.5766,0 3.0787,0.1703 4.5062,0.5113 1.4278,0.341 2.6314,0.6818 3.6116,1.0228 l 1.0868,-6.7119 c -1.3637,-0.4686 -2.8125,-0.8629 -4.3466,-1.1823 -1.5341,-0.3198 -3.2601,-0.4794 -5.1778,-0.4794 -2.5568,0 -4.8472,0.3303 -6.8714,0.9907 -2.0242,0.6605 -3.75,1.6407 -5.1775,2.9404 -1.4275,1.2997 -2.5251,2.9084 -3.2922,4.826 -0.767,1.9176 -1.1505,4.1547 -1.1505,6.7116 z m 20.1353,3.1323 c 0,0.8096 -0.1066,1.5874 -0.3198,2.3329 -0.2131,0.7458 -0.5538,1.417 -1.0227,2.0137 -0.4686,0.5964 -1.0653,1.0761 -1.7898,1.438 -0.7242,0.3623 -1.6191,0.5434 -2.6847,0.5434 -1.0227,0 -1.9069,-0.1703 -2.6526,-0.5113 -0.7458,-0.341 -1.3638,-0.8096 -1.8536,-1.4063 -0.4901,-0.5967 -0.8737,-1.2784 -1.1506,-2.0455 -0.2772,-0.767 -0.4581,-1.5553 -0.5434,-2.3649 l 12.0172,0 z"
|
||||
id="path260"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 425.5109,1076.2642 c -0.5113,0.1279 -1.1185,0.2557 -1.8218,0.3835 -0.703,0.1279 -1.4168,0.2452 -2.1413,0.3515 -0.7245,0.1066 -1.4275,0.1812 -2.1092,0.224 -0.6821,0.043 -1.2572,0.064 -1.7261,0.064 -1.1077,0 -2.1945,-0.053 -3.2598,-0.1599 -1.0656,-0.1063 -2.1521,-0.309 -3.2601,-0.6072 l 0,-22.3084 -7.9263,0 0,28.0613 c 2.0883,0.7671 4.2296,1.385 6.4242,1.8539 2.1946,0.4685 4.7622,0.703 7.7026,0.703 0.426,0 1.0332,-0.021 1.8215,-0.064 0.7886,-0.043 1.6302,-0.1174 2.5251,-0.2239 0.8949,-0.1066 1.8003,-0.2345 2.7165,-0.3836 0.9161,-0.1491 1.7365,-0.3515 2.461,-0.6072 l -1.4063,-7.287 z"
|
||||
id="path262"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 433.5647,1083.2956 c 1.4063,0.3835 3.1428,0.7458 5.2098,1.0868 2.0668,0.3407 4.3146,0.5113 6.7436,0.5113 2.3862,0 4.3679,-0.3305 5.9446,-0.991 1.5766,-0.6604 2.8233,-1.5978 3.7394,-2.8125 0.9162,-1.2144 1.5662,-2.6631 1.9497,-4.3466 0.3835,-1.6833 0.5752,-3.5476 0.5752,-5.5931 l 0,-16.939 -7.8622,0 0,15.9163 c 0,2.8125 -0.3198,4.8367 -0.959,6.0726 -0.6392,1.2357 -1.9389,1.8536 -3.899,1.8536 -0.5967,0 -1.1826,-0.021 -1.7578,-0.064 -0.5754,-0.043 -1.1826,-0.1065 -1.8218,-0.1919 l 0,-23.5868 -7.8625,0 0,29.0841 z"
|
||||
id="path264"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 463.4796,1068.9772 c 0,2.6422 0.4051,4.9646 1.2146,6.9676 0.8096,2.0026 1.8752,3.6754 3.1961,5.0176 1.3209,1.3425 2.8338,2.3544 4.5383,3.0364 1.7047,0.6818 3.4517,1.0228 5.2415,1.0228 4.432,0 7.7984,-1.3105 10.0996,-3.9314 2.3011,-2.6206 3.4517,-6.4451 3.4517,-11.4738 0,-0.5114 -0.0108,-1.044 -0.032,-1.5979 -0.0213,-0.5542 -0.0533,-1.0015 -0.0958,-1.3425 l -19.4959,0 c 0,-1.9601 0.8096,-3.505 2.429,-4.634 1.6192,-1.1294 3.7075,-1.694 6.2643,-1.694 1.5767,0 3.0787,0.1703 4.5063,0.5113 1.4278,0.341 2.6314,0.6818 3.6116,1.0228 l 1.0868,-6.7119 c -1.3638,-0.4686 -2.8126,-0.8629 -4.3467,-1.1823 -1.5341,-0.3198 -3.2601,-0.4794 -5.1777,-0.4794 -2.5569,0 -4.8473,0.3303 -6.8715,0.9907 -2.0242,0.6605 -3.7499,1.6407 -5.1775,2.9404 -1.4275,1.2997 -2.5251,2.9084 -3.2921,4.826 -0.7671,1.9176 -1.1506,4.1547 -1.1506,6.7116 z m 20.1353,3.1323 c 0,0.8096 -0.1066,1.5874 -0.3197,2.3329 -0.2132,0.7458 -0.5539,1.417 -1.0228,2.0137 -0.4685,0.5964 -1.0652,1.0761 -1.7898,1.438 -0.7242,0.3623 -1.6191,0.5434 -2.6846,0.5434 -1.0228,0 -1.9069,-0.1703 -2.6527,-0.5113 -0.7458,-0.341 -1.3638,-0.8096 -1.8536,-1.4063 -0.4901,-0.5967 -0.8736,-1.2784 -1.1506,-2.0455 -0.2772,-0.767 -0.458,-1.5553 -0.5434,-2.3649 l 12.0172,0 z"
|
||||
id="path266"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 502.5991,1077.7346 -6.8394,0 0,6.5199 6.8394,0 0,7.5424 7.8625,1.2785 0,-8.8209 12.5923,0 0,-6.5199 -12.5923,0 0,-12.1451 c 0,-1.1081 0.1066,-2.0029 0.3194,-2.6847 0.2132,-0.6817 0.5114,-1.2146 0.8949,-1.5982 0.3836,-0.3835 0.8524,-0.6392 1.4063,-0.767 0.5542,-0.1279 1.1718,-0.1916 1.8539,-0.1916 0.7242,0 1.3955,0.021 2.0134,0.064 0.618,0.043 1.2144,0.1174 1.7898,0.224 0.5752,0.1063 1.1718,0.2662 1.7898,0.4793 0.618,0.2132 1.2889,0.4901 2.0135,0.8309 l 1.0868,-6.7757 c -1.4491,-0.5964 -3.0153,-1.0225 -4.6985,-1.2781 -1.6832,-0.2557 -3.3131,-0.3836 -4.8897,-0.3836 -1.8326,0 -3.4518,0.1491 -4.8581,0.4473 -1.4062,0.2982 -2.5996,0.8737 -3.5795,1.7258 -0.9803,0.8524 -1.7261,2.0562 -2.2374,3.6116 -0.5114,1.5554 -0.7671,3.5904 -0.7671,6.1044 l 0,12.337 z"
|
||||
id="path268"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 527.4003,1068.9772 c 0,2.6422 0.4051,4.9646 1.2146,6.9676 0.8096,2.0026 1.8751,3.6754 3.1961,5.0176 1.3209,1.3425 2.8338,2.3544 4.5383,3.0364 1.7047,0.6818 3.4517,1.0228 5.2415,1.0228 4.432,0 7.7984,-1.3105 10.0996,-3.9314 2.3011,-2.6206 3.4517,-6.4451 3.4517,-11.4738 0,-0.5114 -0.0108,-1.044 -0.032,-1.5979 -0.0213,-0.5542 -0.0533,-1.0015 -0.0958,-1.3425 l -19.4959,0 c 0,-1.9601 0.8096,-3.505 2.429,-4.634 1.6192,-1.1294 3.7075,-1.694 6.2643,-1.694 1.5766,0 3.0787,0.1703 4.5063,0.5113 1.4278,0.341 2.6314,0.6818 3.6116,1.0228 l 1.0868,-6.7119 c -1.3638,-0.4686 -2.8126,-0.8629 -4.3467,-1.1823 -1.5341,-0.3198 -3.2601,-0.4794 -5.1777,-0.4794 -2.5569,0 -4.8473,0.3303 -6.8715,0.9907 -2.0242,0.6605 -3.7499,1.6407 -5.1775,2.9404 -1.4275,1.2997 -2.5251,2.9084 -3.2921,4.826 -0.7671,1.9176 -1.1506,4.1547 -1.1506,6.7116 z m 20.1353,3.1323 c 0,0.8096 -0.1066,1.5874 -0.3197,2.3329 -0.2132,0.7458 -0.5539,1.417 -1.0228,2.0137 -0.4685,0.5964 -1.0652,1.0761 -1.7898,1.438 -0.7242,0.3623 -1.6191,0.5434 -2.6847,0.5434 -1.0227,0 -1.9068,-0.1703 -2.6526,-0.5113 -0.7458,-0.341 -1.3638,-0.8096 -1.8536,-1.4063 -0.4901,-0.5967 -0.8736,-1.2784 -1.1506,-2.0455 -0.2772,-0.767 -0.4581,-1.5553 -0.5434,-2.3649 l 12.0172,0 z"
|
||||
id="path270"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
|
||||
d="m 571.9533,1060.1561 c 1.9176,0 3.3876,0.1386 4.4104,0.4155 1.0227,0.277 1.5341,0.8416 1.5341,1.694 0,0.5539 -0.1599,1.0227 -0.4793,1.4063 -0.3198,0.3835 -0.7671,0.7137 -1.3425,0.9907 -0.5752,0.2769 -1.2357,0.5434 -1.9814,0.7991 -0.7458,0.2557 -1.5234,0.5113 -2.3332,0.767 -1.1506,0.3407 -2.3225,0.735 -3.5156,1.1826 -1.1931,0.4473 -2.2799,1.012 -3.2601,1.6937 -0.9799,0.6821 -1.7898,1.5449 -2.429,2.5889 -0.6392,1.044 -0.9587,2.3545 -0.9587,3.9311 0,1.2784 0.2449,2.4823 0.735,3.6116 0.4901,1.1294 1.2572,2.1305 2.3012,3.0042 1.044,0.8736 2.3757,1.5554 3.9952,2.0455 1.6191,0.4901 3.5583,0.7353 5.8166,0.7353 1.9605,0 3.782,-0.1494 5.4652,-0.4476 1.6835,-0.2982 3.1428,-0.7246 4.3787,-1.2784 l -1.2143,-6.6479 c -0.7246,0.2132 -1.8539,0.5647 -3.388,1.0548 -1.5341,0.4901 -3.2599,0.735 -5.1775,0.735 -2.003,0 -3.3559,-0.2449 -4.0589,-0.735 -0.7033,-0.4901 -1.0548,-1.0122 -1.0548,-1.5661 0,-0.4689 0.1599,-0.8841 0.4793,-1.2464 0.3198,-0.3623 0.7458,-0.6925 1.2785,-0.9907 0.5326,-0.2985 1.1506,-0.5859 1.8538,-0.8632 0.703,-0.2769 1.4593,-0.5431 2.2692,-0.7988 1.1505,-0.3835 2.3437,-0.8098 3.5796,-1.2784 1.2356,-0.4689 2.3544,-1.0548 3.3556,-1.7578 1.0015,-0.7032 1.8218,-1.5981 2.461,-2.6847 0.6393,-1.0868 0.959,-2.4185 0.959,-3.9951 0,-1.2359 -0.2344,-2.4078 -0.7033,-3.5158 -0.4688,-1.1078 -1.2464,-2.0668 -2.3332,-2.8763 -1.0865,-0.8096 -2.5035,-1.4488 -4.2505,-1.9177 -1.7473,-0.4688 -3.8994,-0.703 -6.4562,-0.703 -2.5994,0 -4.8152,0.2982 -6.6478,0.8946 -1.8323,0.5967 -3.3024,1.1294 -4.4104,1.5982 l 1.2143,6.5838 c 1.4916,-0.5964 3.079,-1.1506 4.7622,-1.662 1.6832,-0.5114 3.3985,-0.767 5.1458,-0.767"
|
||||
id="path272"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /></g></svg>
|
Before Width: | Height: | Size: 41 KiB |
3
www/master/.bowerrc
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"directory": "../../third_party/ui/bower_components"
|
||||
}
|
9
www/master/README.md
Normal file
@ -0,0 +1,9 @@
|
||||
### Running the App in Production
|
||||
|
||||
master/ directory
|
||||
|
||||
This directory contains the source files. You will find the following directorys inside
|
||||
|
||||
jade/ This directory contains JADE files. This files need to be compiled into html files to be displayed by a browser
|
||||
less/ This directory contains the LESS files for the core styles and material styles.
|
||||
js/ Here you will find pure JS files. All this files are concatenated into the file app.js.
|
30
www/master/bower.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "kubernetes-ui",
|
||||
"description": "UI for Kubernetes",
|
||||
"version": "0.0.0",
|
||||
"homepage": "https://github.com/kubernetes-ui/kubernetes-ui",
|
||||
"license": "Apache",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"jquery": "~2.1.3",
|
||||
"angular": "1.3.x",
|
||||
"angular-route": "1.3.x",
|
||||
"angular-css": "1.0.x",
|
||||
"angular-mocks": "1.3.x",
|
||||
"angular-animate": "1.3.x",
|
||||
"angular-aria": "1.3.x",
|
||||
"angular-material": "0.8.1",
|
||||
"angularjs-jasmine-matchers": "0.2",
|
||||
"hammerjs": "~2.0.2",
|
||||
"angular-cookies": "1.3.x",
|
||||
"modernizr": "2.8.3",
|
||||
"ng-lodash": "~0.2.0",
|
||||
"string-format-js": "~0.1.2",
|
||||
"sprintf": "~1.0.2",
|
||||
"jsonpath": "#3f6e79e",
|
||||
"d3": "~3.5.5",
|
||||
"d3-context-menu": "",
|
||||
"angular-json-human": "~1.2.1",
|
||||
"angular-filter": "~0.5.1"
|
||||
}
|
||||
}
|
125
www/master/components/README.md
Normal file
@ -0,0 +1,125 @@
|
||||
Components
|
||||
==========
|
||||
|
||||
A tab in the Kubernetes UI with its set of visualizations is referred to as a *component*. Components are separated from the UI chrome and base data providers to simplify the development of new visualizations. This document provides reference for creation and modification of components.
|
||||
|
||||
Each component has its own directory, which contains a manifest file, HTML views, Angular providers, CSS, Less and other assets. Below is the recommended directory structure for a component.
|
||||
```
|
||||
foo_component
|
||||
├── config
|
||||
├── css
|
||||
├── img
|
||||
├── js
|
||||
│ └── modules
|
||||
│ ├── controllers
|
||||
│ ├── directives
|
||||
│ └── services
|
||||
├── less
|
||||
├── pages
|
||||
├── views
|
||||
│ └── partials
|
||||
└── manifest.json
|
||||
```
|
||||
|
||||
###Manifest file
|
||||
|
||||
The JSON-formatted manifest file, named ```manifest.json```, is located at the root of a component. Based on the component directory name and the contents of the manifest, the Kubernetes UI automatically adds a tab to the chrome, a dependency on the component's AngularJS module to main AngularJS app and Angular routes for the component.
|
||||
|
||||
For example, consider a manifest file at ```master/components/foo_component/manifest.json```:
|
||||
```
|
||||
{
|
||||
"routes": [
|
||||
{
|
||||
"url": "/",
|
||||
"templateUrl": "/components/foo_component/pages/home.html"
|
||||
},
|
||||
{
|
||||
"url": "/kittens",
|
||||
"templateUrl": "/components/foo_component/pages/kittens.html",
|
||||
"css": "/components/foo_component/css/kittens.css"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
From the name of the component directory, the Kubernetes UI
|
||||
* creates a tab called "Foo Component",
|
||||
* adds Angular module ```kubernetesApp.components.fooComponent``` to the dependencies of ```kubernetesApp```, and
|
||||
* defines Angular routes ```/foo_component/``` and ```/foo_component/kittens```.
|
||||
|
||||
Every tab links to ```/``` relative to its component, so it is important to always define a ```/``` route.
|
||||
|
||||
###Source files
|
||||
In general, all files located in ```master/components/<component>``` are copied to ```app/components/<component>/``` on each gulp build. This includes (but is not limited to) HTML views, CSS and images. Exceptions to this copy are the ```config``` and ```less``` directories as well as all ```.js``` files.
|
||||
|
||||
The sections below describe how the exceptions are built into the UI.
|
||||
|
||||
####JavaScript
|
||||
All JavaScript files located in the ```master/components/<component>/js``` are uglified and concatenated together with the rest of the UI's JavaScript. Once aggregated, the JavaScript file is minified and written to ```app/assets/js/app.js```.
|
||||
|
||||
####Configuration
|
||||
|
||||
Similar to the [UI-wide configuration](../../README.md#configuration), components can define different configuration for each environment. The gulp task creates the constant ```ENV``` under the ```kubernetesApp.config``` module for configuration, which is an object with a property for the root UI and each component.
|
||||
|
||||
For example, a configuration for the ```development``` environment specific to ```foo_component``` would be located at ```master/components/foo_component/config/development.json```. Such a component would access its ```development.json``` configuration verbatim at ```ENV.foo_component```:
|
||||
```
|
||||
angular.module('kubernetesApp.components.fooComponent', ['kubernetesApp.config'])
|
||||
.provider('foo', ...)
|
||||
.config(['fooProvider', 'ENV', function(fooProvider, ENV) {
|
||||
// Configure fooProvider using ENV['foo_component'].
|
||||
});
|
||||
```
|
||||
|
||||
####Less
|
||||
|
||||
Like JavaScript, the component's Less files are built into one monolithic CSS file. All top-level Less files located at ```master/components/<component>/less/*.less``` are imported into the main UI's Less file. The result is then minified and copied to ```app/assets/css/app.css```.
|
||||
|
||||
Sub-directories of this path are watched for changes, but not directly imported. This is useful for defining common colors, mixins and other functions or variables used by the top-level Less files.
|
||||
|
||||
###Appendix
|
||||
|
||||
####Manifest schema
|
||||
|
||||
```
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "Very brief summary of the component. Use a README.md file for detailed descriptions."
|
||||
},
|
||||
"routes": {
|
||||
"type": "array",
|
||||
"description": "Angular routes for the component.",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "Short description of the route."
|
||||
},
|
||||
"url": {
|
||||
"type": "string",
|
||||
"description": "Route location relative to '/<component>'."
|
||||
},
|
||||
"templateUrl": {
|
||||
"type": "string",
|
||||
"description": "Absolute location of the HTML template."
|
||||
},
|
||||
"css": {
|
||||
"type": "string",
|
||||
"description": "Absolute location of CSS to use with this route."
|
||||
}
|
||||
},
|
||||
"required": ["url", "templateUrl"]
|
||||
},
|
||||
"minItems": 1
|
||||
}
|
||||
},
|
||||
"required": ["routes"]
|
||||
}
|
||||
```
|
||||
|
||||
Content available under the [CC-By 3.0
|
||||
license](http://creativecommons.org/licenses/by/3.0/)
|
0
www/master/components/dashboard/.gitkeep
Normal file
1
www/master/components/dashboard/README.md
Normal file
@ -0,0 +1 @@
|
||||
Dashboard Component for Kubernetes WebUI
|
@ -0,0 +1 @@
|
||||
{}
|
0
www/master/components/dashboard/img/.gitkeep
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18"><path d="M5 8l4 4 4-4z"/></svg>
|
After Width: | Height: | Size: 114 B |
@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<path d="M7 10l5 5 5-5z"/>
|
||||
<path d="M0 0h24v24h-24z" fill="none"/>
|
||||
</svg>
|
After Width: | Height: | Size: 166 B |
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18"><path d="M14.53 4.53l-1.06-1.06L9 7.94 4.53 3.47 3.47 4.53 7.94 9l-4.47 4.47 1.06 1.06L9 10.06l4.47 4.47 1.06-1.06L10.06 9z"/></svg>
|
After Width: | Height: | Size: 215 B |
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
|
After Width: | Height: | Size: 202 B |
0
www/master/components/dashboard/js/.gitkeep
Normal file
0
www/master/components/dashboard/js/modules/.gitkeep
Normal file
@ -0,0 +1,188 @@
|
||||
|
||||
|
||||
app.controller('cAdvisorController', [
|
||||
'$scope',
|
||||
'$routeParams',
|
||||
'k8sApi',
|
||||
'lodash',
|
||||
'cAdvisorService',
|
||||
'$q',
|
||||
'$interval',
|
||||
function($scope, $routeParams, k8sApi, lodash, cAdvisorService, $q, $interval) {
|
||||
$scope.k8sApi = k8sApi;
|
||||
|
||||
$scope.activeMinionDataById = {};
|
||||
$scope.maxDataByById = {};
|
||||
|
||||
$scope.getData = function() {
|
||||
$scope.loading = true;
|
||||
|
||||
k8sApi.getMinions().success(angular.bind(this, function(res) {
|
||||
$scope.minions = res;
|
||||
// console.log(res);
|
||||
var promises = lodash.map(res.items, function(m) { return cAdvisorService.getDataForMinion(m.id); });
|
||||
|
||||
$q.all(promises).then(
|
||||
function(dataArray) {
|
||||
lodash.each(dataArray, function(data, i) {
|
||||
var m = res.items[i];
|
||||
|
||||
var maxData = maxMemCpuInfo(m.id, data.memoryData, data.cpuData, data.filesystemData);
|
||||
|
||||
// console.log("maxData", maxData);
|
||||
|
||||
$scope.activeMinionDataById[m.id] =
|
||||
transformMemCpuInfo(data.memoryData, data.cpuData, data.filesystemData, maxData, m.hostIP)
|
||||
});
|
||||
|
||||
},
|
||||
function(errorData) {
|
||||
// console.log("Error: " + errorData);
|
||||
$scope.loading = false;
|
||||
});
|
||||
|
||||
$scope.loading = false;
|
||||
})).error(angular.bind(this, this.handleError));
|
||||
};
|
||||
|
||||
function getcAdvisorDataForMinion(m) {
|
||||
var p = cAdvisorService.getDataForMinion(m.hostIP);
|
||||
return p;
|
||||
}
|
||||
|
||||
function handleError(data, status, headers, config) {
|
||||
// console.log("Error (" + status + "): " + data);
|
||||
$scope.loading = false;
|
||||
};
|
||||
|
||||
// d3
|
||||
function getColorForIndex(i, percentage) {
|
||||
// var colors = ['red', 'blue', 'yellow', 'pink', 'purple', 'green', 'orange'];
|
||||
// return colors[i];
|
||||
var c = "color-" + (i + 1);
|
||||
if (percentage && percentage >= 90)
|
||||
c = c + ' color-critical';
|
||||
else if (percentage && percentage >= 80)
|
||||
c = c + ' color-warning';
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
function getMaxColorForIndex(i, percentage) {
|
||||
// var colors = ['red', 'blue', 'yellow', 'pink', 'purple', 'green', 'orange'];
|
||||
// return colors[i];
|
||||
var c = "color-max-" + (i + 1);
|
||||
if (percentage && percentage >= 90)
|
||||
c = c + ' color-max-critical';
|
||||
else if (percentage && percentage >= 80)
|
||||
c = c + ' color-max-warning';
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
function maxMemCpuInfo(mId, mem, cpu, fsDataArray) {
|
||||
if ($scope.maxDataByById[mId] === undefined) $scope.maxDataByById[mId] = {};
|
||||
|
||||
var currentMem = mem.current;
|
||||
var currentCpu = cpu;
|
||||
|
||||
var items = [];
|
||||
|
||||
if ($scope.maxDataByById[mId]['cpu'] === undefined ||
|
||||
$scope.maxDataByById[mId]['cpu'] < currentCpu.cpuPercentUsage) {
|
||||
// console.log("New max cpu " + mId, $scope.maxDataByById[mId].cpu, currentCpu.cpuPercentUsage);
|
||||
$scope.maxDataByById[mId]['cpu'] = currentCpu.cpuPercentUsage;
|
||||
}
|
||||
items.push({
|
||||
maxValue: $scope.maxDataByById[mId]['cpu'],
|
||||
maxTickClassNames: getColorForIndex(0, $scope.maxDataByById[mId]['cpu']),
|
||||
maxClassNames: getMaxColorForIndex(0, $scope.maxDataByById[mId]['cpu'])
|
||||
});
|
||||
|
||||
var memPercentage = Math.floor((currentMem.memoryUsage * 100.0) / currentMem.memoryLimit);
|
||||
if ($scope.maxDataByById[mId]['mem'] === undefined || $scope.maxDataByById[mId]['mem'] < memPercentage)
|
||||
$scope.maxDataByById[mId]['mem'] = memPercentage;
|
||||
items.push({
|
||||
maxValue: $scope.maxDataByById[mId]['mem'],
|
||||
maxTickClassNames: getColorForIndex(1, $scope.maxDataByById[mId]['mem']),
|
||||
maxClassNames: getMaxColorForIndex(1, $scope.maxDataByById[mId]['mem'])
|
||||
});
|
||||
|
||||
for (var i = 0; i < fsDataArray.length; i++) {
|
||||
var f = fsDataArray[i];
|
||||
var fid = 'FS #' + f.filesystemNumber;
|
||||
if ($scope.maxDataByById[mId][fid] === undefined || $scope.maxDataByById[mId][fid] < f.totalUsage)
|
||||
$scope.maxDataByById[mId][fid] = f.totalUsage;
|
||||
items.push({
|
||||
maxValue: $scope.maxDataByById[mId][fid],
|
||||
maxTickClassNames: getColorForIndex(2 + i, $scope.maxDataByById[mId][fid]),
|
||||
maxClassNames: getMaxColorForIndex(2 + i, $scope.maxDataByById[mId][fid])
|
||||
});
|
||||
}
|
||||
|
||||
// console.log("Max Data is now " + mId, $scope.maxDataByById[mId]);
|
||||
return items;
|
||||
}
|
||||
|
||||
function transformMemCpuInfo(mem, cpu, fsDataArray, maxData, hostName) {
|
||||
var currentMem = mem.current;
|
||||
var currentCpu = cpu;
|
||||
|
||||
var items = [];
|
||||
|
||||
items.push({
|
||||
label: 'CPU',
|
||||
stats: currentCpu.cpuPercentUsage + '%',
|
||||
value: currentCpu.cpuPercentUsage,
|
||||
classNames: getColorForIndex(0, currentCpu.cpuPercentUsage),
|
||||
maxData: maxData[0],
|
||||
hostName: hostName
|
||||
});
|
||||
|
||||
var memPercentage = Math.floor((currentMem.memoryUsage * 100.0) / currentMem.memoryLimit);
|
||||
items.push({
|
||||
label: 'Memory',
|
||||
stats: currentMem.memoryUsageDescription + ' / ' + currentMem.memoryLimitDescription,
|
||||
value: memPercentage,
|
||||
classNames: getColorForIndex(1, memPercentage),
|
||||
maxData: maxData[1],
|
||||
hostName: hostName
|
||||
});
|
||||
|
||||
for (var i = 0; i < fsDataArray.length; i++) {
|
||||
var f = fsDataArray[i];
|
||||
|
||||
items.push({
|
||||
label: 'FS #' + f.filesystemNumber,
|
||||
stats: f.usageDescription + ' / ' + f.capacityDescription,
|
||||
value: f.totalUsage,
|
||||
classNames: getColorForIndex(2 + i, f.totalUsage),
|
||||
maxData: maxData[2 + i],
|
||||
hostName: hostName
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
var a = [];
|
||||
var segments = {
|
||||
segments: items
|
||||
};
|
||||
a.push(segments);
|
||||
return a;
|
||||
};
|
||||
|
||||
// end d3
|
||||
var promise = $interval($scope.getData, 3000);
|
||||
|
||||
// Cancel interval on page changes
|
||||
$scope.$on('$destroy', function() {
|
||||
if (angular.isDefined(promise)) {
|
||||
$interval.cancel(promise);
|
||||
promise = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
$scope.getData();
|
||||
|
||||
}
|
||||
]);
|
@ -0,0 +1,6 @@
|
||||
/**=========================================================
|
||||
* Module: Dashboard
|
||||
* Visualizer for clusters
|
||||
=========================================================*/
|
||||
|
||||
app.controller('DashboardCtrl', ['$scope', function($scope) {}]);
|
@ -0,0 +1,223 @@
|
||||
/**=========================================================
|
||||
* Module: Group
|
||||
* Visualizer for groups
|
||||
=========================================================*/
|
||||
|
||||
app.controller('GroupCtrl', [
|
||||
'$scope',
|
||||
'$route',
|
||||
'$interval',
|
||||
'$routeParams',
|
||||
'k8sApi',
|
||||
'$rootScope',
|
||||
'$location',
|
||||
'lodash',
|
||||
function($scope, $route, $interval, $routeParams, k8sApi, $rootScope, $location, _) {
|
||||
'use strict';
|
||||
$scope.doTheBack = function() { window.history.back(); };
|
||||
|
||||
$scope.capitalize = function(s) { return _.capitalize(s); };
|
||||
|
||||
$rootScope.doTheBack = $scope.doTheBack;
|
||||
|
||||
$scope.resetGroupLayout = function(group) { delete group.settings; };
|
||||
|
||||
$scope.handlePath = function(path) {
|
||||
var parts = path.split("/");
|
||||
// split leaves an empty string at the beginning.
|
||||
parts = parts.slice(1);
|
||||
|
||||
if (parts.length === 0) {
|
||||
return;
|
||||
}
|
||||
this.handleGroups(parts.slice(1));
|
||||
};
|
||||
|
||||
$scope.getState = function(obj) { return Object.keys(obj)[0]; };
|
||||
|
||||
$scope.clearSelector = function(grouping) { $location.path("/dashboard/groups/" + grouping + "/selector/"); };
|
||||
|
||||
$scope.changeGroupBy = function() {
|
||||
var grouping = $scope.selectedGroupBy;
|
||||
|
||||
var s = _.clone($location.search());
|
||||
if ($scope.routeParams.grouping != grouping)
|
||||
$location.path("/dashboard/groups/" + grouping + "/selector/").search(s);
|
||||
};
|
||||
|
||||
$scope.createBarrier = function(count, callback) {
|
||||
var barrier = count;
|
||||
var barrierFunction = angular.bind(this, function(data) {
|
||||
// JavaScript is single threaded so this is safe.
|
||||
barrier--;
|
||||
if (barrier === 0) {
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
});
|
||||
return barrierFunction;
|
||||
};
|
||||
|
||||
$scope.handleGroups = function(parts, selector) {
|
||||
$scope.groupBy = parts;
|
||||
$scope.loading = true;
|
||||
$scope.selector = selector;
|
||||
var args = [];
|
||||
var type = "";
|
||||
if (selector && selector.length > 0) {
|
||||
$scope.selectorPieces = selector.split(",");
|
||||
var labels = [];
|
||||
var fields = [];
|
||||
for (var i = 0; i < $scope.selectorPieces.length; i++) {
|
||||
var piece = $scope.selectorPieces[i];
|
||||
if (piece[0] == '$') {
|
||||
fields.push(piece.slice(2));
|
||||
} else {
|
||||
if (piece.indexOf("type=") === 0) {
|
||||
var labelParts = piece.split("=");
|
||||
if (labelParts.length > 1) {
|
||||
type = labelParts[1];
|
||||
}
|
||||
} else {
|
||||
labels.push(piece);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (labels.length > 0) {
|
||||
args.push("labels=" + encodeURI(labels.join(",")));
|
||||
}
|
||||
if (fields.length > 0) {
|
||||
args.push("fields=" + encodeURI(fields.join(",")));
|
||||
}
|
||||
}
|
||||
var query = "?" + args.join("&");
|
||||
var list = [];
|
||||
var count = type.length > 0 ? 1 : 3;
|
||||
var barrier = $scope.createBarrier(count, function() {
|
||||
$scope.groups = $scope.groupData(list, 0);
|
||||
$scope.loading = false;
|
||||
$scope.groupByOptions = buildGroupByOptions();
|
||||
$scope.selectedGroupBy = $routeParams.grouping;
|
||||
});
|
||||
|
||||
if (type === "" || type == "pod") {
|
||||
k8sApi.getPods(query).success(function(data) {
|
||||
$scope.addLabel("type", "pod", data.items);
|
||||
for (var i = 0; data.items && i < data.items.length; ++i) {
|
||||
data.items[i].labels.host = data.items[i].currentState.host;
|
||||
list.push(data.items[i]);
|
||||
}
|
||||
barrier();
|
||||
}).error($scope.handleError);
|
||||
}
|
||||
if (type === "" || type == "service") {
|
||||
k8sApi.getServices(query).success(function(data) {
|
||||
$scope.addLabel("type", "service", data.items);
|
||||
for (var i = 0; data.items && i < data.items.length; ++i) {
|
||||
list.push(data.items[i]);
|
||||
}
|
||||
barrier();
|
||||
}).error($scope.handleError);
|
||||
}
|
||||
if (type === "" || type == "replicationController") {
|
||||
k8sApi.getReplicationControllers(query).success(angular.bind(this, function(data) {
|
||||
$scope.addLabel("type", "replicationController", data.items);
|
||||
for (var i = 0; data.items && i < data.items.length; ++i) {
|
||||
list.push(data.items[i]);
|
||||
}
|
||||
barrier();
|
||||
})).error($scope.handleError);
|
||||
}
|
||||
};
|
||||
|
||||
$scope.addLabel = function(key, value, items) {
|
||||
if (!items) {
|
||||
return;
|
||||
}
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
if (!items[i].labels) {
|
||||
items[i].labels = [];
|
||||
}
|
||||
items[i].labels[key] = value;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.groupData = function(items, index) {
|
||||
var result = {
|
||||
"items": {},
|
||||
"kind": "grouping"
|
||||
};
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
key = items[i].labels[$scope.groupBy[index]];
|
||||
if (!key) {
|
||||
key = "";
|
||||
}
|
||||
var list = result.items[key];
|
||||
if (!list) {
|
||||
list = [];
|
||||
result.items[key] = list;
|
||||
}
|
||||
list.push(items[i]);
|
||||
}
|
||||
|
||||
if (index + 1 < $scope.groupBy.length) {
|
||||
for (var key in result.items) {
|
||||
result.items[key] = $scope.groupData(result.items[key], index + 1);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
$scope.getGroupColor = function(type) {
|
||||
if (type === 'pod') {
|
||||
return '#6193F0';
|
||||
} else if (type === 'replicationController') {
|
||||
return '#E008FE';
|
||||
} else if (type === 'service') {
|
||||
return '#7C43FF';
|
||||
}
|
||||
};
|
||||
|
||||
var groups = $routeParams.grouping;
|
||||
if (!groups) {
|
||||
groups = '';
|
||||
}
|
||||
|
||||
$scope.routeParams = $routeParams;
|
||||
$scope.route = $route;
|
||||
|
||||
$scope.handleGroups(groups.split('/'), $routeParams.selector);
|
||||
|
||||
$scope.handleError = function(data, status, headers, config) {
|
||||
console.log("Error (" + status + "): " + data);
|
||||
$scope_.loading = false;
|
||||
};
|
||||
|
||||
function getDefaultGroupByOptions() { return [{name: 'Type', value: 'type'}, {name: 'Name', value: 'name'}]; }
|
||||
|
||||
function buildGroupByOptions() {
|
||||
var g = $scope.groups;
|
||||
var options = getDefaultGroupByOptions();
|
||||
var newOptions = _.map(g.items, function(vals) { return _.map(vals, function(v) { return _.keys(v.labels); }); });
|
||||
newOptions =
|
||||
_.reject(_.uniq(_.flattenDeep(newOptions)), function(o) { return o == 'name' || o == 'type' || o == ""; });
|
||||
newOptions = _.map(newOptions, function(o) {
|
||||
return {
|
||||
name: o,
|
||||
value: o
|
||||
};
|
||||
});
|
||||
|
||||
options = options.concat(newOptions);
|
||||
return options;
|
||||
}
|
||||
|
||||
$scope.changeFilterBy = function(selector) {
|
||||
var grouping = $scope.selectedGroupBy;
|
||||
|
||||
var s = _.clone($location.search());
|
||||
if ($scope.routeParams.selector != selector)
|
||||
$location.path("/dashboard/groups/" + $scope.routeParams.grouping + "/selector/" + selector).search(s);
|
||||
};
|
||||
}
|
||||
]);
|
@ -0,0 +1,27 @@
|
||||
/**=========================================================
|
||||
* Module: Header
|
||||
* Visualizer for clusters
|
||||
=========================================================*/
|
||||
|
||||
angular.module('kubernetesApp.components.dashboard', [])
|
||||
.controller('HeaderCtrl', [
|
||||
'$scope',
|
||||
'$location',
|
||||
function($scope, $location) {
|
||||
'use strict';
|
||||
$scope.$watch('Pages', function(newValue, oldValue) {
|
||||
if (typeof newValue !== 'undefined') {
|
||||
$location.path(newValue);
|
||||
}
|
||||
});
|
||||
|
||||
$scope.subPages = [
|
||||
{category: 'dashboard', name: 'Explore', value: '/dashboard/groups/type/selector/'},
|
||||
{category: 'dashboard', name: 'Pods', value: '/dashboard/pods'},
|
||||
{category: 'dashboard', name: 'Minions', value: '/dashboard/minions'},
|
||||
{category: 'dashboard', name: 'Replication Controllers', value: '/dashboard/replicationcontrollers'},
|
||||
{category: 'dashboard', name: 'Services', value: '/dashboard/services'},
|
||||
{category: 'dashboard', name: 'Events', value: '/dashboard/events'}
|
||||
];
|
||||
}
|
||||
]);
|
@ -0,0 +1,85 @@
|
||||
/**=========================================================
|
||||
* Module: List Events
|
||||
* Visualizer list events
|
||||
=========================================================*/
|
||||
|
||||
app.controller('ListEventsCtrl', [
|
||||
'$scope',
|
||||
'$routeParams',
|
||||
'k8sApi',
|
||||
'$location',
|
||||
'$filter',
|
||||
function($scope, $routeParams, k8sApi, $location, $filter) {
|
||||
'use strict';
|
||||
$scope.getData = getData;
|
||||
$scope.loading = true;
|
||||
$scope.k8sApi = k8sApi;
|
||||
$scope.pods = null;
|
||||
$scope.groupedPods = null;
|
||||
$scope.serverView = false;
|
||||
|
||||
$scope.headers = [
|
||||
{name: 'Time', field: 'time'},
|
||||
{name: 'From', field: 'from'},
|
||||
{name: 'Sub Object Path', field: 'subobject'},
|
||||
{name: 'Reason', field: 'reason'},
|
||||
{name: 'Message', field: 'message'}
|
||||
];
|
||||
|
||||
$scope.custom = {
|
||||
time: '',
|
||||
from: 'grey',
|
||||
subobject: 'grey',
|
||||
reason: 'grey',
|
||||
message: 'grey'
|
||||
};
|
||||
$scope.sortable = ['time', 'from', 'subobject'];
|
||||
$scope.thumbs = 'thumb';
|
||||
$scope.count = 10;
|
||||
|
||||
$scope.go = function(d) { $location.path('/dashboard/pods/' + d.id); };
|
||||
|
||||
$scope.moreClick = function(d, e) {
|
||||
$location.path('/dashboard/pods/' + d.id);
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
function handleError(data, status, headers, config) {
|
||||
console.log("Error (" + status + "): " + data);
|
||||
$scope.loading = false;
|
||||
}
|
||||
|
||||
$scope.content = [];
|
||||
|
||||
function getData(dataId) {
|
||||
$scope.loading = true;
|
||||
k8sApi.getEvents().success(function(data) {
|
||||
$scope.loading = false;
|
||||
|
||||
var _fixComma = function(str) {
|
||||
if (str.substring(0, 1) == ',') {
|
||||
return str.substring(1);
|
||||
} else {
|
||||
return str;
|
||||
}
|
||||
};
|
||||
|
||||
data.items.forEach(function(event) {
|
||||
|
||||
$scope.content.push({
|
||||
time: $filter('date')(event.timestamp, 'medium'),
|
||||
from: event.source,
|
||||
subobject: event.involvedObject.fieldPath,
|
||||
reason: event.reason,
|
||||
message: event.message
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}).error($scope.handleError);
|
||||
}
|
||||
|
||||
getData($routeParams.serviceId);
|
||||
|
||||
}
|
||||
]);
|
@ -0,0 +1,76 @@
|
||||
/**=========================================================
|
||||
* Module: Minions
|
||||
* Visualizer for minions
|
||||
=========================================================*/
|
||||
|
||||
app.controller('ListMinionsCtrl', [
|
||||
'$scope',
|
||||
'$routeParams',
|
||||
'k8sApi',
|
||||
'$location',
|
||||
function($scope, $routeParams, k8sApi, $location) {
|
||||
'use strict';
|
||||
$scope.getData = getData;
|
||||
$scope.loading = true;
|
||||
$scope.k8sApi = k8sApi;
|
||||
$scope.pods = null;
|
||||
$scope.groupedPods = null;
|
||||
$scope.serverView = false;
|
||||
|
||||
$scope.headers = [{name: 'Name', field: 'name'}, {name: 'IP', field: 'ip'}, {name: 'Status', field: 'status'}];
|
||||
|
||||
$scope.custom = {
|
||||
name: '',
|
||||
status: 'grey',
|
||||
ip: 'grey'
|
||||
};
|
||||
$scope.sortable = ['name', 'status', 'ip'];
|
||||
$scope.thumbs = 'thumb';
|
||||
$scope.count = 10;
|
||||
|
||||
$scope.go = function(d) { $location.path('/dashboard/pods/' + d.id); };
|
||||
|
||||
$scope.moreClick = function(d, e) {
|
||||
$location.path('/dashboard/pods/' + d.id);
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
function handleError(data, status, headers, config) {
|
||||
console.log("Error (" + status + "): " + data);
|
||||
$scope.loading = false;
|
||||
}
|
||||
|
||||
$scope.content = [];
|
||||
|
||||
function getData(dataId) {
|
||||
$scope.loading = true;
|
||||
k8sApi.getMinions().success(function(data) {
|
||||
$scope.loading = false;
|
||||
|
||||
var _fixComma = function(str) {
|
||||
if (str.substring(0, 1) == ',') {
|
||||
return str.substring(1);
|
||||
} else {
|
||||
return str;
|
||||
}
|
||||
};
|
||||
|
||||
data.items.forEach(function(minion) {
|
||||
var _kind = '';
|
||||
|
||||
if (minion.status.conditions) {
|
||||
Object.keys(minion.status.conditions)
|
||||
.forEach(function(key) { _kind += minion.status.conditions[key].kind; });
|
||||
}
|
||||
|
||||
$scope.content.push({name: minion.id, ip: minion.hostIP, status: _kind});
|
||||
|
||||
});
|
||||
|
||||
}).error($scope.handleError);
|
||||
}
|
||||
|
||||
getData($routeParams.serviceId);
|
||||
|
||||
}
|
||||
]);
|
@ -0,0 +1,137 @@
|
||||
|
||||
|
||||
app.controller('ListPodsCtrl', [
|
||||
'$scope',
|
||||
'$routeParams',
|
||||
'k8sApi',
|
||||
'lodash',
|
||||
'$location',
|
||||
function($scope, $routeParams, k8sApi, lodash, $location) {
|
||||
var _ = lodash;
|
||||
$scope.getData = getData;
|
||||
$scope.loading = true;
|
||||
$scope.k8sApi = k8sApi;
|
||||
$scope.pods = null;
|
||||
$scope.groupedPods = null;
|
||||
$scope.serverView = false;
|
||||
|
||||
$scope.headers = [
|
||||
{name: '', field: 'thumb'},
|
||||
{name: 'Pod', field: 'pod'},
|
||||
{name: 'IP', field: 'ip'},
|
||||
{name: 'Status', field: 'status'},
|
||||
{name: 'Containers', field: 'containers'},
|
||||
{name: 'Images', field: 'images'},
|
||||
{name: 'Host', field: 'host'},
|
||||
{name: 'Labels', field: 'labels'}
|
||||
];
|
||||
|
||||
$scope.custom = {
|
||||
pod: '',
|
||||
ip: 'grey',
|
||||
containers: 'grey',
|
||||
images: 'grey',
|
||||
host: 'grey',
|
||||
labels: 'grey',
|
||||
status: 'grey'
|
||||
};
|
||||
$scope.sortable = ['pod', 'ip', 'status'];
|
||||
$scope.thumbs = 'thumb';
|
||||
$scope.count = 10;
|
||||
|
||||
$scope.go = function(d) { $location.path('/dashboard/pods/' + d.id); };
|
||||
|
||||
$scope.moreClick = function(d, e) {
|
||||
$location.path('/dashboard/pods/' + d.id);
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
var orderedPodNames = [];
|
||||
|
||||
function handleError(data, status, headers, config) {
|
||||
console.log("Error (" + status + "): " + data);
|
||||
$scope.loading = false;
|
||||
};
|
||||
|
||||
function getPodName(pod) { return _.has(pod.labels, 'name') ? pod.labels.name : pod.id; }
|
||||
|
||||
$scope.content = [];
|
||||
|
||||
function getData(dataId) {
|
||||
$scope.loading = true;
|
||||
k8sApi.getPods().success(angular.bind(this, function(data) {
|
||||
$scope.loading = false;
|
||||
|
||||
var _fixComma = function(str) {
|
||||
if (str.substring(0, 1) == ',') {
|
||||
return str.substring(1);
|
||||
} else {
|
||||
return str;
|
||||
}
|
||||
};
|
||||
|
||||
data.items.forEach(function(pod) {
|
||||
var _containers = '', _images = '', _labels = '', _uses = '';
|
||||
|
||||
if (pod.desiredState.manifest) {
|
||||
Object.keys(pod.desiredState.manifest.containers)
|
||||
.forEach(function(key) {
|
||||
_containers += ', ' + pod.desiredState.manifest.containers[key].name;
|
||||
_images += ', ' + pod.desiredState.manifest.containers[key].image;
|
||||
});
|
||||
}
|
||||
|
||||
Object.keys(pod.labels)
|
||||
.forEach(function(key) {
|
||||
if (key == 'name') {
|
||||
_labels += ', ' + pod.labels[key];
|
||||
}
|
||||
if (key == 'uses') {
|
||||
_uses += ', ' + pod.labels[key];
|
||||
}
|
||||
});
|
||||
|
||||
$scope.content.push({
|
||||
thumb: '"assets/img/kubernetes.svg"',
|
||||
pod: pod.id,
|
||||
ip: pod.currentState.podIP,
|
||||
containers: _fixComma(_containers),
|
||||
images: _fixComma(_images),
|
||||
host: pod.currentState.host,
|
||||
labels: _fixComma(_labels) + ':' + _fixComma(_uses),
|
||||
status: pod.currentState.status
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
})).error(angular.bind(this, handleError));
|
||||
};
|
||||
|
||||
$scope.getPodRestarts = function(pod) {
|
||||
var r = null;
|
||||
var container = _.first(pod.desiredState.manifest.containers);
|
||||
if (container) r = pod.currentState.info[container.name].restartCount;
|
||||
return r;
|
||||
};
|
||||
|
||||
$scope.otherLabels = function(labels) { return _.omit(labels, 'name') };
|
||||
|
||||
$scope.podStatusClass = function(pod) {
|
||||
|
||||
var s = pod.currentState.status.toLowerCase();
|
||||
|
||||
if (s == 'running' || s == 'succeeded')
|
||||
return null;
|
||||
else
|
||||
return "status-" + s;
|
||||
};
|
||||
|
||||
$scope.podIndexFromName = function(pod) {
|
||||
var name = getPodName(pod);
|
||||
return _.indexOf(orderedPodNames, name) + 1;
|
||||
};
|
||||
|
||||
getData($routeParams.serviceId);
|
||||
|
||||
}
|
||||
]);
|
@ -0,0 +1,101 @@
|
||||
/**=========================================================
|
||||
* Module: Replication Controllers
|
||||
* Visualizer for replication controllers
|
||||
=========================================================*/
|
||||
|
||||
app.controller('ListReplicationControllersCtrl', [
|
||||
'$scope',
|
||||
'$routeParams',
|
||||
'k8sApi',
|
||||
'$location',
|
||||
function($scope, $routeParams, k8sApi, $location) {
|
||||
'use strict';
|
||||
$scope.getData = getData;
|
||||
$scope.loading = true;
|
||||
$scope.k8sApi = k8sApi;
|
||||
$scope.pods = null;
|
||||
$scope.groupedPods = null;
|
||||
$scope.serverView = false;
|
||||
|
||||
$scope.headers = [
|
||||
{name: 'Controller', field: 'controller'},
|
||||
{name: 'Containers', field: 'containers'},
|
||||
{name: 'Images', field: 'images'},
|
||||
{name: 'Selector', field: 'selector'},
|
||||
{name: 'Replicas', field: 'replicas'}
|
||||
];
|
||||
|
||||
$scope.custom = {
|
||||
controller: '',
|
||||
containers: 'grey',
|
||||
images: 'grey',
|
||||
selector: 'grey',
|
||||
replicas: 'grey'
|
||||
};
|
||||
$scope.sortable = ['controller', 'containers', 'images'];
|
||||
$scope.thumbs = 'thumb';
|
||||
$scope.count = 10;
|
||||
|
||||
$scope.go = function(d) { $location.path('/dashboard/pods/' + d.id); };
|
||||
|
||||
$scope.moreClick = function(d, e) {
|
||||
$location.path('/dashboard/pods/' + d.id);
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
function handleError(data, status, headers, config) {
|
||||
console.log("Error (" + status + "): " + data);
|
||||
$scope.loading = false;
|
||||
}
|
||||
|
||||
$scope.content = [];
|
||||
|
||||
function getData(dataId) {
|
||||
$scope.loading = true;
|
||||
k8sApi.getReplicationControllers().success(function(data) {
|
||||
$scope.loading = false;
|
||||
|
||||
var _fixComma = function(str) {
|
||||
if (str.substring(0, 1) == ',') {
|
||||
return str.substring(1);
|
||||
} else {
|
||||
return str;
|
||||
}
|
||||
};
|
||||
|
||||
data.items.forEach(function(replicationController) {
|
||||
|
||||
var _name = '', _image = '';
|
||||
|
||||
if (replicationController.desiredState.podTemplate.desiredState.manifest.containers) {
|
||||
Object.keys(replicationController.desiredState.podTemplate.desiredState.manifest.containers)
|
||||
.forEach(function(key) {
|
||||
_name += replicationController.desiredState.podTemplate.desiredState.manifest.containers[key].name;
|
||||
_image += replicationController.desiredState.podTemplate.desiredState.manifest.containers[key].image;
|
||||
});
|
||||
}
|
||||
|
||||
var _name_selector = '';
|
||||
|
||||
if (replicationController.desiredState.replicaSelector) {
|
||||
Object.keys(replicationController.desiredState.replicaSelector)
|
||||
.forEach(function(key) { _name_selector += replicationController.desiredState.replicaSelector[key]; });
|
||||
}
|
||||
|
||||
$scope.content.push({
|
||||
controller: replicationController.id,
|
||||
containers: _name,
|
||||
images: _image,
|
||||
selector: _name_selector,
|
||||
replicas: replicationController.currentState.replicas
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}).error($scope.handleError);
|
||||
}
|
||||
|
||||
getData($routeParams.serviceId);
|
||||
|
||||
}
|
||||
]);
|
@ -0,0 +1,110 @@
|
||||
/**=========================================================
|
||||
* Module: Services
|
||||
* Visualizer for services
|
||||
=========================================================*/
|
||||
|
||||
app.controller('ListServicesCtrl', [
|
||||
'$scope',
|
||||
'$interval',
|
||||
'$routeParams',
|
||||
'k8sApi',
|
||||
'$rootScope',
|
||||
function($scope, $interval, $routeParams, k8sApi, $rootScope) {
|
||||
'use strict';
|
||||
$scope.doTheBack = function() { window.history.back(); };
|
||||
|
||||
$scope.headers = [
|
||||
{name: 'Name', field: 'name'},
|
||||
{name: 'Labels', field: 'labels'},
|
||||
{name: 'Selector', field: 'selector'},
|
||||
{name: 'IP', field: 'ip'},
|
||||
{name: 'Port', field: 'port'}
|
||||
];
|
||||
|
||||
$scope.custom = {
|
||||
name: '',
|
||||
ip: 'grey',
|
||||
selector: 'grey',
|
||||
port: 'grey',
|
||||
labels: 'grey'
|
||||
};
|
||||
$scope.sortable = ['name', 'ip', 'port'];
|
||||
$scope.count = 10;
|
||||
|
||||
$scope.content = [];
|
||||
|
||||
$rootScope.doTheBack = $scope.doTheBack;
|
||||
|
||||
$scope.handleError = function(data, status, headers, config) {
|
||||
console.log("Error (" + status + "): " + data);
|
||||
$scope_.loading = false;
|
||||
};
|
||||
|
||||
$scope.getData = function(dataId) {
|
||||
$scope.loading = true;
|
||||
k8sApi.getServices(dataId).success(angular.bind(this, function(data) {
|
||||
$scope.services = data;
|
||||
$scope.loading = false;
|
||||
|
||||
var _fixComma = function(str) {
|
||||
if (str.substring(0, 1) == ',') {
|
||||
return str.substring(1);
|
||||
} else {
|
||||
return str;
|
||||
}
|
||||
};
|
||||
|
||||
var addLabel = function(str, label) {
|
||||
if (str) {
|
||||
str = label + str;
|
||||
}
|
||||
return str;
|
||||
};
|
||||
|
||||
if (data.items.constructor === Array) {
|
||||
data.items.forEach(function(service) {
|
||||
|
||||
var _name = '', _uses = '', _component = '', _provider = '';
|
||||
|
||||
if (service.labels !== null && typeof service.labels === 'object') {
|
||||
Object.keys(service.labels)
|
||||
.forEach(function(key) {
|
||||
if (key == 'name') {
|
||||
_name += ',' + service.labels[key];
|
||||
}
|
||||
if (key == 'component') {
|
||||
_component += ',' + service.labels[key];
|
||||
}
|
||||
if (key == 'provider') {
|
||||
_provider += ',' + service.labels[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var _selectors = '';
|
||||
|
||||
if (service.selector !== null && typeof service.selector === 'object') {
|
||||
Object.keys(service.selector)
|
||||
.forEach(function(key) {
|
||||
if (key == 'name') {
|
||||
_selectors += ',' + service.selector[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$scope.content.push({
|
||||
name: service.id,
|
||||
ip: service.portalIP,
|
||||
port: service.port,
|
||||
selector: addLabel(_fixComma(_selectors), 'name='),
|
||||
labels: addLabel(_fixComma(_name), 'name=') + ' ' + addLabel(_fixComma(_component), 'component=') + ' ' +
|
||||
addLabel(_fixComma(_provider), 'provider=')
|
||||
});
|
||||
});
|
||||
}
|
||||
})).error($scope.handleError);
|
||||
};
|
||||
|
||||
$scope.getData($routeParams.serviceId);
|
||||
}
|
||||
]);
|
@ -0,0 +1,33 @@
|
||||
/**=========================================================
|
||||
* Module: Pods
|
||||
* Visualizer for pods
|
||||
=========================================================*/
|
||||
|
||||
app.controller('PodCtrl', [
|
||||
'$scope',
|
||||
'$interval',
|
||||
'$routeParams',
|
||||
'k8sApi',
|
||||
'$rootScope',
|
||||
function($scope, $interval, $routeParams, k8sApi, $rootScope) {
|
||||
'use strict';
|
||||
$scope.doTheBack = function() { window.history.back(); };
|
||||
|
||||
$rootScope.doTheBack = $scope.doTheBack;
|
||||
|
||||
$scope.handleError = function(data, status, headers, config) {
|
||||
console.log("Error (" + status + "): " + data);
|
||||
$scope_.loading = false;
|
||||
};
|
||||
|
||||
$scope.handlePod = function(podId) {
|
||||
$scope.loading = true;
|
||||
k8sApi.getPods(podId).success(angular.bind(this, function(data) {
|
||||
$scope.pod = data;
|
||||
$scope.loading = false;
|
||||
})).error($scope.handleError);
|
||||
};
|
||||
|
||||
$scope.handlePod($routeParams.podId);
|
||||
}
|
||||
]);
|
@ -0,0 +1,32 @@
|
||||
/**=========================================================
|
||||
* Module: Replication
|
||||
* Visualizer for replication controllers
|
||||
=========================================================*/
|
||||
|
||||
function ReplicationController() {
|
||||
}
|
||||
|
||||
ReplicationController.prototype.getData = function(dataId) {
|
||||
this.scope.loading = true;
|
||||
this.k8sApi.getReplicationControllers(dataId).success(angular.bind(this, function(data) {
|
||||
this.scope.replicationController = data;
|
||||
this.scope.loading = false;
|
||||
})).error(angular.bind(this, this.handleError));
|
||||
};
|
||||
|
||||
ReplicationController.prototype.handleError = function(data, status, headers, config) {
|
||||
console.log("Error (" + status + "): " + data);
|
||||
this.scope.loading = false;
|
||||
};
|
||||
|
||||
app.controller('ReplicationControllerCtrl', [
|
||||
'$scope',
|
||||
'$routeParams',
|
||||
'k8sApi',
|
||||
function($scope, $routeParams, k8sApi) {
|
||||
$scope.controller = new ReplicationController();
|
||||
$scope.controller.k8sApi = k8sApi;
|
||||
$scope.controller.scope = $scope;
|
||||
$scope.controller.getData($routeParams.replicationControllerId);
|
||||
}
|
||||
]);
|
@ -0,0 +1,40 @@
|
||||
/**=========================================================
|
||||
* Module: Services
|
||||
* Visualizer for services
|
||||
=========================================================*/
|
||||
|
||||
function ServiceController() {
|
||||
}
|
||||
|
||||
ServiceController.prototype.getData = function(dataId) {
|
||||
this.scope.loading = true;
|
||||
this.k8sApi.getServices(dataId).success(angular.bind(this, function(data) {
|
||||
this.scope.service = data;
|
||||
this.scope.loading = false;
|
||||
})).error(angular.bind(this, this.handleError));
|
||||
};
|
||||
|
||||
ServiceController.prototype.handleError = function(data, status, headers, config) {
|
||||
console.log("Error (" + status + "): " + data);
|
||||
this.scope.loading = false;
|
||||
};
|
||||
|
||||
app.controller('ServiceCtrl', [
|
||||
'$scope',
|
||||
'$routeParams',
|
||||
'k8sApi',
|
||||
'$location',
|
||||
function($scope, $routeParams, k8sApi, $location) {
|
||||
$scope.controller = new ServiceController();
|
||||
$scope.controller.k8sApi = k8sApi;
|
||||
$scope.controller.scope = $scope;
|
||||
$scope.controller.getData($routeParams.serviceId);
|
||||
|
||||
$scope.go = function(d) { $location.path('/dashboard/services/' + d.id); }
|
||||
|
||||
$scope.moreClick = function(d, e) {
|
||||
$location.path('/dashboard/services/' + d.id);
|
||||
e.stopPropagation();
|
||||
}
|
||||
}
|
||||
]);
|
346
www/master/components/dashboard/js/modules/directives/d3MinionBarGauge.js
vendored
Normal file
@ -0,0 +1,346 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
angular.module('kubernetesApp.components.dashboard')
|
||||
.directive('d3MinionBarGauge', [
|
||||
'd3DashboardService',
|
||||
function(d3DashboardService) {
|
||||
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
data: '=',
|
||||
thickness: '@',
|
||||
graphWidth: '@',
|
||||
graphHeight: '@'
|
||||
|
||||
},
|
||||
link: function(scope, element, attrs) {
|
||||
|
||||
var draw = function(d3) {
|
||||
var svg = d3.select("svg.chart");
|
||||
var legendSvg = d3.select("svg.legend");
|
||||
window.onresize = function() { return scope.$apply(); };
|
||||
|
||||
scope.$watch(function() { return angular.element(window)[0].innerWidth; },
|
||||
function() { return scope.render(scope.data); });
|
||||
|
||||
scope.$watch('data', function(newVals, oldVals) {
|
||||
return initOrUpdate(newVals, oldVals);
|
||||
|
||||
}, true);
|
||||
|
||||
function initOrUpdate(newVals, oldVals) {
|
||||
if (oldVals === null || oldVals === undefined) {
|
||||
return scope.render(newVals);
|
||||
} else {
|
||||
return update(oldVals, newVals);
|
||||
}
|
||||
}
|
||||
|
||||
var textOffset = 10;
|
||||
var el = null;
|
||||
var radius = 100;
|
||||
var oldData = [];
|
||||
|
||||
function init(options) {
|
||||
var clone = options.data;
|
||||
var preparedData = setData(clone);
|
||||
setup(preparedData, options.width, options.height);
|
||||
}
|
||||
|
||||
function setup(data, w, h) {
|
||||
svg = d3.select(element[0]).append("svg").attr("width", "100%");
|
||||
|
||||
legendSvg = d3.select(element[0]).append("svg").attr("width", "100%");
|
||||
|
||||
var chart = svg.attr("class", "chart")
|
||||
.attr("width", w)
|
||||
.attr("height", h - 25)
|
||||
.append("svg:g")
|
||||
.attr("class", "concentricchart")
|
||||
.attr("transform", "translate(" + ((w / 2)) + "," + h / 4 + ")");
|
||||
|
||||
var legend = legendSvg.attr("class", "legend").attr("width", w);
|
||||
|
||||
radius = Math.min(w, h) / 2;
|
||||
|
||||
var hostName = legendSvg.append("text")
|
||||
.attr("class", "hostName")
|
||||
.attr("transform", "translate(" + ((w - 120) / 2) + "," + 15 + ")");
|
||||
|
||||
var label_legend_area = legendSvg.append("svg:g")
|
||||
.attr("class", "label_legend_area")
|
||||
.attr("transform", "translate(" + ((w - 185) / 2) + "," + 35 + ")");
|
||||
|
||||
var legend_group = label_legend_area.append("svg:g").attr("class", "legend_group");
|
||||
|
||||
var label_group = label_legend_area.append("svg:g")
|
||||
.attr("class", "label_group")
|
||||
.attr("transform", "translate(" + 25 + "," + 11 + ")");
|
||||
|
||||
var stats_group = label_legend_area.append("svg:g")
|
||||
.attr("class", "stats_group")
|
||||
.attr("transform", "translate(" + 85 + "," + 11 + ")");
|
||||
|
||||
var path_group = chart.append("svg:g")
|
||||
.attr("class", "path_group")
|
||||
.attr("transform", "translate(0," + (h / 4) + ")");
|
||||
var value_group = chart.append("svg:g")
|
||||
.attr("class", "value_group")
|
||||
.attr("transform", "translate(" + -(w * 0.205) + "," + -(h * 0.10) + ")");
|
||||
generateArcs(chart, data);
|
||||
}
|
||||
|
||||
function update(_oldData, _newData) {
|
||||
if (_newData === undefined || _newData === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var clone = jQuery.extend(true, {}, _newData);
|
||||
var cloneOld = jQuery.extend(true, {}, _oldData);
|
||||
var preparedData = setData(clone);
|
||||
oldData = setData(cloneOld);
|
||||
animate(preparedData);
|
||||
}
|
||||
|
||||
function animate(data) { generateArcs(null, data); }
|
||||
|
||||
function setData(data) {
|
||||
var diameter = 2 * Math.PI * radius;
|
||||
var localData = [];
|
||||
|
||||
$.each(data[0].segments, function(ri, value) {
|
||||
|
||||
function calcAngles(v) {
|
||||
var segmentValueSum = 200;
|
||||
if (v > segmentValueSum) {
|
||||
v = segmentValueSum;
|
||||
}
|
||||
|
||||
var segmentValue = v;
|
||||
var fraction = segmentValue / segmentValueSum;
|
||||
var arcBatchLength = fraction * 4 * Math.PI;
|
||||
var arcPartition = arcBatchLength;
|
||||
var startAngle = Math.PI * 2;
|
||||
var endAngle = startAngle + arcPartition;
|
||||
|
||||
return {
|
||||
startAngle: startAngle,
|
||||
endAngle: endAngle
|
||||
};
|
||||
}
|
||||
|
||||
var valueData = calcAngles(value.value);
|
||||
data[0].segments[ri].startAngle = valueData.startAngle;
|
||||
data[0].segments[ri].endAngle = valueData.endAngle;
|
||||
|
||||
var maxData = value.maxData;
|
||||
var maxTickData = calcAngles(maxData.maxValue + 0.2);
|
||||
data[0].segments[ri].maxTickStartAngle = maxTickData.startAngle;
|
||||
data[0].segments[ri].maxTickEndAngle = maxTickData.endAngle;
|
||||
|
||||
var maxArcData = calcAngles(maxData.maxValue);
|
||||
data[0].segments[ri].maxArcStartAngle = maxArcData.startAngle;
|
||||
data[0].segments[ri].maxArcEndAngle = maxArcData.endAngle;
|
||||
|
||||
data[0].segments[ri].index = ri;
|
||||
});
|
||||
localData.push(data[0].segments);
|
||||
return localData[0];
|
||||
}
|
||||
|
||||
function generateArcs(_svg, data) {
|
||||
var chart = svg;
|
||||
var transitionTime = 750;
|
||||
$.each(data, function(index, value) {
|
||||
if (oldData[index] !== undefined) {
|
||||
data[index].previousEndAngle = oldData[index].endAngle;
|
||||
} else {
|
||||
data[index].previousEndAngle = 0;
|
||||
}
|
||||
});
|
||||
var thickness = parseInt(scope.thickness, 10);
|
||||
var ir = (parseInt(scope.graphWidth, 10) / 3);
|
||||
var path_group = svg.select('.path_group');
|
||||
var arc_group = path_group.selectAll(".arc_group").data(data);
|
||||
var arcEnter = arc_group.enter().append("g").attr("class", "arc_group");
|
||||
|
||||
arcEnter.append("path").attr("class", "bg-circle").attr("d", getBackgroundArc(thickness, ir));
|
||||
|
||||
arcEnter.append("path")
|
||||
.attr("class", function(d, i) { return 'max_tick_arc ' + d.maxData.maxTickClassNames; });
|
||||
|
||||
arcEnter.append("path")
|
||||
.attr("class", function(d, i) { return 'max_bg_arc ' + d.maxData.maxClassNames; });
|
||||
|
||||
arcEnter.append("path").attr("class", function(d, i) { return 'value_arc ' + d.classNames; });
|
||||
|
||||
var max_tick_arc = arc_group.select(".max_tick_arc");
|
||||
|
||||
max_tick_arc.transition()
|
||||
.attr("class", function(d, i) { return 'max_tick_arc ' + d.maxData.maxTickClassNames; })
|
||||
.attr("d", function(d) {
|
||||
var arc = maxArc(thickness, ir);
|
||||
arc.startAngle(d.maxTickStartAngle);
|
||||
arc.endAngle(d.maxTickEndAngle);
|
||||
return arc(d);
|
||||
});
|
||||
|
||||
var max_bg_arc = arc_group.select(".max_bg_arc");
|
||||
|
||||
max_bg_arc.transition()
|
||||
.attr("class", function(d, i) { return 'max_bg_arc ' + d.maxData.maxClassNames; })
|
||||
.attr("d", function(d) {
|
||||
var arc = maxArc(thickness, ir);
|
||||
arc.startAngle(d.maxArcStartAngle);
|
||||
arc.endAngle(d.maxArcEndAngle);
|
||||
return arc(d);
|
||||
});
|
||||
|
||||
var value_arc = arc_group.select(".value_arc");
|
||||
|
||||
value_arc.transition().ease("exp").attr("class", function(d, i) {
|
||||
return 'value_arc ' + d.classNames;
|
||||
}).duration(transitionTime).attrTween("d", function(d) { return arcTween(d, thickness, ir); });
|
||||
|
||||
arc_group.exit()
|
||||
.select(".value_arc")
|
||||
.transition()
|
||||
.ease("exp")
|
||||
.duration(transitionTime)
|
||||
.attrTween("d", function(d) { return arcTween(d, thickness, ir); })
|
||||
.remove();
|
||||
|
||||
drawLabels(chart, data, ir, thickness);
|
||||
buildLegend(chart, data);
|
||||
}
|
||||
|
||||
function arcTween(b, thickness, ir) {
|
||||
var prev = JSON.parse(JSON.stringify(b));
|
||||
prev.endAngle = b.previousEndAngle;
|
||||
var i = d3.interpolate(prev, b);
|
||||
return function(t) { return getArc(thickness, ir)(i(t)); };
|
||||
}
|
||||
|
||||
function maxArc(thickness, ir) {
|
||||
var arc = d3.svg.arc().innerRadius(function(d) {
|
||||
return getRadiusRing(ir, d.index);
|
||||
}).outerRadius(function(d) { return getRadiusRing(ir + thickness, d.index); });
|
||||
return arc;
|
||||
}
|
||||
|
||||
function drawLabels(chart, data, ir, thickness) {
|
||||
svg.select('.value_group').selectAll("*").remove();
|
||||
var counts = data.length;
|
||||
var value_group = chart.select('.value_group');
|
||||
var valueLabels = value_group.selectAll("text.value").data(data);
|
||||
valueLabels.enter()
|
||||
.append("svg:text")
|
||||
.attr("class", "value")
|
||||
.attr(
|
||||
"transform", function(d) { return "translate(" + (getRadiusRing(ir, counts - 1)) + ", 0)"; })
|
||||
.attr("dx", function(d, i) { return 0; })
|
||||
.attr("dy", function(d, i) { return (thickness + 3) * i; })
|
||||
.attr("text-anchor", function(d) { return "start"; })
|
||||
.text(function(d) { return d.value; });
|
||||
valueLabels.transition().duration(300).attrTween(
|
||||
"d", function(d) { return arcTween(d, thickness, ir); });
|
||||
valueLabels.exit().remove();
|
||||
}
|
||||
|
||||
function buildLegend(chart, data) {
|
||||
var svg = legendSvg;
|
||||
svg.select('.label_group').selectAll("*").remove();
|
||||
svg.select('.legend_group').selectAll("*").remove();
|
||||
svg.select('.stats_group').selectAll("*").remove();
|
||||
|
||||
var host_name = svg.select('.hostName');
|
||||
var label_group = svg.select('.label_group');
|
||||
var stats_group = svg.select('.stats_group');
|
||||
|
||||
host_name.text(data[0].hostName);
|
||||
|
||||
host_name = svg.selectAll("text.hostName").data(data);
|
||||
|
||||
host_name.attr("text-anchor", function(d) { return "start"; })
|
||||
.text(function(d) { return d.hostName; });
|
||||
host_name.exit().remove();
|
||||
|
||||
var labels = label_group.selectAll("text.labels").data(data);
|
||||
labels.enter()
|
||||
.append("svg:text")
|
||||
.attr("class", "labels")
|
||||
.attr("dy", function(d, i) { return 19 * i; })
|
||||
.attr("text-anchor", function(d) { return "start"; })
|
||||
.text(function(d) { return d.label; });
|
||||
labels.exit().remove();
|
||||
|
||||
var stats = stats_group.selectAll("text.stats").data(data);
|
||||
stats.enter()
|
||||
.append("svg:text")
|
||||
.attr("class", "stats")
|
||||
.attr("dy", function(d, i) { return 19 * i; })
|
||||
.attr("text-anchor", function(d) { return "start"; })
|
||||
.text(function(d) { return d.stats; });
|
||||
stats.exit().remove();
|
||||
|
||||
var legend_group = svg.select('.legend_group');
|
||||
var legend = legend_group.selectAll("rect").data(data);
|
||||
legend.enter()
|
||||
.append("svg:rect")
|
||||
.attr("x", 2)
|
||||
.attr("y", function(d, i) { return 19 * i; })
|
||||
.attr("width", 13)
|
||||
.attr("height", 13)
|
||||
.attr("class", function(d, i) { return "rect " + d.classNames; });
|
||||
|
||||
legend.exit().remove();
|
||||
}
|
||||
|
||||
function getRadiusRing(ir, i) { return ir - (i * 20); }
|
||||
|
||||
function getArc(thickness, ir) {
|
||||
var arc = d3.svg.arc()
|
||||
.innerRadius(function(d) { return getRadiusRing(ir, d.index); })
|
||||
.outerRadius(function(d) { return getRadiusRing(ir + thickness, d.index); })
|
||||
.startAngle(function(d, i) { return d.startAngle; })
|
||||
.endAngle(function(d, i) { return d.endAngle; });
|
||||
return arc;
|
||||
}
|
||||
|
||||
function getBackgroundArc(thickness, ir) {
|
||||
var arc = d3.svg.arc()
|
||||
.innerRadius(function(d) { return getRadiusRing(ir, d.index); })
|
||||
.outerRadius(function(d) { return getRadiusRing(ir + thickness, d.index); })
|
||||
.startAngle(0)
|
||||
.endAngle(function() { return 2 * Math.PI; });
|
||||
return arc;
|
||||
}
|
||||
|
||||
scope.render = function(data) {
|
||||
if (data === undefined || data === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
svg.selectAll("*").remove();
|
||||
|
||||
var graph = $(element[0]);
|
||||
var w = scope.graphWidth;
|
||||
var h = scope.graphHeight;
|
||||
|
||||
var options = {
|
||||
data: data,
|
||||
width: w,
|
||||
height: h
|
||||
};
|
||||
|
||||
init(options);
|
||||
};
|
||||
};
|
||||
d3DashboardService.d3().then(draw);
|
||||
}
|
||||
};
|
||||
}
|
||||
]);
|
||||
}());
|
@ -0,0 +1,101 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
angular.module('kubernetesApp.components.dashboard')
|
||||
.directive(
|
||||
'dashboardHeader',
|
||||
function() {
|
||||
'use strict';
|
||||
return {
|
||||
restrict: 'A',
|
||||
replace: true,
|
||||
scope: {user: '='},
|
||||
templateUrl: "components/dashboard/pages/header.html",
|
||||
controller: [
|
||||
'$scope',
|
||||
'$filter',
|
||||
'$location',
|
||||
'$rootScope',
|
||||
function($scope, $filter, $location, $rootScope) {
|
||||
$scope.$watch('page', function(newValue, oldValue) {
|
||||
if (typeof newValue !== 'undefined') {
|
||||
$location.path(newValue);
|
||||
}
|
||||
});
|
||||
|
||||
$scope.subpages = [
|
||||
{
|
||||
category: 'dashboard',
|
||||
name: 'Groups',
|
||||
value: '/dashboard/groups/type/selector/',
|
||||
id: 'groupsView'
|
||||
},
|
||||
{category: 'dashboard', name: 'Pods', value: '/dashboard/pods', id: 'podsView'},
|
||||
{category: 'dashboard', name: 'Minions', value: '/dashboard/minions', id: 'minionsView'},
|
||||
{
|
||||
category: 'dashboard',
|
||||
name: 'Replication Controllers',
|
||||
value: '/dashboard/replicationcontrollers',
|
||||
id: 'rcView'
|
||||
},
|
||||
{category: 'dashboard', name: 'Services', value: '/dashboard/services', id: 'servicesView'},
|
||||
{category: 'dashboard', name: 'Events', value: '/dashboard/events', id: 'eventsView'},
|
||||
];
|
||||
}
|
||||
]
|
||||
};
|
||||
})
|
||||
.directive('dashboardFooter',
|
||||
function() {
|
||||
'use strict';
|
||||
return {
|
||||
restrict: 'A',
|
||||
replace: true,
|
||||
templateUrl: "components/dashboard/pages/footer.html",
|
||||
controller: ['$scope', '$filter', function($scope, $filter) {}]
|
||||
};
|
||||
})
|
||||
.directive('mdTable', function() {
|
||||
'use strict';
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
headers: '=',
|
||||
content: '=',
|
||||
sortable: '=',
|
||||
filters: '=',
|
||||
customClass: '=customClass',
|
||||
thumbs: '=',
|
||||
count: '='
|
||||
},
|
||||
controller: function($scope, $filter, $window, $location) {
|
||||
var orderBy = $filter('orderBy');
|
||||
$scope.currentPage = 0;
|
||||
$scope.nbOfPages = function() { return Math.ceil($scope.content.length / $scope.count); };
|
||||
$scope.handleSort = function(field) {
|
||||
if ($scope.sortable.indexOf(field) > -1) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
$scope.go = function(d) {
|
||||
if (d.pod) {
|
||||
$location.path('/dashboard/pods/' + d.pod);
|
||||
} else if (d.name) {
|
||||
$location.path('/dashboard/services/' + d.name);
|
||||
}
|
||||
};
|
||||
$scope.order = function(predicate, reverse) {
|
||||
$scope.content = orderBy($scope.content, predicate, reverse);
|
||||
$scope.predicate = predicate;
|
||||
};
|
||||
$scope.order($scope.sortable[0], false);
|
||||
$scope.getNumber = function(num) { return new Array(num); };
|
||||
$scope.goToPage = function(page) { $scope.currentPage = page; };
|
||||
},
|
||||
templateUrl: 'views/partials/md-table.tmpl.html'
|
||||
};
|
||||
});
|
||||
|
||||
}());
|
31
www/master/components/dashboard/js/modules/services/d3.js
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
angular.module('kubernetesApp.components.dashboard')
|
||||
.factory('d3DashboardService', [
|
||||
'$document',
|
||||
'$q',
|
||||
'$rootScope',
|
||||
function($document, $q, $rootScope) {
|
||||
var d = $q.defer();
|
||||
function onScriptLoad() {
|
||||
// Load client in the browser
|
||||
$rootScope.$apply(function() { d.resolve(window.d3); });
|
||||
}
|
||||
// Create a script tag with d3 as the source
|
||||
// and call our onScriptLoad callback when it
|
||||
// has been loaded
|
||||
var scriptTag = $document[0].createElement('script');
|
||||
scriptTag.type = 'text/javascript';
|
||||
scriptTag.async = true;
|
||||
scriptTag.src = 'vendor/d3/d3.min.js';
|
||||
scriptTag.onreadystatechange = function() {
|
||||
if (this.readyState == 'complete') onScriptLoad();
|
||||
};
|
||||
scriptTag.onload = onScriptLoad;
|
||||
|
||||
var s = $document[0].getElementsByTagName('body')[0];
|
||||
s.appendChild(scriptTag);
|
||||
|
||||
return {
|
||||
d3: function() { return d.promise; }
|
||||
};
|
||||
}
|
||||
]);
|
@ -0,0 +1,76 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
angular.module('pods', []).service('podService', PodDataService);
|
||||
|
||||
/**
|
||||
* Pod DataService
|
||||
* Mock async data service.
|
||||
*
|
||||
* @returns {{loadAll: Function}}
|
||||
* @constructor
|
||||
*/
|
||||
function PodDataService($q) {
|
||||
var pods = {
|
||||
"kind": "PodList",
|
||||
"creationTimestamp": null,
|
||||
"selfLink": "/api/v1beta1/pods",
|
||||
"resourceVersion": 166552,
|
||||
"apiVersion": "v1beta1",
|
||||
"items": [{
|
||||
"id": "hello",
|
||||
"uid": "0fe3644e-ab53-11e4-8ae8-061695c59fcf",
|
||||
"creationTimestamp": "2015-02-03T03:16:36Z",
|
||||
"selfLink": "/api/v1beta1/pods/hello?namespace=default",
|
||||
"resourceVersion": 466,
|
||||
"namespace": "default",
|
||||
"labels": {"environment": "testing", "name": "hello"},
|
||||
"desiredState": {
|
||||
"manifest": {
|
||||
"version": "v1beta2",
|
||||
"id": "",
|
||||
"volumes": null,
|
||||
"containers": [{
|
||||
"name": "hello",
|
||||
"image": "quay.io/kelseyhightower/hello",
|
||||
"ports": [{"hostPort": 80, "containerPort": 80, "protocol": "TCP"}],
|
||||
"imagePullPolicy": "PullIfNotPresent"
|
||||
}],
|
||||
"restartPolicy": {"always": {}},
|
||||
"dnsPolicy": "ClusterFirst"
|
||||
}
|
||||
},
|
||||
"currentState": {
|
||||
"manifest": {"version": "", "id": "", "volumes": null, "containers": null, "restartPolicy": {}},
|
||||
"status": "Running",
|
||||
"host": "172.31.12.204",
|
||||
"podIP": "10.244.73.2",
|
||||
"info": {
|
||||
"hello": {
|
||||
"state": {"running": {"startedAt": "2015-02-03T03:16:51Z"}},
|
||||
"restartCount": 0,
|
||||
"image": "quay.io/kelseyhightower/hello",
|
||||
"containerID": "docker://96ade8ff30a44c4489969eaf343a7899317671b07a9766ecd0963e9b41501256"
|
||||
},
|
||||
"net": {
|
||||
"state": {"running": {"startedAt": "2015-02-03T03:16:41Z"}},
|
||||
"restartCount": 0,
|
||||
"podIP": "10.244.73.2",
|
||||
"image": "kubernetes/pause:latest",
|
||||
"containerID": "docker://93d32603cafbff7165dadb1d4527899c24246bca2f5e6770b8297fd3721b272c"
|
||||
}
|
||||
}
|
||||
}
|
||||
}]
|
||||
};
|
||||
|
||||
// Uses promises
|
||||
return {
|
||||
loadAll: function() {
|
||||
// Simulate async call
|
||||
return $q.when(pods);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
})();
|
@ -0,0 +1,33 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
angular.module('replicationControllers', [])
|
||||
.service('replicationControllerService', ReplicationControllerDataService);
|
||||
|
||||
/**
|
||||
* Replication Controller DataService
|
||||
* Mock async data service.
|
||||
*
|
||||
* @returns {{loadAll: Function}}
|
||||
* @constructor
|
||||
*/
|
||||
function ReplicationControllerDataService($q) {
|
||||
var replicationControllers = {
|
||||
"kind": "ReplicationControllerList",
|
||||
"creationTimestamp": null,
|
||||
"selfLink": "/api/v1beta1/replicationControllers",
|
||||
"resourceVersion": 166552,
|
||||
"apiVersion": "v1beta1",
|
||||
"items": []
|
||||
};
|
||||
|
||||
// Uses promises
|
||||
return {
|
||||
loadAll: function() {
|
||||
// Simulate async call
|
||||
return $q.when(replicationControllers);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
})();
|
@ -0,0 +1,63 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
angular.module('services', []).service('serviceService', ServiceDataService);
|
||||
|
||||
/**
|
||||
* Service DataService
|
||||
* Mock async data service.
|
||||
*
|
||||
* @returns {{loadAll: Function}}
|
||||
* @constructor
|
||||
*/
|
||||
function ServiceDataService($q) {
|
||||
var services = {
|
||||
"kind": "ServiceList",
|
||||
"creationTimestamp": null,
|
||||
"selfLink": "/api/v1beta1/services",
|
||||
"resourceVersion": 166552,
|
||||
"apiVersion": "v1beta1",
|
||||
"items": [
|
||||
{
|
||||
"id": "kubernetes",
|
||||
"uid": "626dd08d-ab51-11e4-8ae8-061695c59fcf",
|
||||
"creationTimestamp": "2015-02-03T03:04:36Z",
|
||||
"selfLink": "/api/v1beta1/services/kubernetes?namespace=default",
|
||||
"resourceVersion": 11,
|
||||
"namespace": "default",
|
||||
"port": 443,
|
||||
"protocol": "TCP",
|
||||
"labels": {"component": "apiserver", "provider": "kubernetes"},
|
||||
"selector": null,
|
||||
"containerPort": 0,
|
||||
"portalIP": "10.244.66.215",
|
||||
"sessionAffinity": "None"
|
||||
},
|
||||
{
|
||||
"id": "kubernetes-ro",
|
||||
"uid": "626f9584-ab51-11e4-8ae8-061695c59fcf",
|
||||
"creationTimestamp": "2015-02-03T03:04:36Z",
|
||||
"selfLink": "/api/v1beta1/services/kubernetes-ro?namespace=default",
|
||||
"resourceVersion": 12,
|
||||
"namespace": "default",
|
||||
"port": 80,
|
||||
"protocol": "TCP",
|
||||
"labels": {"component": "apiserver", "provider": "kubernetes"},
|
||||
"selector": null,
|
||||
"containerPort": 0,
|
||||
"portalIP": "10.244.182.142",
|
||||
"sessionAffinity": "None"
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
// Uses promises
|
||||
return {
|
||||
loadAll: function() {
|
||||
// Simulate async call
|
||||
return $q.when(services);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
})();
|
0
www/master/components/dashboard/less/.gitkeep
Normal file
73
www/master/components/dashboard/less/dashboard.less
Executable file
@ -0,0 +1,73 @@
|
||||
.dashboard {
|
||||
.body-wrapper {
|
||||
padding: 25px;
|
||||
}
|
||||
// analagous to float:right when used with row layout
|
||||
[flex-align-self="end"] {
|
||||
-webkit-align-self: flex-end;
|
||||
-ms-flex-align-self: end;
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
.back {
|
||||
font-size: 18px;
|
||||
line-height: 27px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.heading {
|
||||
font-size: 18px;
|
||||
line-height: 21px;
|
||||
color: #222222;
|
||||
margin-bottom: 25px;
|
||||
|
||||
.label {
|
||||
color: #777777;
|
||||
}
|
||||
}
|
||||
|
||||
@import "dashboard/pods";
|
||||
@import "dashboard/tables";
|
||||
@import "dashboard/servers";
|
||||
@import "dashboard/groups";
|
||||
|
||||
.detail {
|
||||
color: #222222;
|
||||
|
||||
.back {
|
||||
font-size: 18px;
|
||||
line-height: 27px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.heading {
|
||||
font-size: 18px;
|
||||
line-height: 21px;
|
||||
color: #222222;
|
||||
margin-bottom: 25px;
|
||||
|
||||
.label {
|
||||
color: #777777;
|
||||
}
|
||||
}
|
||||
|
||||
td.name {
|
||||
font-size: 14px;
|
||||
color: #222222;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
td.value {
|
||||
margin-left: 50px;
|
||||
font-size: 14px;
|
||||
color: #888888;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.containerTable {
|
||||
td {
|
||||
padding-right: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
122
www/master/components/dashboard/less/dashboard/colors.less
Normal file
@ -0,0 +1,122 @@
|
||||
|
||||
@color-codes:
|
||||
#2962FF, //blue-deep
|
||||
#AA00FF, //purple-deep
|
||||
#00C853, //green-deep
|
||||
#304FFE, //indigo-deep
|
||||
#0091EA, //light-blue-deep
|
||||
#FF6D00, //orange-deep
|
||||
#00BFA5, //teal-deep
|
||||
#C51162, //pink-deep
|
||||
#64DD17, //light-green-deep
|
||||
#6200EA, //deep-purple-deep
|
||||
#FFD600, //yellow-deep
|
||||
#00B8D4, //cyan-deep
|
||||
#FFAB00, //amber-deep
|
||||
#DD2C00, //deep-orange-deep
|
||||
#2979FF, //blue-med
|
||||
#D500F9, //purple-med
|
||||
#00E676, //green-med
|
||||
#3D5AFE, //indigo-med
|
||||
#00B0FF, //light-blue-med
|
||||
#FF9100, //orange-med
|
||||
#1DE9B6, //teal-med
|
||||
#F50057, //pink-med
|
||||
#76FF03, //light-green-med
|
||||
#651FFF, //deep-purple-med
|
||||
#FFEA00, //yellow-med
|
||||
#00E5FF, //cyan-med
|
||||
#FFC400, //amber-med
|
||||
#FF3D00, //deep-orange-med
|
||||
#448AFF, //blue-light
|
||||
#E040FB, //purple-light
|
||||
#69F0AE, //green-light
|
||||
#536DFE, //indigo-light
|
||||
#40C4FF, //light-blue-light
|
||||
#FFAB40, //orange-light
|
||||
#64FFDA, //teal-light
|
||||
#FF4081, //pink-light
|
||||
#B2FF59, //light-green-light
|
||||
#7C4DFF, //deep-purple-light
|
||||
#FFFF00, //yellow-light
|
||||
#18FFFF, //cyan-light
|
||||
#FFD740, //amber-light
|
||||
#FF6E40; //deep-orange-light
|
||||
|
||||
|
||||
.dark-overlay {
|
||||
background-color: #292935;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.light-overlay {
|
||||
background-color: #FFFFFF;
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
|
||||
// #D50000, //red-deep
|
||||
// #FF1744, //red-med
|
||||
// #FF5252, //red-light
|
||||
|
||||
@warningColor: #FF9800;
|
||||
@criticalColor: #F44336;
|
||||
|
||||
@freeColor: #2E2E3B;
|
||||
@errorColor: #FF1744;
|
||||
@waitingColor: #DAD462;
|
||||
|
||||
.getColorIndex(@i){
|
||||
@index: @i; //mod(@i - 1, 3) * 7 + round((@i - 1) / 3 - .3);
|
||||
}
|
||||
.creatcolorclasses(@i:1) when(@i <= length(@color-codes)) {
|
||||
|
||||
.getColorIndex(@i);
|
||||
.color-@{index} {
|
||||
background-color: extract(@color-codes, @i);
|
||||
// border-color: lighten(extract(@color-codes, @i), 20%);
|
||||
// border-width: 2px;
|
||||
// border-style: solid;
|
||||
fill: extract(@color-codes, @i);
|
||||
stroke: extract(@color-codes, @i);
|
||||
}
|
||||
|
||||
md-grid-list.list-color-@{i} md-grid-tile.colored {
|
||||
background-color: extract(@color-codes, @i);
|
||||
// border-color: darken(extract(@color-codes, @i), 10%);
|
||||
// border-width: 1px;
|
||||
// border-style: solid;
|
||||
}
|
||||
.creatcolorclasses((@i + 1));
|
||||
}
|
||||
.creatcolorclasses();
|
||||
|
||||
.color-warning {
|
||||
background-color: @warningColor !important;
|
||||
border-color: @warningColor !important;
|
||||
fill: @warningColor !important;
|
||||
stroke: @warningColor !important;
|
||||
}
|
||||
|
||||
.color-critical {
|
||||
background-color: @criticalColor !important;
|
||||
border-color: @criticalColor !important;
|
||||
fill: @criticalColor !important;
|
||||
stroke: @criticalColor !important;
|
||||
}
|
||||
|
||||
.status-waiting {
|
||||
background-color: @freeColor !important;
|
||||
border-color: @waitingColor !important;
|
||||
border-width: 2px !important;
|
||||
border-style: solid !important;
|
||||
}
|
||||
|
||||
.status-succeeded {}
|
||||
|
||||
.status-terminated, .status-unknown {
|
||||
background-color: @errorColor !important;
|
||||
border-color: darken(@errorColor, 10%) !important;
|
||||
border-width: 1px !important;
|
||||
border-style: solid !important;
|
||||
}
|
158
www/master/components/dashboard/less/dashboard/groups.less
Normal file
@ -0,0 +1,158 @@
|
||||
.groups {
|
||||
font-size: 13px;
|
||||
|
||||
.header {
|
||||
line-height: 21px;
|
||||
a {
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.selector-area {
|
||||
|
||||
.filter-area {
|
||||
// font-size: 15px;
|
||||
// padding-left: 20px;
|
||||
}
|
||||
|
||||
.filter-label {
|
||||
|
||||
}
|
||||
|
||||
.filter-text {
|
||||
font-size: 13px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.cancel-button {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
padding: 0;
|
||||
|
||||
&:focus, &:hover {
|
||||
background-color: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.cancel-icon {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
fill: #777777;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.group-heading {
|
||||
|
||||
.label {
|
||||
// font-size: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
.select-group-by {
|
||||
min-width: 110px;
|
||||
margin-left: 10px;
|
||||
margin-right: 40px;
|
||||
|
||||
.md-select-label {
|
||||
padding-top: 6px;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
.group-item {
|
||||
padding-top: 20px;
|
||||
|
||||
.filter-button {
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
|
||||
.filter-icon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
// fill: #777777;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.icon-area {
|
||||
// padding-right: 10px;
|
||||
min-width: 34px;
|
||||
.group-icon {
|
||||
border-radius: 21px;
|
||||
width: 21px;
|
||||
height: 21px;
|
||||
}
|
||||
}
|
||||
|
||||
.group-main-area {
|
||||
// padding-bottom: 10px;
|
||||
|
||||
.subtype {
|
||||
line-height: 21px;
|
||||
}
|
||||
}
|
||||
|
||||
md-divider {
|
||||
margin-top: 40px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.group-name {
|
||||
padding-top: 10px;
|
||||
}
|
||||
.selectFilter {
|
||||
padding-top: 10px;
|
||||
margin-right: 30px;
|
||||
|
||||
.md-select-label {
|
||||
border-bottom: none !important;
|
||||
width: 17px;
|
||||
min-width: 17px;
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
md-select-menu {
|
||||
min-height: 40px;
|
||||
max-height: 40px;
|
||||
}
|
||||
|
||||
|
||||
.group-link-area {
|
||||
padding-left: 15px;
|
||||
padding-bottom: 15px;
|
||||
|
||||
button {
|
||||
line-height: 12px;
|
||||
// line-height: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.group-type-circle {
|
||||
width: 21px;
|
||||
height: 21px;
|
||||
}
|
||||
.group-type-circle span {
|
||||
|
||||
}
|
||||
// .group-type-area .pod-color
|
||||
// {
|
||||
// background-color: rgb(57,73,171);
|
||||
// }
|
||||
// .group-type-area .replicationController-color
|
||||
// {
|
||||
// background-color: rgb(57,73,171);
|
||||
// }
|
||||
|
||||
// .group-type-area .service-color
|
||||
// {
|
||||
// background-color: rgb(57,73,171);
|
||||
// }
|
||||
|
||||
md-select {
|
||||
margin-top: 0px;
|
||||
}
|
||||
}
|
79
www/master/components/dashboard/less/dashboard/pods.less
Normal file
@ -0,0 +1,79 @@
|
||||
.clear-bg {
|
||||
background-color: transparent;
|
||||
}
|
||||
.list-pods {
|
||||
.pod-group {
|
||||
margin: 25px;
|
||||
|
||||
md-grid-list {
|
||||
margin-top: 50px;
|
||||
color: white;
|
||||
|
||||
// Header and footer
|
||||
figcaption {
|
||||
width:100%;
|
||||
}
|
||||
|
||||
md-grid-tile-header {
|
||||
padding-left: 10px;
|
||||
.labels {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
md-grid-tile {
|
||||
transition: all 700ms ease-in 50ms;
|
||||
}
|
||||
|
||||
|
||||
// inner body
|
||||
.inner-box {
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
// Footer
|
||||
md-grid-tile-footer {
|
||||
|
||||
background: rgba(0, 0, 0, 0.50);
|
||||
// height: 36px;
|
||||
|
||||
.pod-title
|
||||
{
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.pod-host {
|
||||
text-align: right;
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
// Restart button
|
||||
.restarts {
|
||||
width: 100%;
|
||||
text-align: right;
|
||||
padding-right: 10px;
|
||||
|
||||
.restart-button {
|
||||
&, &:not([disabled]):hover, &:not([disabled]):focus, &:hover, &:focus {
|
||||
background-color: @errorColor;
|
||||
width:30px;
|
||||
height:30px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.gray {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
@import (multiple) "colors";
|
||||
}
|
125
www/master/components/dashboard/less/dashboard/servers.less
Normal file
@ -0,0 +1,125 @@
|
||||
.server-overview {
|
||||
@import (multiple) "colors";
|
||||
|
||||
.color-1 {
|
||||
background-color: #512DA8;
|
||||
border-color: #512DA8;
|
||||
fill: #512DA8;
|
||||
stroke: #512DA8;
|
||||
}
|
||||
|
||||
.color-2 {
|
||||
background-color: #9C27B0;
|
||||
border-color: #9C27B0;
|
||||
fill: #9C27B0;
|
||||
stroke: #9C27B0;
|
||||
}
|
||||
|
||||
.color-3 {
|
||||
background-color: #00BCD4;
|
||||
border-color: #00BCD4;
|
||||
fill: #00BCD4;
|
||||
stroke: #00BCD4;
|
||||
}
|
||||
|
||||
.color-max-1 {
|
||||
background-color: lighten(#512DA8, 40%);
|
||||
border-color: lighten(#512DA8, 40%);
|
||||
fill: lighten(#512DA8, 40%);
|
||||
// stroke: lighten(#512DA8, 40%);
|
||||
}
|
||||
|
||||
.color-max-2 {
|
||||
background-color: lighten(#9C27B0, 40%);
|
||||
border-color: lighten(#9C27B0, 40%);
|
||||
fill: lighten(#9C27B0, 40%);
|
||||
// stroke: lighten(#9C27B0, 40%);
|
||||
}
|
||||
|
||||
.color-max-3 {
|
||||
background-color: lighten(#00BCD4, 40%);
|
||||
border-color: lighten(#00BCD4, 40%);
|
||||
fill: lighten(#00BCD4, 40%);
|
||||
// stroke: lighten(#00BCD4, 40%);
|
||||
}
|
||||
|
||||
.color-max-warning {
|
||||
background-color: lighten(@warningColor, 30%) !important;
|
||||
border-color: lighten(@warningColor, 30%) !important;
|
||||
fill: lighten(@warningColor, 30%) !important;
|
||||
// stroke: lighten(@warningColor, 30%) !important;
|
||||
}
|
||||
|
||||
.color-max-critical {
|
||||
background-color: lighten(@criticalColor, 30%) !important;
|
||||
border-color: lighten(@criticalColor, 30%) !important;
|
||||
fill: lighten(@criticalColor, 30%) !important;
|
||||
// stroke: lighten(@criticalColor, 30%) !important;
|
||||
}
|
||||
|
||||
.max_tick_arc {
|
||||
stroke: #FFF !important;
|
||||
}
|
||||
|
||||
.concentric {
|
||||
// float:left;
|
||||
// border: 0px; /* solid black; */
|
||||
}
|
||||
// Chart
|
||||
.concentricchart {
|
||||
|
||||
.bg-circle {
|
||||
background: #F9F9F9;
|
||||
fill: #F9F9F9;
|
||||
stroke: #FFFFFF;
|
||||
stroke-width: 1px;
|
||||
}
|
||||
path {
|
||||
// stroke: #FFFFFF;
|
||||
// stroke-width: 1px;
|
||||
// opacity:0.8;
|
||||
}
|
||||
text {
|
||||
font-size:12px;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
}
|
||||
|
||||
.value_group {
|
||||
fill: white;
|
||||
}
|
||||
|
||||
.legend_group rect {
|
||||
// stroke: #cccccc;
|
||||
// stroke-width: 1px;
|
||||
opacity:0.8;
|
||||
}
|
||||
}
|
||||
|
||||
svg.legend {
|
||||
height: 115px;
|
||||
|
||||
text {
|
||||
font-size:12px;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
}
|
||||
|
||||
.hostName {
|
||||
font-size:16px;
|
||||
}
|
||||
|
||||
.rect {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.minion-name {
|
||||
text-align: center;
|
||||
vertical-align: bottom;
|
||||
width: 100%;
|
||||
}
|
||||
.chart_area {
|
||||
width: 325px;
|
||||
height: 425px;
|
||||
}
|
||||
}
|