aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKoren Lev <korenlev@gmail.com>2017-07-27 16:42:15 +0300
committerKoren Lev <korenlev@gmail.com>2017-07-27 16:42:15 +0300
commitb88c78e3cf2bef22aa2f1c4d0bf305e303bc15f0 (patch)
treeffa30a6e1511d72562d8772b8700cda52b2752a1
parentb70483739d1f6f4f0d31987ed2e4d1e30d71d579 (diff)
adding calipso ui
Change-Id: Ifa6f63daebb07f45580f747341960e898fdb00c4 Signed-off-by: Koren Lev <korenlev@gmail.com>
-rw-r--r--ui/Dockerfile2
-rw-r--r--ui/app.styl34
-rwxr-xr-xui/build-deploy22
-rwxr-xr-xui/build.sh20
-rw-r--r--ui/client/accounts-config.js11
-rw-r--r--ui/client/css/accordionNavMenu.styl259
-rw-r--r--ui/client/css/components.styl187
-rw-r--r--ui/client/css/d3graph.styl47
-rw-r--r--ui/client/css/flex.styl23
-rw-r--r--ui/client/css/header.styl118
-rw-r--r--ui/client/css/landing.css122
-rw-r--r--ui/client/css/loading.styl19
-rw-r--r--ui/client/css/modals.styl10
-rw-r--r--ui/client/css/plugins.styl4
-rw-r--r--ui/client/css/wizard.css28
-rw-r--r--ui/client/lib/jquery.multilevelpushmenu.css120
-rw-r--r--ui/client/lib/jquery.multilevelpushmenu.js1148
-rw-r--r--ui/client/main.html18
-rw-r--r--ui/client/main.js10
-rw-r--r--ui/client/main.styl85
-rw-r--r--ui/client/style/d3plusgraph.css14
-rw-r--r--ui/client/style/landingpage.css138
-rw-r--r--ui/client/style/multilevelorig.css43
-rw-r--r--ui/client/style/threeTest.css65
-rw-r--r--ui/client/style/threeTest2.css41
-rw-r--r--ui/client/templates/envdialog.html131
-rw-r--r--ui/client/templates/header.html13
-rw-r--r--ui/client/templates/mainPage.html207
-rw-r--r--ui/contributors.md6
-rw-r--r--ui/doc/tasks/refactor-tree-node-to-component.md13
-rw-r--r--ui/imports/api/accounts/methods.js196
-rw-r--r--ui/imports/api/accounts/server/publications.js29
-rw-r--r--ui/imports/api/attributes_for_hover_on_data/attributes_for_hover_on_data.js12
-rw-r--r--ui/imports/api/attributes_for_hover_on_data/methods.js8
-rw-r--r--ui/imports/api/attributes_for_hover_on_data/server/publications.js25
-rw-r--r--ui/imports/api/clique-constraints/clique-constraints.js48
-rw-r--r--ui/imports/api/clique-constraints/methods.js99
-rw-r--r--ui/imports/api/clique-constraints/server/publications.js30
-rw-r--r--ui/imports/api/clique-types/clique-types.js107
-rw-r--r--ui/imports/api/clique-types/methods.js108
-rw-r--r--ui/imports/api/clique-types/server/publications.js34
-rw-r--r--ui/imports/api/cliques/cliques.js12
-rw-r--r--ui/imports/api/cliques/methods.js8
-rw-r--r--ui/imports/api/cliques/server/publications.js33
-rw-r--r--ui/imports/api/constants/constants.js22
-rw-r--r--ui/imports/api/constants/data/distributions.js64
-rw-r--r--ui/imports/api/constants/data/env-types.js15
-rw-r--r--ui/imports/api/constants/data/environment-monitoring-types.js12
-rw-r--r--ui/imports/api/constants/data/environment-provision-types.js21
-rw-r--r--ui/imports/api/constants/data/log-levels.js27
-rw-r--r--ui/imports/api/constants/data/mechanism-drivers.js24
-rw-r--r--ui/imports/api/constants/data/message-source-systems.js15
-rw-r--r--ui/imports/api/constants/data/network-plugins.js15
-rw-r--r--ui/imports/api/constants/data/object-types-for-links.js39
-rw-r--r--ui/imports/api/constants/data/scans-statuses.js30
-rw-r--r--ui/imports/api/constants/data/type-drivers.js24
-rw-r--r--ui/imports/api/constants/server/publications.js16
-rw-r--r--ui/imports/api/environments/configuration-groups/aci-configuration.js29
-rw-r--r--ui/imports/api/environments/configuration-groups/amqp-configuration.js29
-rw-r--r--ui/imports/api/environments/configuration-groups/cli-configuration.js69
-rw-r--r--ui/imports/api/environments/configuration-groups/monitoring-configuration.js119
-rw-r--r--ui/imports/api/environments/configuration-groups/mysql-configuration.js33
-rw-r--r--ui/imports/api/environments/configuration-groups/nfv-provider-configuration.js25
-rw-r--r--ui/imports/api/environments/configuration-groups/open-stack-configuration.js30
-rw-r--r--ui/imports/api/environments/environments.js457
-rw-r--r--ui/imports/api/environments/methods.js154
-rw-r--r--ui/imports/api/environments/server/publications.js102
-rw-r--r--ui/imports/api/inventories/inventories.js11
-rw-r--r--ui/imports/api/inventories/server/methods.js151
-rw-r--r--ui/imports/api/inventories/server/publications.js250
-rw-r--r--ui/imports/api/link-types/link-types.js86
-rw-r--r--ui/imports/api/link-types/methods.js114
-rw-r--r--ui/imports/api/link-types/server/publications.js46
-rw-r--r--ui/imports/api/links/links.js11
-rw-r--r--ui/imports/api/links/methods.js8
-rw-r--r--ui/imports/api/links/server/publications.js32
-rw-r--r--ui/imports/api/messages/messages.js125
-rw-r--r--ui/imports/api/messages/methods.js8
-rw-r--r--ui/imports/api/messages/server/methods.js49
-rw-r--r--ui/imports/api/messages/server/publications.js98
-rw-r--r--ui/imports/api/migrations/migrations.js20
-rw-r--r--ui/imports/api/scans/methods.js55
-rw-r--r--ui/imports/api/scans/scans.js159
-rw-r--r--ui/imports/api/scans/server/methods.js44
-rw-r--r--ui/imports/api/scans/server/publications.js82
-rw-r--r--ui/imports/api/scheduled-scans/methods.js131
-rw-r--r--ui/imports/api/scheduled-scans/scheduled-scans.js91
-rw-r--r--ui/imports/api/scheduled-scans/server/methods.js27
-rw-r--r--ui/imports/api/scheduled-scans/server/publications.js60
-rw-r--r--ui/imports/api/simple-schema.init.js13
-rw-r--r--ui/imports/api/statistics/helpers.js64
-rw-r--r--ui/imports/api/statistics/methods.js159
-rw-r--r--ui/imports/api/statistics/server/publications.js52
-rw-r--r--ui/imports/api/statistics/statistics.js14
-rw-r--r--ui/imports/api/supported_environments/methods.js8
-rw-r--r--ui/imports/api/supported_environments/server/publications.js17
-rw-r--r--ui/imports/api/supported_environments/supported_environments.js49
-rw-r--r--ui/imports/index.styl1
-rw-r--r--ui/imports/lib/d3-graph.js573
-rw-r--r--ui/imports/lib/d3three.js789
-rw-r--r--ui/imports/lib/general-regex.js15
-rw-r--r--ui/imports/lib/icon.js14
-rw-r--r--ui/imports/lib/images-for-node-type.js22
-rw-r--r--ui/imports/lib/regex-utils.js11
-rw-r--r--ui/imports/lib/simple-schema-utils.js15
-rw-r--r--ui/imports/lib/utilities.js54
-rw-r--r--ui/imports/startup/both/config.js9
-rw-r--r--ui/imports/startup/both/configs/accounts.js9
-rw-r--r--ui/imports/startup/both/index.js10
-rw-r--r--ui/imports/startup/client/index.js38
-rw-r--r--ui/imports/startup/client/template-helpers.js32
-rw-r--r--ui/imports/startup/server/config.js9
-rw-r--r--ui/imports/startup/server/configs/accounts.js16
-rw-r--r--ui/imports/startup/server/index.js13
-rw-r--r--ui/imports/startup/server/register-api.js56
-rw-r--r--ui/imports/startup/server/seeds.js10
-rw-r--r--ui/imports/startup/server/seeds/constants.js68
-rw-r--r--ui/imports/startup/server/seeds/users.js51
-rw-r--r--ui/imports/ui/actions/environment-panel.actions.js225
-rw-r--r--ui/imports/ui/actions/graph-tooltip-window.actions.js30
-rw-r--r--ui/imports/ui/actions/main-app.actions.js21
-rw-r--r--ui/imports/ui/actions/navigation.js84
-rw-r--r--ui/imports/ui/actions/search-interested-parties.js93
-rw-r--r--ui/imports/ui/actions/tree-node.actions.js144
-rw-r--r--ui/imports/ui/actions/vedge-info-window.actions.js41
-rw-r--r--ui/imports/ui/components/accordion-nav-menu/accordion-nav-menu.html43
-rw-r--r--ui/imports/ui/components/accordion-nav-menu/accordion-nav-menu.js155
-rw-r--r--ui/imports/ui/components/accordion-nav-menu/accordion-nav-menu.styl11
-rw-r--r--ui/imports/ui/components/accordion-wiki-menu/accordion-wiki-menu.html42
-rw-r--r--ui/imports/ui/components/accordion-wiki-menu/accordion-wiki-menu.js147
-rw-r--r--ui/imports/ui/components/accordionTreeNode/accordion-tree-node.styl5
-rw-r--r--ui/imports/ui/components/accordionTreeNode/accordionTreeNode.html54
-rw-r--r--ui/imports/ui/components/accordionTreeNode/accordionTreeNode.js284
-rw-r--r--ui/imports/ui/components/accordionTreeNodeChildren/accordionTreeNodeChildren.html19
-rw-r--r--ui/imports/ui/components/accordionTreeNodeChildren/accordionTreeNodeChildren.js125
-rw-r--r--ui/imports/ui/components/aggregate-dashboard/aggregate-dashboard.html35
-rw-r--r--ui/imports/ui/components/aggregate-dashboard/aggregate-dashboard.js212
-rw-r--r--ui/imports/ui/components/aggregate-dashboard/aggregate-dashboard.styl10
-rw-r--r--ui/imports/ui/components/alarm-icons/alarm-icons.html78
-rw-r--r--ui/imports/ui/components/alarm-icons/alarm-icons.js53
-rw-r--r--ui/imports/ui/components/alarm-icons/alarm-icons.styl1
-rw-r--r--ui/imports/ui/components/auto-search-result-line/auto-search-result-line.html16
-rw-r--r--ui/imports/ui/components/auto-search-result-line/auto-search-result-line.js51
-rw-r--r--ui/imports/ui/components/auto-search-result-line/auto-search-result-line.styl23
-rw-r--r--ui/imports/ui/components/breadcrumb/breadcrumb.html17
-rw-r--r--ui/imports/ui/components/breadcrumb/breadcrumb.js83
-rw-r--r--ui/imports/ui/components/breadcrumb/breadcrumb.styl3
-rw-r--r--ui/imports/ui/components/breadcrumbNode/breadcrumbNode.html15
-rw-r--r--ui/imports/ui/components/breadcrumbNode/breadcrumbNode.js43
-rw-r--r--ui/imports/ui/components/breadcrumbNode/breadcrumbNode.styl4
-rw-r--r--ui/imports/ui/components/clique-constraint/clique-constraint.html96
-rw-r--r--ui/imports/ui/components/clique-constraint/clique-constraint.js329
-rw-r--r--ui/imports/ui/components/clique-constraint/clique-constraint.styl35
-rw-r--r--ui/imports/ui/components/clique-constraints-list/clique-constraints-list.html52
-rw-r--r--ui/imports/ui/components/clique-constraints-list/clique-constraints-list.js77
-rw-r--r--ui/imports/ui/components/clique-constraints-list/clique-constraints-list.styl22
-rw-r--r--ui/imports/ui/components/clique-type/clique-type.html100
-rw-r--r--ui/imports/ui/components/clique-type/clique-type.js371
-rw-r--r--ui/imports/ui/components/clique-type/clique-type.styl54
-rw-r--r--ui/imports/ui/components/clique-types-list/clique-types-list.html56
-rw-r--r--ui/imports/ui/components/clique-types-list/clique-types-list.js82
-rw-r--r--ui/imports/ui/components/clique-types-list/clique-types-list.styl22
-rw-r--r--ui/imports/ui/components/d3graph/d3graph.html13
-rw-r--r--ui/imports/ui/components/d3graph/d3graph.js126
-rw-r--r--ui/imports/ui/components/d3graph/d3graph.styl12
-rw-r--r--ui/imports/ui/components/dashboard/dashboard.html157
-rw-r--r--ui/imports/ui/components/dashboard/dashboard.js222
-rw-r--r--ui/imports/ui/components/dashboard/dashboard.styl0
-rw-r--r--ui/imports/ui/components/data-cubic/data-cubic.html23
-rw-r--r--ui/imports/ui/components/data-cubic/data-cubic.js71
-rw-r--r--ui/imports/ui/components/data-cubic/data-cubic.styl27
-rw-r--r--ui/imports/ui/components/detailed-node-info-box/detailed-node-info-box.html53
-rw-r--r--ui/imports/ui/components/detailed-node-info-box/detailed-node-info-box.js57
-rw-r--r--ui/imports/ui/components/detailed-node-info-box/detailed-node-info-box.styl33
-rw-r--r--ui/imports/ui/components/env-aci-info/env-aci-info.html88
-rw-r--r--ui/imports/ui/components/env-aci-info/env-aci-info.js61
-rw-r--r--ui/imports/ui/components/env-amqp-credentials-info/env-amqp-credentials-info.html108
-rw-r--r--ui/imports/ui/components/env-amqp-credentials-info/env-amqp-credentials-info.js52
-rw-r--r--ui/imports/ui/components/env-delete-modal/env-delete-modal.html48
-rw-r--r--ui/imports/ui/components/env-delete-modal/env-delete-modal.js55
-rw-r--r--ui/imports/ui/components/env-delete-modal/env-delete-modal.styl2
-rw-r--r--ui/imports/ui/components/env-form/env-form.html37
-rw-r--r--ui/imports/ui/components/env-form/env-form.js94
-rw-r--r--ui/imports/ui/components/env-form/env-form.styl0
-rw-r--r--ui/imports/ui/components/env-main-info/env-main-info.html193
-rw-r--r--ui/imports/ui/components/env-main-info/env-main-info.js123
-rw-r--r--ui/imports/ui/components/env-master-host-credentials-info/env-master-host-credentials-info.html109
-rw-r--r--ui/imports/ui/components/env-master-host-credentials-info/env-master-host-credentials-info.js52
-rw-r--r--ui/imports/ui/components/env-monitoring-info/env-monitoring-info.html283
-rw-r--r--ui/imports/ui/components/env-monitoring-info/env-monitoring-info.js76
-rw-r--r--ui/imports/ui/components/env-nfv-info/env-nfv-info.html128
-rw-r--r--ui/imports/ui/components/env-nfv-info/env-nfv-info.js63
-rw-r--r--ui/imports/ui/components/env-open-stack-db-credentials-info/env-open-stack-db-credentials-info.html109
-rw-r--r--ui/imports/ui/components/env-open-stack-db-credentials-info/env-open-stack-db-credentials-info.js52
-rw-r--r--ui/imports/ui/components/env-os-api-endpoint-info/env-os-api-endpoint-info.html125
-rw-r--r--ui/imports/ui/components/env-os-api-endpoint-info/env-os-api-endpoint-info.js52
-rw-r--r--ui/imports/ui/components/environment-dashboard/environment-dashboard.html59
-rw-r--r--ui/imports/ui/components/environment-dashboard/environment-dashboard.js380
-rw-r--r--ui/imports/ui/components/environment-dashboard/environment-dashboard.styl12
-rw-r--r--ui/imports/ui/components/environment-wizard/environment-wizard.html83
-rw-r--r--ui/imports/ui/components/environment-wizard/environment-wizard.js452
-rw-r--r--ui/imports/ui/components/environment-wizard/environment-wizard.styl27
-rw-r--r--ui/imports/ui/components/environment/environment.html63
-rw-r--r--ui/imports/ui/components/environment/environment.js570
-rw-r--r--ui/imports/ui/components/environment/environment.styl61
-rw-r--r--ui/imports/ui/components/flow-graph/flow-graph.html17
-rw-r--r--ui/imports/ui/components/flow-graph/flow-graph.js383
-rw-r--r--ui/imports/ui/components/flow-graph/flow-graph.styl18
-rw-r--r--ui/imports/ui/components/general-folder-node-dashboard/general-folder-node-dashboard.html24
-rw-r--r--ui/imports/ui/components/general-folder-node-dashboard/general-folder-node-dashboard.js112
-rw-r--r--ui/imports/ui/components/general-folder-node-dashboard/general-folder-node-dashboard.styl12
-rw-r--r--ui/imports/ui/components/general-node-dashboard/general-node-dashboard.html17
-rw-r--r--ui/imports/ui/components/general-node-dashboard/general-node-dashboard.js84
-rw-r--r--ui/imports/ui/components/general-node-dashboard/general-node-dashboard.styl6
-rw-r--r--ui/imports/ui/components/general-node-info-box/general-node-info-box.html37
-rw-r--r--ui/imports/ui/components/general-node-info-box/general-node-info-box.js63
-rw-r--r--ui/imports/ui/components/general-node-info-box/general-node-info-box.styl33
-rw-r--r--ui/imports/ui/components/get-started/get-started.html412
-rw-r--r--ui/imports/ui/components/get-started/get-started.js32
-rw-r--r--ui/imports/ui/components/graph-tooltip-window/graph-tooltip-window.html17
-rw-r--r--ui/imports/ui/components/graph-tooltip-window/graph-tooltip-window.js57
-rw-r--r--ui/imports/ui/components/graph-tooltip-window/graph-tooltip-window.styl25
-rw-r--r--ui/imports/ui/components/host-dashboard/host-dashboard.html29
-rw-r--r--ui/imports/ui/components/host-dashboard/host-dashboard.js197
-rw-r--r--ui/imports/ui/components/host-dashboard/host-dashboard.styl6
-rw-r--r--ui/imports/ui/components/icon/icon.html18
-rw-r--r--ui/imports/ui/components/icon/icon.js48
-rw-r--r--ui/imports/ui/components/icon/icon.styl2
-rw-r--r--ui/imports/ui/components/index.styl57
-rw-r--r--ui/imports/ui/components/input-model/input-model.html21
-rw-r--r--ui/imports/ui/components/input-model/input-model.js116
-rw-r--r--ui/imports/ui/components/inventory-properties-display/inventory-properties-display.html13
-rw-r--r--ui/imports/ui/components/inventory-properties-display/inventory-properties-display.js90
-rw-r--r--ui/imports/ui/components/inventory-properties-display/inventory-properties-display.styl2
-rw-r--r--ui/imports/ui/components/landing/landing.html201
-rw-r--r--ui/imports/ui/components/landing/landing.js35
-rw-r--r--ui/imports/ui/components/landing/landing.styl80
-rw-r--r--ui/imports/ui/components/link-type/link-type.html88
-rw-r--r--ui/imports/ui/components/link-type/link-type.js328
-rw-r--r--ui/imports/ui/components/link-type/link-type.styl34
-rw-r--r--ui/imports/ui/components/link-types-list/link-types-list.html56
-rw-r--r--ui/imports/ui/components/link-types-list/link-types-list.js87
-rw-r--r--ui/imports/ui/components/link-types-list/link-types-list.styl23
-rw-r--r--ui/imports/ui/components/list-info-box/list-info-box.html60
-rw-r--r--ui/imports/ui/components/list-info-box/list-info-box.js111
-rw-r--r--ui/imports/ui/components/list-info-box/list-info-box.styl4
-rw-r--r--ui/imports/ui/components/loading/loading.html12
-rw-r--r--ui/imports/ui/components/loading/loading.js30
-rw-r--r--ui/imports/ui/components/loading/loading.styl0
-rw-r--r--ui/imports/ui/components/main/main.html15
-rw-r--r--ui/imports/ui/components/main/main.js98
-rw-r--r--ui/imports/ui/components/main/main.styl0
-rw-r--r--ui/imports/ui/components/message/message.html168
-rw-r--r--ui/imports/ui/components/message/message.js257
-rw-r--r--ui/imports/ui/components/message/message.styl41
-rw-r--r--ui/imports/ui/components/messages-info-box/messages-info-box.html27
-rw-r--r--ui/imports/ui/components/messages-info-box/messages-info-box.js66
-rw-r--r--ui/imports/ui/components/messages-info-box/messages-info-box.styl2
-rw-r--r--ui/imports/ui/components/messages-list/messages-list.html103
-rw-r--r--ui/imports/ui/components/messages-list/messages-list.js291
-rw-r--r--ui/imports/ui/components/messages-list/messages-list.styl37
-rw-r--r--ui/imports/ui/components/messages-modal/messages-modal.html78
-rw-r--r--ui/imports/ui/components/messages-modal/messages-modal.js285
-rw-r--r--ui/imports/ui/components/messages-modal/messages-modal.styl18
-rw-r--r--ui/imports/ui/components/mt-input/mt-input.html7
-rw-r--r--ui/imports/ui/components/mt-input/mt-input.js114
-rw-r--r--ui/imports/ui/components/mt-input/mt-input.styl2
-rw-r--r--ui/imports/ui/components/mt-radios/mt-radios.html22
-rw-r--r--ui/imports/ui/components/mt-radios/mt-radios.js70
-rw-r--r--ui/imports/ui/components/mt-radios/mt-radios.styl2
-rw-r--r--ui/imports/ui/components/mt-select/mt-select.html13
-rw-r--r--ui/imports/ui/components/mt-select/mt-select.js99
-rw-r--r--ui/imports/ui/components/mt-select/mt-select.styl2
-rw-r--r--ui/imports/ui/components/network-graph-manager/network-graph-manager.html5
-rw-r--r--ui/imports/ui/components/network-graph-manager/network-graph-manager.js282
-rw-r--r--ui/imports/ui/components/network-graph-manager/network-graph-manager.styl2
-rw-r--r--ui/imports/ui/components/network-graph/network-graph.html7
-rw-r--r--ui/imports/ui/components/network-graph/network-graph.js697
-rw-r--r--ui/imports/ui/components/network-graph/network-graph.styl20
-rw-r--r--ui/imports/ui/components/network-info-box/network-info-box.html38
-rw-r--r--ui/imports/ui/components/network-info-box/network-info-box.js69
-rw-r--r--ui/imports/ui/components/network-info-box/network-info-box.styl2
-rw-r--r--ui/imports/ui/components/new-scanning/new-scanning.html53
-rw-r--r--ui/imports/ui/components/new-scanning/new-scanning.js73
-rw-r--r--ui/imports/ui/components/new-scanning/new-scanning.styl7
-rw-r--r--ui/imports/ui/components/pager/pager.html42
-rw-r--r--ui/imports/ui/components/pager/pager.js123
-rw-r--r--ui/imports/ui/components/pager/pager.styl4
-rw-r--r--ui/imports/ui/components/project-dashboard/project-dashboard.html38
-rw-r--r--ui/imports/ui/components/project-dashboard/project-dashboard.js149
-rw-r--r--ui/imports/ui/components/project-dashboard/project-dashboard.styl14
-rw-r--r--ui/imports/ui/components/region-dashboard/region-dashboard.html38
-rw-r--r--ui/imports/ui/components/region-dashboard/region-dashboard.js220
-rw-r--r--ui/imports/ui/components/region-dashboard/region-dashboard.styl14
-rw-r--r--ui/imports/ui/components/scanning-request/scanning-request.html74
-rw-r--r--ui/imports/ui/components/scanning-request/scanning-request.js371
-rw-r--r--ui/imports/ui/components/scanning-request/scanning-request.styl7
-rw-r--r--ui/imports/ui/components/scans-list/scans-list.html88
-rw-r--r--ui/imports/ui/components/scans-list/scans-list.js224
-rw-r--r--ui/imports/ui/components/scans-list/scans-list.styl33
-rw-r--r--ui/imports/ui/components/scheduled-scan/scheduled-scan.html116
-rw-r--r--ui/imports/ui/components/scheduled-scan/scheduled-scan.js511
-rw-r--r--ui/imports/ui/components/scheduled-scan/scheduled-scan.styl34
-rw-r--r--ui/imports/ui/components/scheduled-scans-list/scheduled-scans-list.html66
-rw-r--r--ui/imports/ui/components/scheduled-scans-list/scheduled-scans-list.js160
-rw-r--r--ui/imports/ui/components/scheduled-scans-list/scheduled-scans-list.styl33
-rw-r--r--ui/imports/ui/components/search-auto-complete-list/search-auto-complete-list.html22
-rw-r--r--ui/imports/ui/components/search-auto-complete-list/search-auto-complete-list.js167
-rw-r--r--ui/imports/ui/components/search-auto-complete-list/search-auto-complete.styl28
-rw-r--r--ui/imports/ui/components/select-model/select-model.html23
-rw-r--r--ui/imports/ui/components/select-model/select-model.js79
-rw-r--r--ui/imports/ui/components/selectable-ordered-input/selectable-ordered-input.html29
-rw-r--r--ui/imports/ui/components/selectable-ordered-input/selectable-ordered-input.js243
-rw-r--r--ui/imports/ui/components/selectable-ordered-input/selectable-ordered-input.styl30
-rw-r--r--ui/imports/ui/components/time-selection-widget/time-selection-widget.html33
-rw-r--r--ui/imports/ui/components/time-selection-widget/time-selection-widget.js45
-rw-r--r--ui/imports/ui/components/time-selection-widget/time-selection-widget.styl5
-rw-r--r--ui/imports/ui/components/top-navbar-menu/top-navbar-menu.html59
-rw-r--r--ui/imports/ui/components/top-navbar-menu/top-navbar-menu.js129
-rw-r--r--ui/imports/ui/components/top-navbar-menu/top-navbar-menu.styl23
-rw-r--r--ui/imports/ui/components/tree-node/tree-node.html58
-rw-r--r--ui/imports/ui/components/tree-node/tree-node.js419
-rw-r--r--ui/imports/ui/components/tree-node/tree-node.styl53
-rw-r--r--ui/imports/ui/components/user-list/user-list.html53
-rw-r--r--ui/imports/ui/components/user-list/user-list.js74
-rw-r--r--ui/imports/ui/components/user-list/user-list.styl22
-rw-r--r--ui/imports/ui/components/user/user.html111
-rw-r--r--ui/imports/ui/components/user/user.js366
-rw-r--r--ui/imports/ui/components/user/user.styl34
-rw-r--r--ui/imports/ui/components/vedge-info-window/vedge-info-window.html114
-rw-r--r--ui/imports/ui/components/vedge-info-window/vedge-info-window.js380
-rw-r--r--ui/imports/ui/components/vedge-info-window/vedge-info-window.styl43
-rw-r--r--ui/imports/ui/components/zone-dashboard/zone-dashboard.html35
-rw-r--r--ui/imports/ui/components/zone-dashboard/zone-dashboard.js214
-rw-r--r--ui/imports/ui/components/zone-dashboard/zone-dashboard.styl10
-rw-r--r--ui/imports/ui/index.styl1
-rw-r--r--ui/imports/ui/lib/environment-tree-node-behavior.js46
-rw-r--r--ui/imports/ui/lib/input-model.js31
-rw-r--r--ui/imports/ui/lib/inventory-tree-node-behavior.js66
-rw-r--r--ui/imports/ui/lib/select-model.js31
-rw-r--r--ui/imports/ui/reducers/environment-panel.reducer.js194
-rw-r--r--ui/imports/ui/reducers/graph-tooltip-window.reducer.js48
-rw-r--r--ui/imports/ui/reducers/i18n.reducer.js180
-rw-r--r--ui/imports/ui/reducers/index.js33
-rw-r--r--ui/imports/ui/reducers/main-app.reducer.js28
-rw-r--r--ui/imports/ui/reducers/navigation.js99
-rw-r--r--ui/imports/ui/reducers/search-interested-parties.js76
-rw-r--r--ui/imports/ui/reducers/tree-node.reducer.js232
-rw-r--r--ui/imports/ui/reducers/vedge-info-window.reducer.js50
-rw-r--r--ui/imports/ui/store/index.js11
-rw-r--r--ui/imports/ui/store/store.js25
-rw-r--r--ui/lib/router.js363
-rw-r--r--ui/osdna.js9
-rwxr-xr-xui/package-source.sh23
-rw-r--r--ui/package.json26
-rw-r--r--ui/public/cisco-favicon.icobin0 -> 38692 bytes
-rw-r--r--ui/public/cisco-logo-load.pngbin0 -> 4214 bytes
-rw-r--r--ui/public/dna-718905_600.pngbin0 -> 17620 bytes
-rw-r--r--ui/public/get-started/dashboard-notifications.pngbin0 -> 10832 bytes
-rw-r--r--ui/public/get-started/env-dashboard.pngbin0 -> 46839 bytes
-rw-r--r--ui/public/get-started/env-delete-action.pngbin0 -> 81457 bytes
-rw-r--r--ui/public/get-started/env-diting-os-db-credentails.pngbin0 -> 65797 bytes
-rw-r--r--ui/public/get-started/env-edit-action.pngbin0 -> 81503 bytes
-rw-r--r--ui/public/get-started/env-editing-amqp-credentials.pngbin0 -> 64211 bytes
-rw-r--r--ui/public/get-started/env-editing-master-host-credentials.pngbin0 -> 71760 bytes
-rw-r--r--ui/public/get-started/env-editing-monitoring.pngbin0 -> 117008 bytes
-rw-r--r--ui/public/get-started/env-editing-os-end-point.pngbin0 -> 45236 bytes
-rw-r--r--ui/public/get-started/env-editing-provision.pngbin0 -> 9517 bytes
-rw-r--r--ui/public/get-started/env-editing.pngbin0 -> 75945 bytes
-rw-r--r--ui/public/get-started/environments-pick.pngbin0 -> 92013 bytes
-rw-r--r--ui/public/get-started/main-screen-explained.pngbin0 -> 195772 bytes
-rw-r--r--ui/public/get-started/navbar-explained.pngbin0 -> 181291 bytes
-rw-r--r--ui/public/get-started/new-environment-action.pngbin0 -> 72093 bytes
-rw-r--r--ui/public/get-started/scan-action.pngbin0 -> 81481 bytes
-rw-r--r--ui/public/get-started/scan-request-screen.pngbin0 -> 25126 bytes
-rw-r--r--ui/public/get-started/selecting-dashboard.pngbin0 -> 91003 bytes
-rw-r--r--ui/public/get-started/selecting-env.pngbin0 -> 7762 bytes
-rw-r--r--ui/public/get-started/setting-action.pngbin0 -> 34477 bytes
-rw-r--r--ui/public/get-started/setting-scans-action.pngbin0 -> 125195 bytes
-rw-r--r--ui/public/ic_cloud_queue_black_48dp_2x.pngbin0 -> 9845 bytes
-rw-r--r--ui/public/ic_computer_black_48dp_2x.pngbin0 -> 5171 bytes
-rw-r--r--ui/public/ic_dns_black_48dp_2x.pngbin0 -> 8044 bytes
-rw-r--r--ui/public/ic_gamepad_black_48dp_2x.pngbin0 -> 5647 bytes
-rw-r--r--ui/public/ic_keyboard_return_black_48dp_2x.pngbin0 -> 5438 bytes
-rw-r--r--ui/public/ic_lens_black_48dp_2x.pngbin0 -> 8523 bytes
-rw-r--r--ui/public/ic_settings_input_composite_black_48dp_2x.pngbin0 -> 5389 bytes
-rw-r--r--ui/public/ic_settings_input_hdmi_black_48dp_2x.pngbin0 -> 5881 bytes
-rw-r--r--ui/public/ic_storage_black_48dp_2x.pngbin0 -> 4804 bytes
-rw-r--r--ui/public/images/calipso-logo.jpgbin0 -> 2895 bytes
-rw-r--r--ui/public/instance.pngbin0 -> 1191 bytes
-rw-r--r--ui/public/landing/calipso-logo.pngbin0 -> 22270 bytes
-rw-r--r--ui/public/landing/docker.pngbin0 -> 11495 bytes
-rw-r--r--ui/public/landing/eye.pngbin0 -> 33084 bytes
-rw-r--r--ui/public/landing/first.pngbin0 -> 47856 bytes
-rw-r--r--ui/public/landing/graph.pngbin0 -> 69443 bytes
-rw-r--r--ui/public/landing/main-screen.pngbin0 -> 48337 bytes
-rw-r--r--ui/public/landing/openstack.pngbin0 -> 8286 bytes
-rw-r--r--ui/public/logo-cisco-png.pngbin0 -> 23417 bytes
-rw-r--r--ui/public/logo-cisco.svg37
-rw-r--r--ui/public/logo_microscope_3.pngbin0 -> 17774 bytes
-rw-r--r--ui/public/network.pngbin0 -> 2506 bytes
-rw-r--r--ui/public/particle.pngbin0 -> 2924 bytes
-rw-r--r--ui/public/pnic.jpgbin0 -> 37828 bytes
-rw-r--r--ui/public/vconnector.pngbin0 -> 3464 bytes
-rw-r--r--ui/public/vnic.pngbin0 -> 4520 bytes
-rw-r--r--ui/public/vservice.pngbin0 -> 4431 bytes
-rw-r--r--ui/public/wiki/addNewEnv.pngbin0 -> 105144 bytes
-rw-r--r--ui/public/wiki/addNewEnv1.pngbin0 -> 160919 bytes
-rw-r--r--ui/public/wiki/wizard1.pngbin0 -> 46714 bytes
-rw-r--r--ui/public/wiki/wizard2.pngbin0 -> 53414 bytes
-rw-r--r--ui/public/wiki/wizard3.pngbin0 -> 62210 bytes
-rw-r--r--ui/public/wiki/wizard4.pngbin0 -> 54438 bytes
-rw-r--r--ui/readme.md43
-rwxr-xr-xui/run2
-rwxr-xr-xui/run-dev.sh10
-rw-r--r--ui/server/main.js10
416 files changed, 29904 insertions, 0 deletions
diff --git a/ui/Dockerfile b/ui/Dockerfile
new file mode 100644
index 0000000..1e3ba52
--- /dev/null
+++ b/ui/Dockerfile
@@ -0,0 +1,2 @@
+FROM meteorhacks/meteord:onbuild
+#FROM jshimko/meteor-launchpad:latest
diff --git a/ui/app.styl b/ui/app.styl
new file mode 100644
index 0000000..bdf9492
--- /dev/null
+++ b/ui/app.styl
@@ -0,0 +1,34 @@
+// Colors
+black = #222
+grey = #D8D8D8
+purp = #524763
+dark-purp = #373142
+red = #E54B4B
+teal = #82D8D8
+dark-teal = #66A6A6
+green = #CBE56C
+blue = #16528E
+bright-blue = #0082C8
+maroon = #6A2140
+light-red = #CA5765
+pink = #CF256D
+orange = #E8846B
+
+// cisco brand Colors
+brand-blue = #2196F3
+spark-blue = #0A9AD7
+spark-green = #73BE46
+spark-grey = #D1D2D1
+dk-blue = #303f9f
+light-grey = #efefef
+dk-gray1 = #58585b
+// cisco brand status Colors
+status-blue = #64bbe3
+status-turq = #14a792
+status-green = #6cc04a
+status-yellow = #ffcc00
+status-orange = #ff7300
+status-red = #cf2030
+// cisco accepteble colors
+
+@import './imports/ui/components/index';
diff --git a/ui/build-deploy b/ui/build-deploy
new file mode 100755
index 0000000..1f3713e
--- /dev/null
+++ b/ui/build-deploy
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+
+SECONDS=0
+OS_TARGET_PATH=".."
+OS_TARGET_NAME=osdna-meteor-frontend-$(date +%Y-%m-%d-%s).tar.gz
+OS_NAME=${PWD##*/}
+OS_USER_DEV="eyal"
+DEV_HOST="korlev-osdna-be.cisco.com"
+
+meteor build $OS_TARGET_PATH/ --architecture=os.linux.x86_64
+mv $OS_TARGET_PATH/$OS_NAME.tar.gz $OS_TARGET_PATH/$OS_TARGET_NAME
+
+duration=$SECONDS
+echo "$(($duration / 60)) minutes and $(($duration % 60)) seconds elapsed."
+
+echo "deploy to dev"
+SECONDS=0
+echo "target $OS_TARGET_PATH/$OS_TARGET_NAME"
+echo "user $OS_USER_DEV"
+echo "dev $DEV_HOST"
+scp $OS_TARGET_PATH/$OS_TARGET_NAME $OS_USER_DEV@$DEV_HOST\:\~
+echo "$(($duration / 60)) minutes and $(($duration % 60)) seconds elapsed."
diff --git a/ui/build.sh b/ui/build.sh
new file mode 100755
index 0000000..4e33f30
--- /dev/null
+++ b/ui/build.sh
@@ -0,0 +1,20 @@
+########################################################################################
+# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others #
+# #
+# All rights reserved. This program and the accompanying materials #
+# are made available under the terms of the Apache License, Version 2.0 #
+# which accompanies this distribution, and is available at #
+# http://www.apache.org/licenses/LICENSE-2.0 #
+########################################################################################
+#!/usr/bin/env bash
+
+SECONDS=0
+OS_TARGET_PATH=".."
+OS_TARGET_NAME=osdna-meteor-frontend-$(date +%Y-%m-%d-%s).tar.gz
+OS_NAME=${PWD##*/}
+
+meteor build $OS_TARGET_PATH/ --architecture=os.linux.x86_64
+mv $OS_TARGET_PATH/$OS_NAME.tar.gz $OS_TARGET_PATH/$OS_TARGET_NAME
+
+duration=$SECONDS
+echo "$(($duration / 60)) minutes and $(($duration % 60)) seconds elapsed."
diff --git a/ui/client/accounts-config.js b/ui/client/accounts-config.js
new file mode 100644
index 0000000..68901ea
--- /dev/null
+++ b/ui/client/accounts-config.js
@@ -0,0 +1,11 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+Accounts.ui.config({
+ passwordSignupFields: 'USERNAME_ONLY',
+}); \ No newline at end of file
diff --git a/ui/client/css/accordionNavMenu.styl b/ui/client/css/accordionNavMenu.styl
new file mode 100644
index 0000000..b5ba1d5
--- /dev/null
+++ b/ui/client/css/accordionNavMenu.styl
@@ -0,0 +1,259 @@
+// for WIKI
+
+.os-accordion-nav-menu
+ .sm-menu-items-list
+ >.os-accordion-tree-node
+ >a
+ display none
+
+.left-nav-content-wiki {
+ width: 300px;
+ height: 100vh
+ background brand-blue
+ top 90px
+ position fixed
+}
+
+
+.left-nav-menu
+ float: left;
+ min-width: 100px;
+ outline: 0;
+ position: relative;
+ //margin-top -40px
+ shadow-level(1)
+ -moz-box-sizing: border-box;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+ outline: 0;
+ .menu-footer, .menu-header
+ background: brand-blue;
+ color: #f0f0f0;
+ float: left;
+ font-weight: normal;
+ line-height: 50px;
+ font-size: 0.8em;
+ width: 100%;
+ border-bottom 1px solid black
+ .menu-footer
+ text-align: center;
+ .menu-header
+ height: 39px;
+ p
+ font-size 1.7em
+ padding 6px
+ margin 0px
+
+
+ i
+ cursor pointer
+ position absolute
+ top -5px
+ right 0
+ display block
+ color white
+ padding-right 5px
+ trans()
+ &:hover
+ color spark-blue
+ opacity 0.8
+
+ ul
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ li
+ display: block;
+ float: left;
+ position: relative;
+ width: 100%;
+ .menu-label
+ background: #f0f0f0;
+ border-radius: 100%;
+ color: #555555;
+ font-size: 11px;
+ font-weight: 800;
+ line-height: 18px;
+ min-width: 20px;
+ padding: 1px 2px 1px 1px;
+ position: absolute;
+ right: 18px;
+ text-align: center;
+ top: 14px;
+ a
+ background: brand-blue;
+ color: #f0f0f0;
+ float: left;
+ font-size: 13px;
+ overflow: hidden;
+ padding: 14px 22px;
+ position: relative;
+ text-decoration: none;
+ white-space: nowrap;
+ width: 100%;
+ trans()
+ &:hover
+ background: @background - 20%;
+ i
+ float: left;
+ font-size: 16px;
+ line-height: 18px;
+ text-align: left;
+ width: 34px;
+
+.left-nav-menu ul li .menu-label {
+ background: #f0f0f0;
+ border-radius: 100%;
+ color: #555555;
+ font-size: 11px;
+ font-weight: 800;
+ line-height: 18px;
+ min-width: 20px;
+ padding: 1px 2px 1px 1px;
+ position: absolute;
+ right: 18px;
+ text-align: center;
+ top: 14px;
+}
+
+.left-nav-menu
+ ul
+ .submenu
+ display: none;
+ position: static;
+ width: 100%;
+ .submenu-indicator
+ line-height: 16px
+ li
+ clear: both;
+ width: 100%;
+ ul.submenu
+ display: none;
+ position: static;
+ width: 100%;
+ overflow: hidden;
+ a
+ background: brand-blue - 10%
+ border-left: solid 6px transparent;
+ border-top: none;
+ float: left;
+ font-size: 11px;
+ position: relative;
+ width: 100%;
+ &:hover
+ background @background - 20%
+
+
+.left-nav-menu ul .submenu li .menu-label {
+ background: #f0f0f0;
+ border-radius: 100%;
+ color: #555555;
+ font-size: 11px;
+ font-weight: 800;
+ line-height: 18px;
+ min-width: 20px;
+ padding: 1px 2px 1px 1px;
+ position: absolute;
+ right: 18px;
+ text-align: center;
+ top: 12px;
+ top: 14px;
+}
+
+.left-nav-menu ul .submenu li a
+ padding-left 40px
+ background brand-blue - 20%
+
+.left-nav-menu ul .submenu > li > ul.submenu > li > a
+ padding-left: 40px
+ background brand-blue - 30%
+
+.left-nav-menu ul .submenu > li > ul.submenu > li > ul.submenu > li > a
+ padding-left: 40px;
+ background brand-blue - 40%
+.left-nav-menu ul .submenu > li > ul.submenu > li > ul.submenu > li > ul.submenu > li > a
+ padding-left: 50px;
+ background brand-blue - 50%
+.left-nav-menu ul .submenu > li > ul.submenu > li > ul.submenu > li > ul.submenu > li > ul.submenu > li > a
+ padding-left: 60px;
+ background brand-blue - 60%
+.left-nav-menu ul .submenu > li > ul.submenu > li > ul.submenu > li > ul.submenu > li > ul.submenu > li > ul.submenu > li > a
+ padding-left: 70px;
+ background brand-blue - 70%
+.left-nav-menu ul .submenu > li > ul.submenu > li > ul.submenu > li > ul.submenu > li > ul.submenu > li > ul.submenu > li > ul.submenu > li > ul.submenu > li > a
+ padding-left: 80px;
+ background brand-blue - 80%
+
+.left-nav-menu .sm-open-close-indicator
+ float: right;
+
+ i.fa
+ font-size: 10px;
+ width: 10px;
+
+.left-nav-menu .submenu-indicator {
+ -moz-transition: "transform .3s linear";
+ -o-transition: "transform .3s linear";
+ -webkit-transition: "transform .3s linear";
+ transition: "transform .3s linear";
+ float: right;
+ font-size: 20px;
+ line-height: 19px;
+ position: absolute;
+ right: 22px;
+}
+
+.left-nav-menu .submenu-indicator-minus > .submenu-indicator {
+ -moz-transform: rotate(45deg);
+ -ms-transform: rotate(45deg);
+ -o-transform: rotate(45deg);
+ -webkit-transform: rotate(45deg);
+ transform: rotate(45deg);
+}
+
+.left-nav-menu > ul > li.active > a {
+ background: #3b424d;
+ color: #ffffff;
+}
+
+
+.left-nav-menu > ul > li > a { border-bottom: solid 1px #3b424d; }
+
+.ink {
+ -moz-transform: scale(0);
+ -ms-transform: scale(0);
+ -o-transform: scale(0);
+ -webkit-transform: scale(0);
+ background: rgba(255, 255, 255, 0.3);
+ border-radius: 100%;
+ display: block;
+ position: absolute;
+ transform: scale(0);
+}
+
+.animate-ink {
+ -moz-animation: ripple .3s linear;
+ -ms-animation: ripple .3s linear;
+ -o-animation: ripple .3s linear;
+ -webkit-animation: ripple .3s linear;
+ animation: ripple .3s linear;
+}
+
+// @-moz-keyframes 'ripple' {
+// 100% {
+// opacity: 0;
+// transform: scale(2.5);
+// }
+// }
+// @-webkit-keyframes 'ripple' {
+// 100% {
+// opacity: 0;
+// transform: scale(2.5);
+// }
+// }
+// @keyframes 'ripple' {
+// 100% {
+// opacity: 0;
+// transform: scale(2.5);
+// }
+// }
diff --git a/ui/client/css/components.styl b/ui/client/css/components.styl
new file mode 100644
index 0000000..a4b2f04
--- /dev/null
+++ b/ui/client/css/components.styl
@@ -0,0 +1,187 @@
+.main-layout
+ margin 30px 30px 30px 330px
+
+.main-layout-no-nav
+ margin 0px
+
+.site-sidenav
+ width: 300px;
+ min-width: 300px;
+ max-width: 300px;
+ //height: 100vh
+ height: initial;
+ align-self: stretch;
+ min-height: 95vh;
+ background brand-blue
+ trans()
+
+.site-sidenav-collapse
+ width: 30px
+ min-width: 30px
+ max-width: 30px
+ min-height 100vh
+ background brand-blue
+ color white
+ margin-top -40px
+ trans()
+
+.cards
+ color #FFF
+ padding 20px
+ margin-bottom 30px
+ .material-icons
+ padding-top 20px
+ shadow-level(1)
+ trans()
+ &:hover
+ shadow-level(2)
+ &.not-active
+ shadow-level (0)
+ &.blue
+ background-color spark-blue
+ &.green
+ background-color status-green
+ &.grey
+ background-color dk-gray1
+ &.light-grey
+ background-color light-grey
+ &.dk-blue
+ background-color dk-blue
+ &.orange
+ background-color status-orange
+ &.red
+ background-color status-red
+ &.white
+ background #FFF
+ color #000
+ &.blue-text
+ color spark-blue
+ &.green-text
+ .fa, .material-icons
+ color status-green
+ h5
+ border-bottom 3px solid status-green
+ p.active
+ padding-top 3px
+ font-size 1.2em
+ color status-green
+ &.orange-text
+ .fa, .material-icons
+ color status-orange
+ h5
+ border-bottom 3px solid status-orange
+ p.active
+ padding-top 3px
+ font-size 1.2em
+ color status-orange
+ &.red-text
+ .fa, .material-icons
+ color status-red
+ h5
+ border-bottom 3px solid status-red
+ p.active
+ padding-top 3px
+ font-size 1.2em
+ color status-red
+ h3
+ color spark-blue
+ font-size 2em
+ border-bottom 3px solid spark-blue
+ p.text
+ color #000
+ .fa, .material-icons
+ font-size 4em
+ &.title
+ h4
+ margin 0
+ text-align center
+ color spark-blue
+
+.cards-w250
+ @extend .cards
+ padding 20px 5px
+ max-height 100px
+ width: 250px
+ max-width: 250px
+ min-width: 250px
+ text-align center
+ .fa, .material-icons
+ font-size 3em
+ p
+ margin 0
+ span
+ font-size 1.3em
+ font-weight bold
+
+.cards-w300
+ @extend .cards
+ max-height 100px
+ width: 300px
+ max-width: 300px
+ min-width: 300px;
+ p
+ font-size 1.1em
+ span
+ font-size 1.3em
+ font-weight bold
+
+.cards-w300-h60
+ @extend .cards-w300
+ font-size 0.9em
+ height 60px
+ .fa, .material-icons
+ font-size 2em
+ &:hover
+ shadow-level(1)
+
+.cards-flex-col-h120
+ @extend .cards
+ height: 120px
+ max-height 120px
+ min-height 120px
+ h5
+ padding 0px
+ margin 0px
+ a
+ color spark-blue
+ p
+ font-size 1em
+ padding 0px
+ margin 0px
+ span
+ font-size 1.3em
+ font-weight bold
+
+.cards-450
+ @extend .cards
+ width: 520px
+ max-width: 520px
+ min-width: 520px
+ p
+ font-size 1.1em
+ span
+ font-size 1.3em
+ font-weight bold
+ .material-icons
+ color spark-blue
+ font-size 5em
+ padding 40px 30px
+
+.cards-flex-col-h500
+ @extend .cards
+ margin-right 30px
+ height 500px
+ max-height 500px
+ min-height 500px
+ p
+ font-size 1.1em
+ span
+ font-size 1.3em
+ font-weight bold
+ .material-icons
+ color spark-blue
+ font-size 8em
+ padding 40px
+
+.margin-top-40
+ margin-top 40px
diff --git a/ui/client/css/d3graph.styl b/ui/client/css/d3graph.styl
new file mode 100644
index 0000000..6d53c45
--- /dev/null
+++ b/ui/client/css/d3graph.styl
@@ -0,0 +1,47 @@
+
+.node circle {
+ fill: steelblue;
+}
+.node text {
+ font: bold 14px sans-serif;
+ fill: rgb(16, 15, 15);
+ text-shadow: 0 0px 0 #fff;
+ /*font-size: 0.8em;*/
+}
+.link {
+ /*
+ fill: #1313AB;
+ stroke: #1313AB;
+ */
+}
+.different-groups{
+ stroke: #999;
+}
+.default {
+ stroke: #fbb;
+ stroke-width: 4px;
+}
+.outline {
+ stroke: #844;
+ stroke-width: 6px;
+}
+
+div.tooltip {
+ position: absolute;
+ text-align: left;
+ opacity 1
+ font: bold 18px sans-serif !important;
+ /* width: 60px; */
+ /* height: 28px; */
+ padding: 20px;
+ font: 16px sans-serif;
+ background: dk-gray1;
+ color white
+ border: 2px solid stark-blue
+ pointer-events: none;
+}
+.background {
+ stroke: white;
+ stroke-width: 1px;
+ fill: white;
+}
diff --git a/ui/client/css/flex.styl b/ui/client/css/flex.styl
new file mode 100644
index 0000000..e780d78
--- /dev/null
+++ b/ui/client/css/flex.styl
@@ -0,0 +1,23 @@
+// flex grid system
+.flex-box
+ list-style none
+ display flex
+ flex-wrap wrap
+ // border 2px solid red
+ .flex-box-1
+ flex 1
+ // border 2px solid green
+ .flex-box-2
+ flex 2
+ .flex-box-3
+ flex 3
+ .flex-box-4
+ flex 4
+.flex-col
+ flex-direction column
+.justify-content-around
+ justify-content space-around
+.justify-content-between
+ justify-content space-between
+.justify-content-start
+ justify-content flex-start \ No newline at end of file
diff --git a/ui/client/css/header.styl b/ui/client/css/header.styl
new file mode 100644
index 0000000..ebc3ff6
--- /dev/null
+++ b/ui/client/css/header.styl
@@ -0,0 +1,118 @@
+.os-alarm-icons
+ display: flex;
+ flex-flow: row;
+ justify-content: flex-end;
+
+ padding-top 50px
+ //background-color dk-gray1
+ background-color: brand-blue
+ border-bottom: solid 1px black;
+
+ .breadcrumb
+ background-color dk-gray1
+
+ .alarm-icons
+ padding-top 15px
+ padding-right 10px
+ color #FFF
+ //text-align right
+
+ .material-icons:hover
+ cursor pointer
+ color spark-grey
+ trans()
+
+ .dropdown
+ display inline
+ trans()
+
+ .dropdown-menu-right
+ right 25px
+
+ .dropdown-header
+ font-size 1.4em
+ color dk-gray1
+
+ >a
+ font-size: 20px;
+ color dk-gray1
+ line-height: 1.42857143;
+ padding-left: 4px;
+
+img.navbar-brand.navbar-custom {
+ padding-left: 40px;
+}
+
+p.navbar-brand.navbar-custom{
+ margin: 0px;
+ padding-left: 95px;
+ color: #FFF;
+}
+
+nav.navbar-inverse.navbar-custom
+ background-color: brand-blue
+ color: #FFF;
+ a
+ color: #FFF;
+ .login-close-text
+ color: #555;
+ #login-name-link:hover
+ color: rgb(63,81,181);
+ &:hover
+ color: rgb(63,81,181);
+ trans()
+ a.droplist
+ color: brand-blue
+ padding: 10px 0px 0px 10px
+
+ .search
+ position: relative;
+ color: white
+ input
+ height: 30px;
+ background: brand-blue
+ // text-indent: 25px;
+ padding-left: 20px;
+ border 0px
+ border-bottom 2px solid spark-grey
+ width 175px
+ &:hover, &:active, &:focus
+ border-bottom 2px solid white
+ ::-webkit-input-placeholder
+ color: white
+ .fa-search
+ position: absolute;
+ top: 7px;
+
+ .search-auto-complete
+ position: absolute;
+ right: 0;
+ top: 34px
+ z-index: 1000;
+
+.navbar-inverse .navbar-nav > .open > a,
+.navbar-inverse .navbar-nav > .open > a:hover,
+.navbar-inverse .navbar-nav > .open > a:focus {
+ color: brand-blue
+ background-color: #fff;
+}
+
+/* Login form style */
+.sign-in #login-sign-in-link, #login-name-link, #login-name-link.login-link-text {
+ color: #FFF;
+ text-decoration: none;
+}
+
+.login-button#login-buttons-open-change-password,
+.login-button#login-buttons-password,
+.login-button#login-buttons-logout {
+ background-color: brand-blue
+ border: brand-blue
+}
+
+.login-button#login-buttons-open-change-password:hover,
+.login-button#login-buttons-password:hover,
+.login-button#login-buttons-logout:hover {
+ background-color: rgb(63,81,181);
+ border: rgb(63,81,181);
+}
diff --git a/ui/client/css/landing.css b/ui/client/css/landing.css
new file mode 100644
index 0000000..59c0806
--- /dev/null
+++ b/ui/client/css/landing.css
@@ -0,0 +1,122 @@
+/****************************************************************************************/
+/* Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others */
+/* */
+/* All rights reserved. This program and the accompanying materials */
+/* are made available under the terms of the Apache License, Version 2.0 */
+/* which accompanies this distribution, and is available at */
+/* http://www.apache.org/licenses/LICENSE-2.0 */
+/****************************************************************************************/
+/* ======= landging page =======*/
+
+a.mdl-button:hover{
+ text-decoration: none;
+}
+
+.mdl-button--raised.mdl-button--colored {
+ background-color: #2196F3;
+}
+
+.mdl-button--accent.mdl-button--accent.mdl-button--raised:hover {
+ background-color: rgb(63,81,181);
+
+}
+
+section.section-margin-50 {
+ margin: 50px 0;
+}
+
+.background-blue{
+ background-color: #2196F3;
+ color: #f9f9f9;
+}
+.background-white {
+ border-color: #FFF;
+}
+
+.background-grey {
+ background-color: #f9f9f9;
+}
+
+.home-fullscreen {
+ /* padding: 25px; */
+}
+
+.mtop-50{
+ margin-top: 100px;
+}
+
+.font20{
+ font-size: 20px;
+}
+
+.title-border-bottom{
+ border-bottom: 5px solid #2196F3;
+}
+.text-align-center{
+ text-align: center;
+}
+
+/*
+.home_mockups {
+ position: relative;
+ margin-top: 4rem;
+ margin-bottom: -1rem;
+ z-index: 3;
+}
+
+.home_mockups_1 {
+ position: relative;
+ animation-delay: 0.5s;
+}
+
+.home_mockups_2 {
+ animation-delay: 1s;
+}
+
+.home_mockups_2, .home_mockups_3 {
+ position: absolute;
+ margin: 0 auto;
+ top: 0;
+ right: 0;
+ left: 0;
+ bottom: 0;
+}
+*/
+
+.box {
+ margin: 50px;
+}
+
+.box-side{
+ margin: 10px 50px;
+}
+/* ======= End of section =========*/
+
+/*====== Main page =======*/
+.logo{
+ padding: 10px;
+}
+
+.view-100{
+ height: 100vh;
+}
+
+.user-bar{
+ background: #42A5F5;
+ color: #fff;
+ padding-left: 15px;
+}
+/* ======= End of section =======*/
+
+/*// Rules for sizing the icon.*/
+.material-icons.md-18 { font-size: 21px; }
+.material-icons.md-24 { font-size: 24px; }
+.material-icons.md-36 { font-size: 36px; }
+.material-icons.md-48 { font-size: 48px; }
+/*// Rules for using icons as black on a light background.*/
+.material-icons.md-dark { color: rgba(0, 0, 0, 0.54); }
+.material-icons.md-dark.md-inactive { color: rgba(0, 0, 0, 0.26); }
+/*// Rules for using icons as white on a dark background.*/
+.material-icons.md-light { color: rgba(255, 255, 255, 1); }
+.material-icons.md-light.md-inactive { color: rgba(255, 255, 255, 0.3); }
+/* ======= End of section ===========*/
diff --git a/ui/client/css/loading.styl b/ui/client/css/loading.styl
new file mode 100644
index 0000000..1545b58
--- /dev/null
+++ b/ui/client/css/loading.styl
@@ -0,0 +1,19 @@
+
+
+.loading-message {
+ color: white;
+ font-size: 2.5em;
+ font-weight: 300;
+ font-family: sans-serif;
+}
+
+.sk-spinner-rotating-plane.sk-spinner {
+ background-color: white;
+}
+
+.landing
+ .material-icons
+ font-size 64px
+
+.margin-50
+ margin 80px 0px \ No newline at end of file
diff --git a/ui/client/css/modals.styl b/ui/client/css/modals.styl
new file mode 100644
index 0000000..f0d4741
--- /dev/null
+++ b/ui/client/css/modals.styl
@@ -0,0 +1,10 @@
+.modal-header
+ .material-icons
+ color spark-blue
+
+.modal-body
+ a
+ color spark-blue
+ h5
+ color spark-blue
+ border-bottom 2px solid spark-blue
diff --git a/ui/client/css/plugins.styl b/ui/client/css/plugins.styl
new file mode 100644
index 0000000..cd602ba
--- /dev/null
+++ b/ui/client/css/plugins.styl
@@ -0,0 +1,4 @@
+.graph
+ width: 100%;
+ height: 400px;
+ border: 3px solid spark-blue; \ No newline at end of file
diff --git a/ui/client/css/wizard.css b/ui/client/css/wizard.css
new file mode 100644
index 0000000..ebac04f
--- /dev/null
+++ b/ui/client/css/wizard.css
@@ -0,0 +1,28 @@
+/****************************************************************************************/
+/* Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others */
+/* */
+/* All rights reserved. This program and the accompanying materials */
+/* are made available under the terms of the Apache License, Version 2.0 */
+/* which accompanies this distribution, and is available at */
+/* http://www.apache.org/licenses/LICENSE-2.0 */
+/****************************************************************************************/
+.btn-mgt-5 {
+ margin-top: 5px;
+}
+
+.nav > li > a {
+ color: #2196F3;
+}
+
+.form-horizontal {
+ margin-top: 25px;
+}
+
+.mdl-button {
+ color: #2196F3;
+}
+
+.mdl-button:hover {
+ background-color: rgb(63,81,181);
+ color: #FFF;
+}
diff --git a/ui/client/lib/jquery.multilevelpushmenu.css b/ui/client/lib/jquery.multilevelpushmenu.css
new file mode 100644
index 0000000..407cda9
--- /dev/null
+++ b/ui/client/lib/jquery.multilevelpushmenu.css
@@ -0,0 +1,120 @@
+/****************************************************************************************/
+/* Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others */
+/* */
+/* All rights reserved. This program and the accompanying materials */
+/* are made available under the terms of the Apache License, Version 2.0 */
+/* which accompanies this distribution, and is available at */
+/* http://www.apache.org/licenses/LICENSE-2.0 */
+/****************************************************************************************/
+.multilevelpushmenu_wrapper {
+ position: absolute;
+ overflow: hidden;
+ min-width: 100%;
+ min-height: 100%;
+ margin: 0;
+ padding: 0;
+}
+
+.multilevelpushmenu_wrapper .levelHolderClass {
+ position: absolute;
+ overflow: hidden;
+ top: 0;
+ background: #336ca6;
+ width: auto;
+ min-height: 100%;
+ font-family: 'Open Sans Condensed', sans-serif;
+ font-size: 1em;
+ zoom: 1;
+}
+
+.multilevelpushmenu_wrapper .ltr {
+ margin-left: -100%;
+ left: 0;
+ -moz-box-shadow: 5px 0 5px -5px #1f4164;
+ -webkit-box-shadow: 5px 0 5px -5px #1f4164;
+ box-shadow: 5px 0 5px -5px #1f4164;
+ filter: progid:DXImageTransform.Microsoft.Shadow(color=#1f4164,direction=90,strength=2);
+}
+
+.multilevelpushmenu_wrapper .rtl {
+ margin-right: -100%;
+ right: 0;
+ -moz-box-shadow: 5px 0 5px 5px #1f4164;
+ -webkit-box-shadow: 5px 0 5px 5px #1f4164;
+ box-shadow: 5px 0 5px 5px #1f4164;
+ filter: progid:DXImageTransform.Microsoft.Shadow(color=#1f4164,direction=270,strength=2);
+}
+
+.multilevelpushmenu_wrapper .multilevelpushmenu_inactive {
+ background: #2e6196;
+}
+
+.multilevelpushmenu_wrapper h2 {
+ font-size: 1.5em;
+ line-height: 1em;
+ font-weight: bold;
+ color: #1f4164;
+ padding: 0 .4em 0 .4em;
+}
+
+.multilevelpushmenu_wrapper ul {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+}
+
+.multilevelpushmenu_wrapper li {
+ cursor: pointer;
+ border-top: 1px solid #295685;
+ padding: .4em .4em .4em .4em;
+}
+
+.multilevelpushmenu_wrapper li:last-child {
+ border-bottom: 1px solid #295685;
+}
+
+.multilevelpushmenu_wrapper li:hover {
+ background-color: #295685;
+}
+
+.multilevelpushmenu_wrapper a {
+ display: block;
+ outline: none;
+ overflow: hidden;
+ font-size: 1.5em;
+ line-height: 1em;
+ padding: .2em .2em;
+ text-decoration: none;
+ color: #fff;
+}
+
+.multilevelpushmenu_wrapper a:hover {
+ color: #ffe;
+}
+
+.multilevelpushmenu_wrapper .backItemClass {
+ display: block;
+ padding: .4em .4em .4em .4em;
+ background: #2e6196;
+ border-top: 1px solid #295685;
+}
+
+.multilevelpushmenu_wrapper .floatRight {
+ float: right;
+}
+
+.multilevelpushmenu_wrapper .floatLeft {
+ float: left;
+}
+
+.multilevelpushmenu_wrapper .cursorPointer {
+ cursor: pointer;
+}
+
+.multilevelpushmenu_wrapper .iconSpacing_ltr {
+ padding: 0 .4em 0 0;
+}
+
+.multilevelpushmenu_wrapper .iconSpacing_rtl {
+ padding: 0 0 0 .4em;
+} \ No newline at end of file
diff --git a/ui/client/lib/jquery.multilevelpushmenu.js b/ui/client/lib/jquery.multilevelpushmenu.js
new file mode 100644
index 0000000..0b2a24d
--- /dev/null
+++ b/ui/client/lib/jquery.multilevelpushmenu.js
@@ -0,0 +1,1148 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * jquery.multilevelpushmenu.js v2.1.4
+ *
+ * Licensed under the MIT license.
+ * http://www.opensource.org/licenses/mit-license.php
+ *
+ * Copyright 2013-2014, Make IT d.o.o.
+ * http://multi-level-push-menu.make.rs
+ * https://github.com/adgsm/multi-level-push-menu
+ */
+(function ( $ ) {
+ $.fn.multilevelpushmenu = function( options ) {
+ "use strict";
+ var args = arguments,
+ returnValue = null;
+
+ this.each(function(){
+ var instance = this,
+ $this = $( this ),
+ $container = ( $this.context != undefined ) ? $this : $( 'body' ),
+ menu = ( options && options.menu != undefined ) ? options.menu : $this.find( 'nav' ),
+ clickEventType, dragEventType;
+
+ // Settings
+ var settings = $.extend({
+ container: $container,
+ containersToPush: null,
+ menuID: ( ( $container.prop( 'id' ) != undefined && $container.prop( 'id' ) != '' ) ? $container.prop( 'id' ) : this.nodeName.toLowerCase() ) + "_multilevelpushmenu",
+ wrapperClass: 'multilevelpushmenu_wrapper',
+ menuInactiveClass: 'multilevelpushmenu_inactive',
+ menu: menu,
+ menuWidth: 0,
+ menuHeight: 0,
+ collapsed: false,
+ fullCollapse: false,
+ direction: 'ltr',
+ backText: 'Back',
+ backItemClass: 'backItemClass',
+ backItemIcon: 'fa fa-angle-right',
+ groupIcon: 'fa fa-angle-left',
+ mode: 'overlap',
+ overlapWidth: 40,
+ preventItemClick: true,
+ preventGroupItemClick: true,
+ swipe: 'both',
+ durationSlideOut: 400,
+ durationSlideDown: 500,
+ durationTransition: 400,
+ onCollapseMenuStart: function() {},
+ onCollapseMenuEnd: function() {},
+ onExpandMenuStart: function() {},
+ onExpandMenuEnd: function() {},
+ onGroupItemClick: function() {},
+ onItemClick: function() {},
+ onTitleItemClick: function() {},
+ onBackItemClick: function() {},
+ onMenuReady: function() {},
+ onMenuSwipe: function() {}
+ }, options );
+
+ // Store a settings reference withint the element's data
+ if (!$.data(instance, 'plugin_multilevelpushmenu')) {
+ $.data(instance, 'plugin_multilevelpushmenu', settings);
+ instance.settings = $.data(instance, 'plugin_multilevelpushmenu');
+ }
+
+ // Exposed methods
+ var methods = {
+ // Initialize menu
+ init: function () {
+ return initialize.apply(this, Array.prototype.slice.call(arguments));
+ },
+ // Collapse menu
+ collapse: function () {
+ return collapseMenu.apply(this, Array.prototype.slice.call(arguments));
+ },
+ // Expand menu
+ expand: function () {
+ return expandMenu.apply(this, Array.prototype.slice.call(arguments));
+ },
+ // Menu expanded
+ menuexpanded: function () {
+ return menuExpanded.apply(this, Array.prototype.slice.call(arguments));
+ },
+ // Active menu
+ activemenu: function () {
+ return activeMenu.apply(this, Array.prototype.slice.call(arguments));
+ },
+ // Find menu(s) by title
+ findmenusbytitle: function () {
+ return findMenusByTitle.apply(this, Array.prototype.slice.call(arguments));
+ },
+ // Find item(s) by name
+ finditemsbyname: function () {
+ return findItemsByName.apply(this, Array.prototype.slice.call(arguments));
+ },
+ // Find path to root menu collection
+ pathtoroot: function () {
+ return pathToRoot.apply(this, Array.prototype.slice.call(arguments));
+ },
+ // Find shared path to root of two menus
+ comparepaths: function () {
+ return comparePaths.apply(this, Array.prototype.slice.call(arguments));
+ },
+ // Get/Set settings options
+ option: function () {
+ return manageOptions.apply(this, Array.prototype.slice.call(arguments));
+ },
+ // Add item(s)
+ additems: function () {
+ return addItems.apply(this, Array.prototype.slice.call(arguments));
+ },
+ // Remove item(s)
+ removeitems: function () {
+ return removeItems.apply(this, Array.prototype.slice.call(arguments));
+ },
+ // Size DOM elements
+ redraw: function () {
+ return sizeDOMelements.apply(this, Array.prototype.slice.call(arguments));
+ },
+ // Returns visible level holders
+ visiblemenus: function () {
+ return visibleLevelHolders.apply(this, Array.prototype.slice.call(arguments));
+ },
+ // Returns visible level holders
+ hiddenmenus: function () {
+ return hiddenLevelHolders.apply(this, Array.prototype.slice.call(arguments));
+ },
+ // Propagate event to underneath layer
+ propagateevent: function () {
+ return propagateEvent.apply(this, Array.prototype.slice.call(arguments));
+ }
+ };
+
+ // IE 8 and modern browsers, prevent event propagation
+ function stopEventPropagation( e ){
+ if ( e.stopPropagation && e.preventDefault ) {
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ else {
+ e.cancelBubble = true;
+ e.returnValue = false;
+ }
+ }
+
+ // propagate event to underneath layer
+ // http://jsfiddle.net/E9zTs/2/
+ function propagateEvent( $element , event ) {
+ if( $element == undefined || event == undefined ) return false;
+ $element.on( event , function ( e , ee ) {
+ $element.hide();
+ try {
+ if(!e.pageX || !e.pageY) return false;
+ ee = ee || {
+ pageX: e.pageX,
+ pageY: e.pageY
+ };
+ var next = document.elementFromPoint( ee.pageX , ee.pageY );
+ next = ( next.nodeType == 3 ) ? next.parentNode : next //Opera
+ $( next ).trigger( event , ee );
+ }
+ catch ( err ) {
+ $.error( 'Error while propagating event: ' + err.message );
+ }
+ finally {
+ $element.show();
+ }
+ });
+ }
+
+ // Create DOM structure if it does not already exist within the container (input: array)
+ function createDOMStructure() {
+ var $mainWrapper = $( "<nav />" )
+ .prop( { "id" : instance.settings.menuID, "className" : instance.settings.wrapperClass } )
+ .appendTo( instance.settings.container );
+ createNestedDOMStructure( instance.settings.menu, $mainWrapper );
+ }
+ function createNestedDOMStructure( menus, $wrapper ){
+ if( menus.level == undefined ) menus.level = 0;
+ $.each( menus, function(){
+ var $levelHolder = $( "<div />" )
+ .attr( { "class" : "levelHolderClass" + ( ( instance.settings.direction == 'rtl' ) ? " rtl" : " ltr" ), "data-level" : menus.level, "style" : ( ( instance.settings.direction == 'rtl' ) ? "margin-right: " : "margin-left: " ) + ( ( menus.level == 0 && !instance.settings.collapsed ) ? 0 : "-200%" ) } )
+ .appendTo( $wrapper ),
+ extWidth = ( isValidDim( instance.settings.menuWidth ) || ( isInt( instance.settings.menuWidth ) && instance.settings.menuWidth > 0 ) );
+ $levelHolder.bind( dragEventType , function(e){
+ holderSwipe( e, $levelHolder );
+ });
+ if( this.id != undefined ) $levelHolder.attr( { "id" : this.id } );
+ var $title = $( "<h2 />" )
+ .attr( { "style" : "text-align: " + ( ( instance.settings.direction == 'rtl' ) ? "right" : "left" ) } )
+ .text( this.title )
+ .appendTo( $levelHolder ),
+ $titleIcon = $( "<i />" )
+ .prop( { "class" : ( ( instance.settings.direction == 'rtl' ) ? "floatLeft" : "floatRight" ) + " cursorPointer " + this.icon } )
+ .prependTo( $title );
+ $titleIcon.bind( clickEventType , function(e){
+ titleIconClick(e, $levelHolder, menus);
+ });
+ if( menus.level > 0 ) createBackItem( $levelHolder );
+ var $itemGroup = $( "<ul />" )
+ .appendTo( $levelHolder );
+ $.each(this.items, function(){
+ createItem( this, $levelHolder , -1 );
+ })
+ });
+ }
+
+ // Update DOM structure if it already exists in container (input: HTML markup)
+ function updateDOMStructure() {
+ var $mainWrapper = ( instance.settings.container.find( 'nav' ).length > 0 ) ? instance.settings.container.find( 'nav' ) : instance.settings.menu;
+ if( $mainWrapper.length == 0 ) return false;
+ $mainWrapper.prop( { "id" : instance.settings.menuID, "className" : instance.settings.wrapperClass } );
+ updateNestedDOMStructure( $mainWrapper );
+ }
+ function updateNestedDOMStructure( $wrapper ){
+ if( $wrapper.level == undefined ) $wrapper.level = 0;
+ $.each( $wrapper, function(){
+ var $levelHolder = $( "<div />" )
+ .attr( { "class" : "levelHolderClass" + ( ( instance.settings.direction == 'rtl' ) ? " rtl" : " ltr" ), "data-level" : $wrapper.level, "style" : ( ( instance.settings.direction == 'rtl' ) ? "margin-right: " : "margin-left: " ) + ( ( $wrapper.level == 0 && !instance.settings.collapsed ) ? 0 : "-200%" ) } )
+ .appendTo( $wrapper ),
+ extWidth = ( isValidDim( instance.settings.menuWidth ) || ( isInt( instance.settings.menuWidth ) && instance.settings.menuWidth > 0 ) );
+ $levelHolder.bind( dragEventType , function(e){
+ holderSwipe( e, $levelHolder );
+ });
+ var $title = $wrapper.children( 'h2' );
+ $title.attr( { "style" : "text-align: " + ( ( instance.settings.direction == 'rtl' ) ? "right" : "left" ) } );
+ $title.appendTo( $levelHolder );
+ var $titleIcon = $title.children( 'i' );
+ $titleIcon.addClass( ( ( instance.settings.direction == 'rtl' ) ? "floatLeft" : "floatRight" ) + " cursorPointer" );
+ $titleIcon.bind( clickEventType , function(e){
+ titleIconClick(e, $levelHolder, $wrapper);
+ });
+ if( $wrapper.level > 0 ) createBackItem( $levelHolder );
+ var $itemGroup = $wrapper.children( 'ul' );
+ $itemGroup.appendTo( $levelHolder );
+ $.each($itemGroup.children( 'li' ), function(){
+ var $item = $( this );
+ $item.attr( { "style" : "text-align: " + ( ( instance.settings.direction == 'rtl' ) ? "right" : "left" ) } );
+ var $itemAnchor = $item.children( 'a' );
+ var $itemIcon = $itemAnchor.children( 'i' );
+ $itemIcon.addClass( ( ( instance.settings.direction == 'rtl' ) ? "floatLeft" : "floatRight" ) );
+ if($item.children( 'ul' ).length > 0) {
+ $itemAnchor.bind( clickEventType , function(e){
+ itemGroupAnchorClick( e, $levelHolder, $item );
+ });
+ createItemGroupIcon( $itemAnchor );
+ $item.level = $wrapper.level + 1;
+ updateNestedDOMStructure($item);
+ } else {
+ $itemAnchor.bind( clickEventType , function(e){
+ itemAnchorClick( e, $levelHolder, $item );
+ });
+ }
+ })
+ });
+ }
+
+ // Click event for title icon
+ function titleIconClick( e, $levelHolder, menus ) {
+ if( $(instance).find( 'div.levelHolderClass' ).is(':animated') ) return false;
+ instance.settings.onTitleItemClick.apply(this, Array.prototype.slice.call([e, $levelHolder, instance.settings]));
+ stopEventPropagation(e);
+ var instanceFC = ( instance.settings.direction == 'rtl' ) ?
+ parseInt( $levelHolder.css( 'margin-right' ) ) < 0
+ :
+ parseInt( $levelHolder.css( 'margin-left' ) ) < 0;
+ if( menus.level == 0 && instanceFC ) {
+ expandMenu();
+ }
+ else {
+ var $nextLevelHolders = instance.settings.container
+ .find( '#' + instance.settings.menuID + ' div.levelHolderClass' )
+ .filter(function(){
+ var retObjs = ( instance.settings.direction == 'rtl' ) ?
+ (($( this ).attr( 'data-level' ) > $levelHolder.attr( 'data-level' )) && ( parseInt( $( this ).css( 'margin-right' ) ) >= 0 ) )
+ :
+ (($( this ).attr( 'data-level' ) > $levelHolder.attr( 'data-level' )) && ( parseInt( $( this ).css( 'margin-left' ) ) >= 0 ) );
+ return retObjs;
+ }),
+ $prevLevelHolders = instance.settings.container
+ .find( '#' + instance.settings.menuID + ' div.levelHolderClass' )
+ .filter(function(){
+ var retObjs = ( instance.settings.direction == 'rtl' ) ?
+ (($( this ).attr( 'data-level' ) <= $levelHolder.attr( 'data-level' )) && ( parseInt( $( this ).css( 'margin-right' ) ) >= 0 ) )
+ :
+ (($( this ).attr( 'data-level' ) <= $levelHolder.attr( 'data-level' )) && ( parseInt( $( this ).css( 'margin-left' ) ) >= 0 ) );
+ return retObjs;
+ }),
+ collapseAll = ( $nextLevelHolders.length == 0 && $prevLevelHolders.length == 1 ) ? collapseMenu() : collapseMenu( parseInt( $levelHolder.attr( 'data-level' ) ) );
+ }
+ $levelHolder.css( 'visibility' , 'visible' );
+ $levelHolder.find( '.' + instance.settings.backItemClass ).css( 'visibility' , 'visible' );
+ $levelHolder.find( 'ul' ).css( 'visibility' , 'visible' );
+ $levelHolder.removeClass( instance.settings.menuInactiveClass );
+ }
+
+ // Create Back item DOM elements
+ function createBackItem( $levelHolder ) {
+ var $backItem = $( "<div />" )
+ .attr( { "class" : instance.settings.backItemClass } )
+ .appendTo( $levelHolder ),
+ $backItemAnchor = $( "<a />" )
+ .prop( { "href" : "#" } )
+ .text( instance.settings.backText )
+ .appendTo( $backItem ),
+ $backItemIcon = $( "<i />" )
+ .prop( { "class" : ( ( instance.settings.direction == 'rtl' ) ? "floatLeft " : "floatRight " ) + instance.settings.backItemIcon } )
+ .prependTo( $backItemAnchor );
+ $backItemAnchor.bind( clickEventType , function(e){
+ backItemAnchorClick(e, $levelHolder);
+ });
+ }
+
+ // Click event for back item
+ function backItemAnchorClick( e, $levelHolder ) {
+ if( $(instance).find( 'div.levelHolderClass' ).is(':animated') ) return false;
+ instance.settings.onBackItemClick.apply(this, Array.prototype.slice.call([e, $levelHolder, instance.settings]));
+ stopEventPropagation(e);
+ collapseMenu( parseInt( $levelHolder.attr( 'data-level' ) - 1 ) );
+ }
+
+ // Click event for group items
+ function itemGroupAnchorClick( e, $levelHolder, $item ) {
+ if( $(instance).find( 'div.levelHolderClass' ).is(':animated') ) return false;
+ instance.settings.onGroupItemClick.apply(this, Array.prototype.slice.call([e, $levelHolder, $item, instance.settings]));
+ expandMenu( $item.find( 'div:first' ) );
+ if( instance.settings.preventGroupItemClick ) stopEventPropagation(e);
+ }
+
+ // Create item group DOM element
+ function createItemGroupIcon( $itemAnchor ) {
+ var $itemGroupIcon = $( "<i />" )
+ .attr( { "class" : ( ( instance.settings.direction == 'rtl' ) ? " floatRight iconSpacing_rtl " : " floatLeft iconSpacing_ltr " ) + instance.settings.groupIcon } )
+ .prependTo( $itemAnchor );
+ }
+
+ // Create item DOM element
+ function createItem() {
+ var item = arguments[0],
+ $levelHolder = arguments[1],
+ position = arguments[2],
+ $itemGroup = $levelHolder.find( 'ul:first' ),
+ $item = $( "<li />" );
+ ( position < ( $itemGroup.children( 'li' ).length ) && position >= 0 ) ?
+ $item.insertBefore( $itemGroup.children( 'li' ).eq( position ) ) : $item.appendTo( $itemGroup );
+ $item.attr( { "style" : "text-align: " + ( ( instance.settings.direction == 'rtl' ) ? "right" : "left" ) } );
+ if( item.id != undefined ) $item.attr( { "id" : item.id } );
+ var $itemAnchor = $( "<a />" )
+ .prop( { "href" : item.link } )
+ .text( item.name )
+ .appendTo( $item ),
+ $itemIcon = $( "<i />" )
+ .prop( { "class" : ( ( instance.settings.direction == 'rtl' ) ? "floatLeft " : "floatRight " ) + item.icon } )
+ .prependTo( $itemAnchor );
+ if(item.items) {
+ $itemAnchor.bind( clickEventType , function(e){
+ itemGroupAnchorClick( e, $levelHolder, $item );
+ });
+ createItemGroupIcon( $itemAnchor );
+ item.items.level = parseInt( $levelHolder.attr( 'data-level' ), 10 ) + 1;
+ createNestedDOMStructure(item.items, $item);
+ } else {
+ $itemAnchor.bind( clickEventType , function(e){
+ itemAnchorClick( e, $levelHolder, $item );
+ });
+ }
+ }
+
+ // Click event for items
+ function itemAnchorClick( e, $levelHolder, $item ) {
+ instance.settings.onItemClick.apply(this, Array.prototype.slice.call([e, $levelHolder, $item, instance.settings]));
+ if( instance.settings.preventItemClick ) stopEventPropagation(e);
+ }
+
+ // Swipe/Drag event for holders
+ function holderSwipe( emd, $levelHolder ) {
+ var extRes = instance.settings.onMenuSwipe.apply(this, Array.prototype.slice.call([emd, $levelHolder, instance.settings]));
+ if( extRes == false ) return false;
+ if( $(instance).find( 'div.levelHolderClass' ).is(':animated') ) return false;
+ var level = ( $levelHolder.attr( 'data-level' ) > 0 ) ? $levelHolder.attr( 'data-level' ) - 1 : undefined;
+ if( emd.type == 'touchmove' && instance.settings.swipe != 'desktop' ) {
+ stopEventPropagation( emd );
+ emd = ( emd.touches ) ? emd : emd.originalEvent;
+ if( !emd.touches || emd.touches.length <= 0 ) return false;
+ var touch = emd.touches[0];
+ instance.settings.container.unbind( 'touchend' );
+ instance.settings.container.bind( 'touchend' , function( emm ) {
+ stopEventPropagation( emm );
+ $levelHolder.significance = 0;
+ $levelHolder.swipeStart = 0;
+ instance.settings.container.unbind( 'touchend' );
+ });
+ if ( $levelHolder.swipeStart != undefined && $levelHolder.swipeStart != 0 ) {
+ $levelHolder.significance = touch.pageX - $levelHolder.swipeStart;
+ }
+ else {
+ $levelHolder.significance = 0;
+ $levelHolder.swipeStart = touch.pageX;
+ return true;
+ }
+ if( Math.abs( $levelHolder.significance ) > instance.settings.overlapWidth*.3 ) {
+ if( instance.settings.direction == 'rtl' ) $levelHolder.significance *= ( -1 );
+ ( $levelHolder.significance > 0 ) ? expandMenu( ( level == undefined ) ? level : $levelHolder ) : collapseMenu( level );
+ $levelHolder.significance = 0;
+ $levelHolder.swipeStart = 0;
+ }
+ }
+ else if( instance.settings.swipe != 'touchscreen' ) {
+ stopEventPropagation( emd );
+ var significance = 0;
+ $levelHolder.unbind( 'mousemove' );
+ $levelHolder.bind( 'mousemove' , function( emm ){
+ significance = emm.clientX - emd.clientX;
+ if( Math.abs( significance ) > instance.settings.overlapWidth*.3 ) {
+ $levelHolder.unbind( 'mousemove' );
+ if( instance.settings.direction == 'rtl' ) significance *= ( -1 );
+ ( significance > 0 ) ? expandMenu( ( level == undefined ) ? level : $levelHolder ) : collapseMenu( level );
+ return true;
+ }
+ });
+ instance.settings.container.unbind( 'mouseup' );
+ instance.settings.container.bind( 'mouseup' , function(e){
+ stopEventPropagation( e );
+ $levelHolder.unbind( 'mousemove' );
+ instance.settings.container.unbind( 'mouseup' );
+ });
+ }
+ }
+
+ // Returns visible level holders
+ function visibleLevelHolders() {
+ var $visibleLevelHolders = instance.settings.container
+ .find( '#' + instance.settings.menuID + ' div.levelHolderClass' )
+ .filter(function(){
+ var retObjs = ( instance.settings.direction == 'rtl' ) ?
+ ( parseInt( $( this ).css( 'margin-right' ) ) >= 0 && $( this ).position().left < instance.settings.container.width() - instance.settings.overlapWidth )
+ :
+ ( parseInt( $( this ).css( 'margin-left' ) ) >= 0 && $( this ).position().left >= 0 );
+ return retObjs;
+ });
+ if( $visibleLevelHolders.length < 1 ) $visibleLevelHolders = false;
+ return $visibleLevelHolders;
+ }
+
+ // Returns hidden level holders
+ function hiddenLevelHolders() {
+ var $hiddenLevelHolders = instance.settings.container
+ .find( '#' + instance.settings.menuID + ' div.levelHolderClass' )
+ .filter(function(){
+ var retObjs = ( instance.settings.direction == 'rtl' ) ?
+ ( ( $( this ).position().left > instance.settings.container.width() || parseInt( $( this ).css( 'margin-right' ) ) < 0 ) )
+ :
+ ( ( $( this ).position().left < 0 || parseInt( $( this ).css( 'margin-left' ) ) < 0 ) );
+ return retObjs;
+ });
+ if( $hiddenLevelHolders.length < 1 ) $hiddenLevelHolders = false;
+ return $hiddenLevelHolders;
+ }
+
+ // Sizing DOM elements per creation/update
+ function sizeDOMelements() {
+ if( !instance.redraw ) {
+ instance.redraw = true;
+ var forceWidth = arguments[0],
+ forceHeight = arguments[1],
+ filter = arguments[2],
+ ieShadowFilterDistortion = ($('#' + instance.settings.menuID + ' div.levelHolderClass').first().css('filter').match(/DXImageTransform\.Microsoft\.Shadow/)) ? $('#' + instance.settings.menuID + ' div.levelHolderClass').first().get(0).filters.item("DXImageTransform.Microsoft.Shadow").strength : 0,
+ maxWidth = ( forceWidth == undefined ) ? Math.max.apply( null,
+ $('#' + instance.settings.menuID + ' div.levelHolderClass').map(function(){ return $(this).width(); }).get() ) - ieShadowFilterDistortion : forceWidth - ieShadowFilterDistortion,
+ maxLevel = Math.max.apply( null,
+ $('#' + instance.settings.menuID + ' div.levelHolderClass').map(function(){ return $(this).attr( 'data-level' ); }).get() ),
+ extWidth = ( isValidDim( instance.settings.menuWidth ) || ( isInt( instance.settings.menuWidth ) && instance.settings.menuWidth > 0 ) ),
+ extHeight = ( isValidDim( instance.settings.menuHeight ) || ( isInt( instance.settings.menuHeight ) && instance.settings.menuHeight > 0 ) ),
+ $objects = ( filter == undefined ) ? $('#' + instance.settings.menuID + ' div.levelHolderClass' ) : filter,
+ currWidth;
+ if ( !extWidth && instance.menuWidth != undefined ) maxWidth = instance.menuWidth;
+ ( extWidth && forceWidth == undefined ) ? $objects.width(instance.settings.menuWidth) : $objects.width( maxWidth );
+ if( extWidth ){
+ if( ( $objects.width() == 0 || ( isValidDim( instance.settings.menuWidth ) && instance.settings.menuWidth.indexOf( '%' ) != -1 ) ) && forceWidth == undefined ) {
+ $objects.css( 'min-width' , '' );
+ $objects.width( parseInt( instance.settings.container.parent().width() * parseInt( instance.settings.menuWidth )/100 ) )
+ };
+ maxWidth = $objects.width() - ieShadowFilterDistortion;
+ $objects.css( 'min-width' , $objects.width() - ieShadowFilterDistortion + 'px' );
+ }
+ var maxExtWidth = ( extWidth && forceWidth == undefined ) ? ( $objects.width() - ieShadowFilterDistortion + maxLevel * ( instance.settings.overlapWidth + ieShadowFilterDistortion ) ) : ( maxWidth + maxLevel * ( instance.settings.overlapWidth + ieShadowFilterDistortion ) ),
+ maxHeight = ( forceHeight == undefined ) ? Math.max.apply( null,
+ $('#' + instance.settings.menuID + ' div.levelHolderClass').map(function(){ return $(this).height(); }).get() ) : forceHeight;
+
+ instance.settings.container.css( 'min-height' , '' );
+ instance.settings.container.children( 'nav:first' ).css( 'min-height' , '' );
+ if( extHeight ) {
+ instance.settings.container.height( instance.settings.menuHeight );
+ instance.settings.container.css( 'min-height' , instance.settings.menuHeight );
+ instance.settings.container.children( 'nav:first' ).css( 'min-height' , instance.settings.menuHeight );
+ $('#' + instance.settings.menuID).height(instance.settings.menuHeight);
+ maxHeight = instance.settings.container.height();
+ }
+ else {
+ $('#' + instance.settings.menuID).height( maxHeight );
+ }
+ instance.settings.container.css( 'min-height' , maxHeight + 'px' );
+ instance.settings.container.children( 'nav:first' ).css( 'min-height' , maxHeight + 'px' );
+ instance.settings.container.width( maxExtWidth );
+ instance.settings.container.height( maxHeight );
+ var $baseLevelHolder = $('#' + instance.settings.menuID + ' div.levelHolderClass:first'),
+ $visibleLevelHolders = visibleLevelHolders(),
+ $hiddenLevelHolders = hiddenLevelHolders(),
+ $activeLevelHolder = activeMenu(),
+ activeLevel = ( $activeLevelHolder.length == 1 ) ? $activeLevelHolder.attr( 'data-level' ) : 0;
+ if( $visibleLevelHolders )
+ $visibleLevelHolders.each(function(){
+ if ( instance.settings.mode == 'overlap' )
+ $( this ).width( $( this ).width() + ( parseInt( activeLevel , 10) - parseInt( $( this ).attr( 'data-level' ) , 10) ) * ( instance.settings.overlapWidth + ieShadowFilterDistortion ) );
+ });
+ if( $hiddenLevelHolders )
+ $hiddenLevelHolders.each(function(){
+ ( instance.settings.direction == 'rtl' ) ?
+ $( this ).css( 'margin-right' , ( $( this ).attr( 'data-level' ) == $baseLevelHolder.attr( 'data-level' ) && !instance.settings.fullCollapse ) ? $( this ).width() * (-1) + instance.settings.overlapWidth : $( this ).width() * (-2) )
+ :
+ $( this ).css( 'margin-left' , ( $( this ).attr( 'data-level' ) == $baseLevelHolder.attr( 'data-level' ) && !instance.settings.fullCollapse ) ? $( this ).width() * (-1) + instance.settings.overlapWidth : $( this ).width() * (-2) );
+ });
+ currWidth = $baseLevelHolder.width() + parseInt( $baseLevelHolder.css( ( instance.settings.direction == 'rtl' ) ? 'margin-right' : 'margin-left' ) , 10 );
+ sizeElementWidth( instance.settings.container , currWidth );
+ instance.menuWidth = maxWidth;
+ instance.menuHeight = maxHeight;
+ instance.redraw = false;
+ }
+ }
+
+ // Simple/singe DOM element width sizing
+ function sizeElementWidth( $element , size ) {
+ if( $element == undefined || size == undefined ) return false;
+ $element.css( 'min-width' , '' );
+ $element.css( 'min-width' , size + 'px' );
+ $element.children( 'nav:first' ).css( 'min-width' , '' );
+ $element.children( 'nav:first' ).css( 'min-width' , size + 'px' );
+ $element.width( size );
+ }
+
+ // Hide wrappers in browsers that
+ // does not understand negative margin in %
+ // before DOM element got its dimensions
+ function fixLazyBrowsers() {
+ var $baseLevelHolder = $('#' + instance.settings.menuID + ' div.levelHolderClass:first'),
+ $hiddenLevelHolders = instance.settings.container
+ .find( '#' + instance.settings.menuID + ' div.levelHolderClass' )
+ .filter(function(){
+ var retObjs = ( instance.settings.direction == 'rtl' ) ?
+ ( ( $( this ).position().left > instance.settings.container.width() || parseInt( $( this ).css( 'margin-right' ) ) < 0 ) && $( this ).attr( 'data-level' ) > $baseLevelHolder.attr( 'data-level' ) )
+ :
+ ( ( $( this ).position().left < 0 || parseInt( $( this ).css( 'margin-left' ) ) < 0 ) && $( this ).attr( 'data-level' ) > $baseLevelHolder.attr( 'data-level' ) );
+ return retObjs;
+ });
+ $hiddenLevelHolders.each(function(){
+ if( instance.settings.direction == 'rtl' ){
+ $( this ).css( 'margin-right' , ( ( $( this ).attr( 'data-level' ) == $baseLevelHolder.attr( 'data-level' ) && !instance.settings.collapsed ) ? 0 : (-2) * $( this ).width() ) )
+ }
+ else {
+ $( this ).css( 'margin-left' , ( ( $( this ).attr( 'data-level' ) == $baseLevelHolder.attr( 'data-level' ) && !instance.settings.collapsed ) ? 0 : (-2) * $( this ).width() ) );
+ }
+ });
+ if( instance.settings.direction == 'rtl' ){
+ $baseLevelHolder.css( 'margin-right' , ( !instance.settings.collapsed ) ? 0 : (-2) * $baseLevelHolder.width() )
+ }
+ else {
+ $baseLevelHolder.css( 'margin-left' , ( !instance.settings.collapsed ) ? 0 : (-2) * $baseLevelHolder.width() );
+ }
+ }
+
+ // Is integer
+ function isInt( n ) {
+ return typeof n === 'number' && parseFloat( n ) == parseInt( n, 10 ) && !isNaN( n );
+ }
+
+ // Is Valid CSS dimension
+ function isValidDim( s ) {
+ return typeof s === 'string' && ( s.indexOf( '%' ) != -1 || s.indexOf( 'px' ) != -1 || s.indexOf( 'em' ) != -1 );
+ }
+
+ // Initialize menu level push menu
+ function initialize(){
+ var execute = ( options && options.menu != undefined ) ? createDOMStructure() : updateDOMStructure();
+ propagateEvent( instance.settings.container , clickEventType );
+ sizeDOMelements();
+ fixLazyBrowsers();
+ startMode( instance.settings.collapsed );
+ instance.settings.onMenuReady.apply(this, Array.prototype.slice.call([instance.settings]));
+ return $this;
+ }
+
+ // Initialize menu in collapsed/expanded mode
+ function startMode( mode ) {
+ if( mode ) {
+ var $baseLevelHolder = $('#' + instance.settings.menuID + ' div.levelHolderClass:first');
+ $baseLevelHolder.find( 'ul' ).hide();
+ $baseLevelHolder.addClass( instance.settings.menuInactiveClass );
+ if( instance.settings.direction == 'rtl' ) {
+ $baseLevelHolder.stop().animate({
+ marginRight: ( ( -1 ) * $baseLevelHolder.width() + ( ( instance.settings.fullCollapse ) ? 0 : instance.settings.overlapWidth ) )
+ }, instance.settings.durationSlideOut)
+ }
+ else {
+ $baseLevelHolder.stop().animate({
+ marginLeft: ( ( -1 ) * $baseLevelHolder.width() + ( ( instance.settings.fullCollapse ) ? 0 : instance.settings.overlapWidth ) )
+ }, instance.settings.durationSlideOut);
+ }
+ }
+ }
+
+ // Push container(s) of choice
+ function pushContainers( absMove ) {
+ if( instance.settings.containersToPush == null ) return false;
+ $.each( instance.settings.containersToPush, function() {
+ var lMr = parseInt( $( this ).css( 'margin-left' ) ),
+ lM = isInt( lMr ) ? lMr : 0,
+ rMr = parseInt( $( this ).css( 'margin-right' ) ),
+ rM = isInt( rMr ) ? rMr : 0;
+ $( this ).stop().animate({
+ marginLeft: lM + ( ( instance.settings.direction == 'rtl' ) ? (-1) : 1 ) * absMove,
+ marginRight: rM + ( ( instance.settings.direction == 'rtl' ) ? 1 : (-1) ) * absMove
+ }, instance.settings.durationSlideOut);
+ });
+ }
+
+ // Collapse menu
+ function collapseMenu() {
+ if( $(instance).find( 'div.levelHolderClass' ).is(':animated') ) return false;
+ instance.settings.onCollapseMenuStart.apply(this, Array.prototype.slice.call([instance.settings]));
+ var level = arguments[0],
+ callbacks = arguments[1],
+ collapingObjects = {},
+ ieShadowFilterDistortion,lwidth, lpush, lMarginLeft, lMarginLeftFC,
+ $baseLevelHolder = $('#' + instance.settings.menuID + ' div.levelHolderClass:first'),
+ collapseAll = ( level == undefined ) ? true : false,
+ currWidth;
+ collapingObjects[ 'collapsingEnded' ] = false;
+ if( typeof level == 'object' ) {
+ level = level.attr( 'data-level' );
+ }
+ else if( typeof level == 'string' ){
+ var $selectedLevelHolder = findMenusByTitle( level );
+ if( $selectedLevelHolder && $selectedLevelHolder.length == 1 ) {
+ level = $selectedLevelHolder.attr( 'data-level' );
+ }
+ else {
+ level = $baseLevelHolder.attr( 'data-level' );
+ }
+ }
+ else if( level == undefined || !isInt( level ) || level < 0 ) {
+ level = $baseLevelHolder.attr( 'data-level' );
+ }
+ if( callbacks == undefined && typeof callbacks != 'object' ) {
+ callbacks = [ { 'method' : instance.settings.onCollapseMenuEnd, 'args' : [instance.settings] } ];
+ } else {
+ $.merge(callbacks, [ { 'method' : instance.settings.onCollapseMenuEnd, 'args' : [instance.settings] } ]);
+ }
+ var $nextLevelHolders = instance.settings.container
+ .find( '#' + instance.settings.menuID + ' div.levelHolderClass' )
+ .filter(function(){
+ var retObjs = ( instance.settings.direction == 'rtl' ) ?
+ ($( this ).attr( 'data-level' ) > level) && (parseInt( $( this ).css( 'margin-right' ) ) >= 0 && $( this ).position().left < instance.settings.container.width() - instance.settings.overlapWidth )
+ :
+ ($( this ).attr( 'data-level' ) > level) && (parseInt( $( this ).css( 'margin-left' ) ) >= 0 && $( this ).position().left >= 0 );
+ return retObjs;
+ }),
+ $prevLevelHolders = instance.settings.container
+ .find( '#' + instance.settings.menuID + ' div.levelHolderClass' )
+ .filter(function(){
+ var retObjs = ( instance.settings.direction == 'rtl' ) ?
+ ($( this ).attr( 'data-level' ) <= level) && (parseInt( $( this ).css( 'margin-right' ) ) >= 0 && $( this ).position().left < instance.settings.container.width() - instance.settings.overlapWidth )
+ :
+ ($( this ).attr( 'data-level' ) <= level) && (parseInt( $( this ).css( 'margin-left' ) ) >= 0 && $( this ).position().left >= 0 );
+ return retObjs;
+ });
+ if( $prevLevelHolders.length > 0 ) {
+ collapingObjects[ 'prevAnimEnded' ] = false;
+ $nextLevelHolders.each(function( key, val ){
+ ieShadowFilterDistortion = ($( val ).css('filter').match(/DXImageTransform\.Microsoft\.Shadow/)) ? $( val ).get(0).filters.item("DXImageTransform.Microsoft.Shadow").strength : 0;
+ lwidth = ( instance.settings.mode == 'overlap' ) ? $( val ).width() - ( $nextLevelHolders.length + $prevLevelHolders.length - $( val ).attr( 'data-level' ) - 1) * ( instance.settings.overlapWidth + ieShadowFilterDistortion ) - ieShadowFilterDistortion : $( val ).width() - ieShadowFilterDistortion
+ if( instance.settings.direction == 'rtl' ) {
+ $( val ).stop().animate({
+ marginRight : ( (-1) * lwidth ),
+ width: lwidth
+ }, instance.settings.durationTransition);
+ }
+ else {
+ $( val ).stop().animate({
+ marginLeft : ( (-1) * lwidth ),
+ width: lwidth
+ }, instance.settings.durationTransition);
+ }
+ });
+ collapingObjects[ 'nextAnimEnded' ] = ( $nextLevelHolders.length > 0 ) ? false : true ;
+ $nextLevelHolders.last().queue(function(){
+ collapingObjects[ 'nextAnimEnded' ] = true;
+ animatedEventCallback( collapingObjects , callbacks );
+ });
+ $prevLevelHolders.each(function( key, val ){
+ ieShadowFilterDistortion = ($( val ).css('filter').match(/DXImageTransform\.Microsoft\.Shadow/)) ? $( val ).get(0).filters.item("DXImageTransform.Microsoft.Shadow").strength : 0;
+ var $makeLevelHolderVisible = $prevLevelHolders.filter(function(){
+ return $( this ).attr( 'data-level' ) == level;
+ });
+ $makeLevelHolderVisible.css( 'visibility' , 'visible' );
+ $makeLevelHolderVisible.find( '.' + instance.settings.backItemClass ).css( 'visibility' , 'visible' );
+ $makeLevelHolderVisible.find( 'ul' ).css( 'visibility' , 'visible' );
+ $makeLevelHolderVisible.removeClass( instance.settings.menuInactiveClass );
+ lwidth = ( instance.settings.mode == 'overlap' ) ? $( val ).width() - $nextLevelHolders.length * ( instance.settings.overlapWidth + ieShadowFilterDistortion ) - ieShadowFilterDistortion : $( val ).width() - ieShadowFilterDistortion;
+ if( instance.settings.direction == 'rtl' ) {
+ $( val ).stop().animate({
+ width: lwidth,
+ marginRight : ( $( val ).attr( 'data-level' ) == $baseLevelHolder.attr( 'data-level' ) && collapseAll ) ?
+ ( instance.settings.fullCollapse ) ?
+ ( -1 ) * $( val ).width()
+ :
+ ( ( -1 ) * $( val ).width() + ( ( instance.settings.mode == 'overlap' ) ? $nextLevelHolders.length + 1 : 1 ) * instance.settings.overlapWidth )
+ :
+ 0
+ }, instance.settings.durationSlideOut, function(){
+ if( $( val ).attr( 'data-level' ) == $baseLevelHolder.attr( 'data-level' ) && collapseAll ){
+ $baseLevelHolder.children( 'ul' ).first().hide(instance.settings.durationSlideDown, function(){
+ $baseLevelHolder.addClass( instance.settings.menuInactiveClass );
+ });
+ }
+ currWidth = $baseLevelHolder.width() + parseInt( $baseLevelHolder.css( 'margin-right' ) , 10 );
+ sizeElementWidth( instance.settings.container , currWidth );
+ });
+ }
+ else {
+ $( val ).stop().animate({
+ width: lwidth,
+ marginLeft : ( $( val ).attr( 'data-level' ) == $baseLevelHolder.attr( 'data-level' ) && collapseAll ) ?
+ ( instance.settings.fullCollapse ) ?
+ ( -1 ) * $( val ).width()
+ :
+ ( ( -1 ) * $( val ).width() + ( ( instance.settings.mode == 'overlap' ) ? $nextLevelHolders.length + 1 : 1 ) * instance.settings.overlapWidth )
+ :
+ 0
+ }, instance.settings.durationSlideOut, function(){
+ if( $( val ).attr( 'data-level' ) == $baseLevelHolder.attr( 'data-level' ) && collapseAll ){
+ $baseLevelHolder.children( 'ul' ).first().hide(instance.settings.durationSlideDown, function(){
+ $baseLevelHolder.addClass( instance.settings.menuInactiveClass );
+ });
+ }
+ currWidth = $baseLevelHolder.width() + parseInt( $baseLevelHolder.css( 'margin-left' ) , 10 );
+ sizeElementWidth( instance.settings.container , currWidth );
+ });
+ }
+ lpush = ( instance.settings.mode == 'overlap' ) ? ( (-1) * ( $nextLevelHolders.length * ( instance.settings.overlapWidth + ieShadowFilterDistortion ) ) ) : 0 ;
+ if( $( val ).attr( 'data-level' ) == $baseLevelHolder.attr( 'data-level' ) && collapseAll ){
+ var blpush = ( instance.settings.fullCollapse ) ? ( -1 ) * ( $baseLevelHolder.width() - ieShadowFilterDistortion ) : ( -1 ) * ( $baseLevelHolder.width() - ieShadowFilterDistortion ) + instance.settings.overlapWidth;
+ pushContainers( blpush );
+ }
+ else {
+ pushContainers( lpush );
+ }
+ });
+ $prevLevelHolders.last().queue(function(){
+ collapingObjects[ 'prevAnimEnded' ] = true;
+ animatedEventCallback( collapingObjects , callbacks );
+ });
+ }
+ collapingObjects[ 'collapsingEnded' ] = true;
+ animatedEventCallback( collapingObjects , callbacks );
+ return $this;
+ }
+
+ // Expand Menu helper
+ function expandMenuActions() {
+ if( $(instance).find( 'div.levelHolderClass' ).is(':animated') ) return false;
+ instance.settings.onExpandMenuStart.apply(this, Array.prototype.slice.call([instance.settings]));
+ var menuTitle = arguments[0],
+ callbacks = arguments[1],
+ ieShadowFilterDistortion, lwidth, lpush, blpush, currWidth,
+ expandingObjects = {},
+ $baseLevelHolder = $('#' + instance.settings.menuID + ' div.levelHolderClass:first'),
+ baseExpand = ( menuTitle == undefined ) ? true : false,
+ baseLevelHolderCollapsed = ( instance.settings.direction == 'rtl' ) ?
+ parseInt( $baseLevelHolder.css( 'margin-right' ), 10 ) < 0 || $baseLevelHolder.position().left >= instance.settings.container.width() - instance.settings.overlapWidth
+ :
+ parseInt( $baseLevelHolder.css( 'margin-left' ), 10 ) < 0 || $baseLevelHolder.position().left < 0;
+ expandingObjects[ 'expandingEnded' ] = false;
+ if( callbacks == undefined && typeof callbacks != 'object' ) {
+ callbacks = [ { 'method' : instance.settings.onExpandMenuEnd, 'args' : [instance.settings] } ];
+ } else {
+ $.merge(callbacks, [ { 'method' : instance.settings.onExpandMenuEnd, 'args' : [instance.settings] } ]);
+ }
+ if( baseExpand ) {
+ expandingObjects[ 'baseAnimEnded' ] = false;
+ $baseLevelHolder.removeClass( instance.settings.menuInactiveClass );
+ currWidth = $baseLevelHolder.width();
+ sizeElementWidth( instance.settings.container , currWidth );
+ if( instance.settings.direction == 'rtl' ) {
+ $baseLevelHolder.stop().animate({
+ marginRight: 0
+ }, instance.settings.durationSlideOut, function(){
+ $baseLevelHolder.children( 'ul' ).first().show(instance.settings.durationSlideDown , function(){
+ expandingObjects[ 'baseAnimEnded' ] = true;
+ animatedEventCallback( expandingObjects , callbacks );
+ });
+ });
+ }
+ else {
+ $baseLevelHolder.stop().animate({
+ marginLeft: 0
+ }, instance.settings.durationSlideOut, function(){
+ $baseLevelHolder.children( 'ul' ).first().show(instance.settings.durationSlideDown , function(){
+ expandingObjects[ 'baseAnimEnded' ] = true;
+ animatedEventCallback( expandingObjects , callbacks );
+ });
+ });
+ }
+ blpush = ( instance.settings.fullCollapse ) ? $baseLevelHolder.width() : $baseLevelHolder.width() - instance.settings.overlapWidth;
+ var pushbm = ( !menuExpanded( $baseLevelHolder ) ) ? pushContainers( blpush ) : null;
+ } else {
+ var $selectedLevelHolder;
+ if( typeof menuTitle == 'object' ) {
+ $selectedLevelHolder = menuTitle;
+ }
+ else if( typeof menuTitle == 'string' ){
+ $selectedLevelHolder = findMenusByTitle( menuTitle );
+ }
+ else {
+ $selectedLevelHolder = null;
+ $.error( 'Provided menu selector is not valid' );
+ }
+ if( $selectedLevelHolder && $selectedLevelHolder.length == 1 ) {
+ var $activeLevelHolder = activeMenu(),
+ activeLevel = ( $activeLevelHolder.length == 1 ) ? $activeLevelHolder.attr( 'data-level' ) : 0,
+ baseWidth = $selectedLevelHolder.width(),
+ setToOpenHolders = pathToRoot( $selectedLevelHolder );
+ expandingObjects[ 'setToOpenAnimEnded' ] = false;
+ if( setToOpenHolders ) {
+ var parentLevelHoldersLen = $( setToOpenHolders ).length - 1;
+ $baseLevelHolder.find( 'ul' ).each(function(){
+ $( this ).show(0);
+ });
+ $( setToOpenHolders ).find( 'ul' ).css( 'visibility' , 'hidden' );
+ $( setToOpenHolders ).find( 'div' ).css( 'visibility' , 'visible' );
+ $( setToOpenHolders ).find( '.' + instance.settings.backItemClass ).css( 'visibility' , 'hidden' );
+ $( setToOpenHolders ).each( function( key, val ) {
+ ieShadowFilterDistortion = ($( val ).css('filter').match(/DXImageTransform\.Microsoft\.Shadow/)) ? $( val ).get(0).filters.item("DXImageTransform.Microsoft.Shadow").strength : 0;
+ lwidth = baseWidth - ieShadowFilterDistortion + ( parentLevelHoldersLen - $( val ).attr( 'data-level' ) ) * ( instance.settings.overlapWidth + ieShadowFilterDistortion );
+ if(instance.settings.container.width() < lwidth && instance.settings.mode == 'overlap' )
+ sizeElementWidth( instance.settings.container , lwidth );
+ if( instance.settings.direction == 'rtl' ) {
+ $( val ).stop().animate({
+ marginRight: 0,
+ width: ( instance.settings.mode == 'overlap' ) ? lwidth : baseWidth - ieShadowFilterDistortion
+ }, instance.settings.durationTransition, function(){
+ $( val ).addClass( instance.settings.menuInactiveClass );
+ });
+ }
+ else {
+ $( val ).stop().animate({
+ marginLeft: 0,
+ width: ( instance.settings.mode == 'overlap' ) ? lwidth : baseWidth - ieShadowFilterDistortion
+ }, instance.settings.durationTransition, function(){
+ $( val ).addClass( instance.settings.menuInactiveClass );
+ });
+ }
+ });
+ $( setToOpenHolders ).last().queue(function(){
+ $( this ).removeClass( instance.settings.menuInactiveClass );
+ expandingObjects[ 'setToOpenAnimEnded' ] = true;
+ animatedEventCallback( expandingObjects , callbacks );
+ });
+ if( baseLevelHolderCollapsed ) {
+ blpush = ( instance.settings.fullCollapse ) ? $baseLevelHolder.width() : ( $baseLevelHolder.width() - instance.settings.overlapWidth );
+ pushContainers( blpush );
+ }
+ if( instance.settings.mode == 'overlap' ){
+ lpush = ( ( baseLevelHolderCollapsed ) ? ( baseWidth + ( parentLevelHoldersLen - ( ( instance.settings.fullCollapse ) ? 0 : 1 ) ) * ( instance.settings.overlapWidth + ieShadowFilterDistortion ) ) : ( ( parentLevelHoldersLen - activeLevel ) * ( instance.settings.overlapWidth + ieShadowFilterDistortion ) ) );
+ pushContainers( lpush );
+ }
+ $selectedLevelHolder.css( 'visibility' , 'visible' );
+ $selectedLevelHolder.find( '.' + instance.settings.backItemClass ).css( 'visibility' , 'visible' );
+ $selectedLevelHolder.find( 'ul' ).css( 'visibility' , 'visible' );
+ $selectedLevelHolder.removeClass( instance.settings.menuInactiveClass );
+ }
+ else {
+ $.error( 'Invalid menu object provided' );
+ }
+ }
+ else {
+ $.error( 'No or too many menus named ' + menuTitle );
+ }
+ }
+ expandingObjects[ 'expandingEnded' ] = true;
+ animatedEventCallback( expandingObjects , callbacks );
+ }
+
+ // Expand menu
+ function expandMenu() {
+ var menu = arguments[0],
+ $expandLevelHolder,
+ $activeLevelHolder = activeMenu(),
+ $sharedLevelHolders, collapseLevel, $searchRes;
+ if( typeof menu == 'object' ) {
+ $expandLevelHolder = menu;
+ }
+ else if( typeof menu == 'string' ){
+ $searchRes = findMenusByTitle( menu );
+ if($searchRes) {
+ $expandLevelHolder = $searchRes.eq( 0 );
+ }
+ else {
+ $.error( menu + ' menu level does not exist!' );
+ }
+ }
+ else {
+ $expandLevelHolder = $('#' + instance.settings.menuID + ' div.levelHolderClass:first');
+ }
+ $sharedLevelHolders = comparePaths( $expandLevelHolder , $activeLevelHolder, true );
+ collapseLevel = ( $sharedLevelHolders.length > 0 ) ? Math.max.apply( null,
+ $sharedLevelHolders.map(function(){ return $(this).attr( 'data-level' ); }).get() ) : 0;
+ if( collapseLevel < $activeLevelHolder.attr( 'data-level' ) ) {
+ collapseMenu( collapseLevel , [ { 'method' : expandMenuActions, 'args' : arguments } ] );
+ }
+ else {
+ expandMenuActions.apply( this, Array.prototype.slice.call( arguments ) );
+ }
+ return $this;
+ }
+
+ // Find menu(s) by Title text
+ function findMenusByTitle() {
+ var menuTitle = arguments[0],
+ response,
+ $selectedLevelHolders = instance.settings.container
+ .find( '#' + instance.settings.menuID + ' div.levelHolderClass' )
+ .filter(function(){
+ return ( ($( this ).children( 'h2' ).text() == menuTitle ) );
+ });
+ if( $selectedLevelHolders.length > 0 ) {
+ returnValue = $selectedLevelHolders;
+ response = returnValue;
+ }
+ else {
+ returnValue = false;
+ response = returnValue;
+ }
+ return response;
+ }
+
+ // Find item(s) by Name
+ function findItemsByName() {
+ var itemName = arguments[0],
+ response,
+ $selectedItems = instance.settings.container
+ .find( '#' + instance.settings.menuID + ' div.levelHolderClass li' )
+ .filter(function(){
+ return ( ($( this ).children( 'a' ).text() == itemName ) );
+ });
+ if( $selectedItems.length > 0 ) {
+ returnValue = $selectedItems;
+ response = returnValue;
+ }
+ else {
+ returnValue = false;
+ response = returnValue;
+ }
+ return response;
+ }
+
+ // Find pathToRoot for provided menu
+ function pathToRoot() {
+ var $selectedLevelHolder = arguments[0],
+ $parentLevelHolders, setToOpenHolders, response;
+ if( $selectedLevelHolder == undefined || $selectedLevelHolder.length != 1 ) {
+ returnValue = false;
+ return returnValue;
+ };
+ $parentLevelHolders = $selectedLevelHolder.parents( 'div.levelHolderClass' );
+ setToOpenHolders = $.merge( $parentLevelHolders.get().reverse(), $selectedLevelHolder.get() );
+ returnValue = setToOpenHolders;
+ return returnValue;
+ }
+
+ // Finds the same part of the path to root of two provided menus
+ function comparePaths() {
+ var $levelHolder0 = arguments[0],
+ $levelHolder1 = arguments[1],
+ mode = ( arguments[2] != undefined ) ? arguments[2] : false,
+ $parentLevelHolders0, $parentLevelHolders1, setParents0, setParents1, lPath, sPath, comparePath, response;
+ if( $levelHolder0 == undefined || $levelHolder1 == undefined ) {
+ returnValue = false;
+ return returnValue;
+ };
+ $parentLevelHolders0 = ( $levelHolder0.length == 1 ) ? $levelHolder0.parents( 'div.levelHolderClass' ) : null;
+ $parentLevelHolders1 = ( $levelHolder1.length == 1 ) ? $levelHolder1.parents( 'div.levelHolderClass' ) : null;
+ setParents0 = ( $parentLevelHolders0 != null ) ? $.merge( $parentLevelHolders0.get().reverse(), $levelHolder0.get() ) : [];
+ setParents1 = ( $parentLevelHolders1 != null ) ? $.merge( $parentLevelHolders1.get().reverse(), $levelHolder1.get() ) : [];
+ lPath = ( setParents0.length >= setParents1.length ) ? setParents0 : setParents1;
+ sPath = ( lPath === setParents0 ) ? setParents1 : setParents0;
+ comparePath = $( lPath ).filter(function() {
+ return ( mode ) ? ( $.inArray( this, sPath ) != -1 ) : ( $.inArray( this, sPath ) == -1 );
+ });
+ returnValue = comparePath;
+ return returnValue;
+ }
+
+ // Active menu
+ function activeMenu() {
+ var $activeLevelHolders = instance.settings.container
+ .find( '#' + instance.settings.menuID + ' div.levelHolderClass' )
+ .filter(function(){
+ var retObjs = ( instance.settings.direction == 'rtl' ) ?
+ ((parseInt( $( this ).css( 'margin-right' ) ) >= 0 && $( this ).position().left < instance.settings.container.width() - instance.settings.overlapWidth ) )
+ :
+ ((parseInt( $( this ).css( 'margin-left' ) ) >= 0 && $( this ).position().left >= 0 ) );
+ return retObjs;
+ }),
+ maxLevel = Math.max.apply( null,
+ $activeLevelHolders.map(function(){ return $(this).attr( 'data-level' ); }).get() ),
+ $activeLevelHolder = $activeLevelHolders.filter(function(){
+ return $( this ).attr( 'data-level' ) == maxLevel;
+ });
+ returnValue = $activeLevelHolder;
+ return returnValue;
+ }
+
+ // Menu expanded
+ function menuExpanded() {
+ var $levelHolder = arguments[0],
+ returnValue = false;
+ if( $levelHolder == undefined ) return returnValue;
+
+ var check = ( instance.settings.direction == 'rtl' ) ?
+ ( parseInt( $levelHolder.css( 'margin-right' ) ) >= 0 && $levelHolder.position().left < instance.settings.container.width() - instance.settings.overlapWidth )
+ :
+ ( parseInt( $levelHolder.css( 'margin-left' ) ) >= 0 && $levelHolder.position().left >= 0 );
+ return check;
+ }
+
+ // Add item(s)
+ function addItems() {
+ var items = arguments[0],
+ $levelHolder = arguments[1],
+ position = arguments[2];
+ if( $levelHolder == undefined || typeof items != 'object' || !$levelHolder ) return false;
+ if( items.level == undefined ) items.level = parseInt( $levelHolder.attr( 'data-level' ) , 10 );
+ if( position == undefined ) position = 0;
+ var $itemGroup = $levelHolder.find( 'ul:first' );
+ $.each(items, function() {
+ if( this.name != undefined )
+ createItem( this, $levelHolder, position );
+ });
+ sizeDOMelements( instance.menuWidth );
+ return $this;
+ }
+
+ // Remove item(s)
+ function removeItems() {
+ var $items = arguments[0];
+ if( $items == undefined || typeof $items != 'object' || $items.length == 0 ) return false;
+ $items.remove();
+ var $activeMenu = activeMenu();
+ if( $activeMenu.length == 1 ) {
+ $activeMenu.css( 'visibility' , 'visible' );
+ $activeMenu.find( '.' + instance.settings.backItemClass ).css( 'visibility' , 'visible' );
+ $activeMenu.find( 'ul' ).css( 'visibility' , 'visible' );
+ $activeMenu.removeClass( instance.settings.menuInactiveClass );
+ var widthDiff = $activeMenu.width() - instance.menuWidth;
+ if( widthDiff != 0 ) {
+ var $visibleLevelHolders = visibleLevelHolders();
+ if( $visibleLevelHolders )
+ $visibleLevelHolders.each(function(){
+ $( this ).width( $( this ).width() - widthDiff );
+ });
+ }
+ }
+ sizeDOMelements( instance.menuWidth );
+ return $this;
+ }
+
+ // Manage multiple animated events and associated callbacks
+ function animatedEventCallback( animatedObjects, callbacks ) {
+ var doCallBack = true;
+ $.each( animatedObjects, function( key, val ){
+ doCallBack = doCallBack && val;
+ });
+ if( doCallBack )
+ window.setTimeout(function(){
+ $.each( callbacks, function( key, val ){
+ val['method'].apply( this, Array.prototype.slice.call( val['args'] ) );
+ });
+ }, 1);
+ }
+
+ // Get/set settings options
+ function manageOptions() {
+ var response = false;
+ if( instance.settings[arguments[0]] != undefined ) {
+ if( arguments[1] != undefined )
+ instance.settings[arguments[0]] = arguments[1];
+ response = instance.settings[arguments[0]];
+ } else {
+ $.error('No option ' + arguments[0] + ' found in jQuery.multilevelpushmenu');
+ }
+ return response;
+ }
+
+ // Mobile check
+ // http://coveroverflow.com/a/11381730/989439
+ function mobileCheck() {
+ var check = false;
+ (function(a){if(/(android|ipad|playbook|silk|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))check = true})(navigator.userAgent||navigator.vendor||window.opera);
+ return check;
+ }
+
+ if( mobileCheck() ) {
+ clickEventType = 'touchend';
+ dragEventType = 'touchmove';
+ }
+ else {
+ clickEventType = 'click';
+ dragEventType = 'mousedown';
+ }
+
+ // Invoke called method or init
+ if ( methods[options] ) {
+ returnValue = methods[options].apply(this, Array.prototype.slice.call(args, 1));
+ return returnValue;
+ } else if (typeof options === 'object' || !options) {
+ returnValue = methods.init.apply(this, arguments);
+ return returnValue;
+ } else {
+ $.error('No ' + options + ' method found in jQuery.multilevelpushmenu');
+ }
+
+ // Return object instance or option value
+ if (!returnValue) {
+ returnValue = this;
+ }
+ });
+ return returnValue;
+ }
+}( jQuery ));
diff --git a/ui/client/main.html b/ui/client/main.html
new file mode 100644
index 0000000..5aabed7
--- /dev/null
+++ b/ui/client/main.html
@@ -0,0 +1,18 @@
+<!--
+########################################################################################
+# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others #
+# #
+# All rights reserved. This program and the accompanying materials #
+# are made available under the terms of the Apache License, Version 2.0 #
+# which accompanies this distribution, and is available at #
+# http://www.apache.org/licenses/LICENSE-2.0 #
+########################################################################################
+ -->
+<head>
+ <title>Cisco Calipso</title>
+ <link rel="shortcut icon" href="/cisco-favicon.ico">
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <!-- TODO: this was need because d3.event was null when included as a module. research other ways -->
+ <script src="https://d3js.org/d3.v4.js"></script>
+</head>
diff --git a/ui/client/main.js b/ui/client/main.js
new file mode 100644
index 0000000..1502661
--- /dev/null
+++ b/ui/client/main.js
@@ -0,0 +1,10 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import '/imports/startup/client';
+import '/imports/startup/both';
diff --git a/ui/client/main.styl b/ui/client/main.styl
new file mode 100644
index 0000000..e142eda
--- /dev/null
+++ b/ui/client/main.styl
@@ -0,0 +1,85 @@
+/* CSS declarations go here */
+normalize-css()
+
+// Colors
+black = #222
+grey = #D8D8D8
+purp = #524763
+dark-purp = #373142
+red = #E54B4B
+teal = #82D8D8
+dark-teal = #66A6A6
+green = #CBE56C
+blue = #16528E
+bright-blue = #0082C8
+maroon = #6A2140
+light-red = #CA5765
+pink = #CF256D
+orange = #E8846B
+
+// cisco brand Colors
+brand-blue = #2196F3
+spark-blue = #0A9AD7
+spark-green = #73BE46
+spark-grey = #D1D2D1
+dk-blue = #303f9f
+light-grey = #efefef
+dk-gray1 = #58585b
+// cisco brand status Colors
+status-blue = #64bbe3
+status-turq = #14a792
+status-green = #6cc04a
+status-yellow = #ffcc00
+status-orange = #ff7300
+status-red = #cf2030
+// cisco accepteble colors
+
+// Material Shadows
+shadow-level(l = 1)
+ if l == -1
+ box-shadow: inset -2px 7px 9px -7px rgba(0,0,0,0.7);
+ if l == 0
+ box-shadow: none;
+ if l == 1
+ box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
+ if l == 2
+ box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);
+ if l == 3
+ box-shadow: 0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23);
+ if l == 4
+ box-shadow: 0 14px 28px rgba(0,0,0,0.25), 0 10px 10px rgba(0,0,0,0.22);
+ if l == 5
+ box-shadow: 0 19px 38px rgba(0,0,0,0.30), 0 15px 12px rgba(0,0,0,0.22);
+
+// Common Transition
+trans($sec = 0.3s)
+ transition: all $sec ease
+
+// Grid
+$xsmall = 340px
+$small = 500px
+$med = 700px
+$large = 960px
+$wide = 1200px
+
+z = {
+ neg: -7,
+ lowest: 1,
+ low: 4,
+ mid: 5,
+ video: 6,
+ header: 7,
+ nav: 8,
+ mini: 9,
+ modal: 10,
+ highest: 11
+}
+
+$side-nav = 100px
+$side-nav-large = 200px
+
+body{
+ background light-grey
+}
+
+@import 'css/*'
diff --git a/ui/client/style/d3plusgraph.css b/ui/client/style/d3plusgraph.css
new file mode 100644
index 0000000..f811c80
--- /dev/null
+++ b/ui/client/style/d3plusgraph.css
@@ -0,0 +1,14 @@
+/****************************************************************************************/
+/* Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others */
+/* */
+/* All rights reserved. This program and the accompanying materials */
+/* are made available under the terms of the Apache License, Version 2.0 */
+/* which accompanies this distribution, and is available at */
+/* http://www.apache.org/licenses/LICENSE-2.0 */
+/****************************************************************************************/
+#viz{
+ /*margin: 166px 134px;*/
+ color: white;
+ width: 90%;
+ height: 90%;
+} \ No newline at end of file
diff --git a/ui/client/style/landingpage.css b/ui/client/style/landingpage.css
new file mode 100644
index 0000000..1fd3035
--- /dev/null
+++ b/ui/client/style/landingpage.css
@@ -0,0 +1,138 @@
+/****************************************************************************************/
+/* Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others */
+/* */
+/* All rights reserved. This program and the accompanying materials */
+/* are made available under the terms of the Apache License, Version 2.0 */
+/* which accompanies this distribution, and is available at */
+/* http://www.apache.org/licenses/LICENSE-2.0 */
+/****************************************************************************************/
+/* wizard */
+.mdl-dialog {
+ width: 90vw !important;
+}
+
+.mdl-tabs__panel{
+ margin-top: 30px;
+}
+
+.mdl-dialog__content {
+ border-left: 5px solid #2196F3;
+}
+
+
+/* ======= Home page =======*/
+.background-blue{
+ background-color: #2196F3;
+ color: #f9f9f9;
+}
+.background-white {
+ border-color: #FFF;
+}
+
+.background-grey {
+ background-color: #f9f9f9;
+}
+
+.home-fullscreen {
+ /* padding: 25px; */
+}
+
+.mtop-50{
+ margin-top: 100px;
+}
+
+.font20{
+ font-size: 20px;
+}
+
+.title-border-bottom{
+ border-bottom: 5px solid #2196F3;
+}
+.text-align-center{
+ text-align: center;
+}
+
+/*
+.home_mockups {
+ position: relative;
+ margin-top: 4rem;
+ margin-bottom: -1rem;
+ z-index: 3;
+}
+
+.home_mockups_1 {
+ position: relative;
+ animation-delay: 0.5s;
+}
+
+.home_mockups_2 {
+ animation-delay: 1s;
+}
+
+.home_mockups_2, .home_mockups_3 {
+ position: absolute;
+ margin: 0 auto;
+ top: 0;
+ right: 0;
+ left: 0;
+ bottom: 0;
+}
+*/
+
+.box {
+ margin: 50px;
+}
+
+.box-side{
+ margin: 10px 50px;
+}
+/* ======= End of section =========*/
+
+
+/* ======= Login - Register pages ======= */
+.login-bg-primary {
+ width: 100%;
+ height: 30vh;
+ background-color: #2196F3;
+}
+
+.login-bg-accent {
+ width: 100%;
+ height: 30vh;
+ background-color: #9C27B0;
+}
+
+.login-card{
+ margin-top: -70px;
+ padding: 10px;
+}
+/* ======= End of section =======*/
+
+/*====== Main page =======*/
+.logo{
+ padding: 10px;
+}
+
+.view-100{
+ height: 100vh;
+}
+
+.user-bar{
+ background: #42A5F5;
+ color: #fff;
+ padding-left: 15px;
+}
+/* ======= End of section =======*/
+
+/*// Rules for sizing the icon.*/
+.material-icons.md-18 { font-size: 21px; }
+.material-icons.md-24 { font-size: 24px; }
+.material-icons.md-36 { font-size: 36px; }
+.material-icons.md-48 { font-size: 48px; }
+/*// Rules for using icons as black on a light background.*/
+.material-icons.md-dark { color: rgba(0, 0, 0, 0.54); }
+.material-icons.md-dark.md-inactive { color: rgba(0, 0, 0, 0.26); }
+/*// Rules for using icons as white on a dark background.*/
+.material-icons.md-light { color: rgba(255, 255, 255, 1); }
+.material-icons.md-light.md-inactive { color: rgba(255, 255, 255, 0.3); }
+/* ======= End of section ===========*/
diff --git a/ui/client/style/multilevelorig.css b/ui/client/style/multilevelorig.css
new file mode 100644
index 0000000..c73c7e6
--- /dev/null
+++ b/ui/client/style/multilevelorig.css
@@ -0,0 +1,43 @@
+/****************************************************************************************/
+/* Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others */
+/* */
+/* All rights reserved. This program and the accompanying materials */
+/* are made available under the terms of the Apache License, Version 2.0 */
+/* which accompanies this distribution, and is available at */
+/* http://www.apache.org/licenses/LICENSE-2.0 */
+/****************************************************************************************/
+/*
+body {
+ margin: 0;
+ padding: 0;
+ background: rgb(161, 183, 206);
+ overflow: hidden;
+}
+*/
+#menu{
+ margin-top: -50px;
+}
+#pushobj {
+ position: absolute;
+ top: 92px;
+ left: 384px;
+ color: #fff;
+ background: rgb(161, 183, 206);
+ font-family: 'Open Sans Condensed', sans-serif;
+ font-size: 2em;
+ z-index: 1;
+ width: 100%;
+ height: 89%;
+}
+/*
+.breadcrumb > li + li:before {
+ color: #CCCCCC;
+ content: "/ ";
+ padding: 0 5px;
+}
+.breadcrumb{
+ margin-bottom: 0px;
+ padding: 14px 15px;
+ font-size: 0.6em;
+}
+*/
diff --git a/ui/client/style/threeTest.css b/ui/client/style/threeTest.css
new file mode 100644
index 0000000..7a04d9c
--- /dev/null
+++ b/ui/client/style/threeTest.css
@@ -0,0 +1,65 @@
+/****************************************************************************************/
+/* Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others */
+/* */
+/* All rights reserved. This program and the accompanying materials */
+/* are made available under the terms of the Apache License, Version 2.0 */
+/* which accompanies this distribution, and is available at */
+/* http://www.apache.org/licenses/LICENSE-2.0 */
+/****************************************************************************************/
+/*
+body, html {
+ width: 100%;
+ height: 100%;
+ padding: 0;
+ margin: 0;
+ position: relative;
+ overflow: hidden;
+ background: rgb(161, 183, 206);
+}
+*/
+/*
+svg {
+ !*background-color: antiquewhite;*!
+ background-color: rgb(161, 183, 206);
+ padding-top: 100px;
+
+}
+
+.node circle {
+ fill: steelblue;
+}
+.node text {
+ font: bold 12px sans-serif;
+ fill: rgb(16, 15, 15);
+ text-shadow: 0 0px 0 #fff;
+ font-size: 0.6em;
+}
+.link {
+ fill: #1313AB;
+ stroke: #1313AB;
+}
+.default {
+ stroke: #fbb;
+ stroke-width: 4px;
+}
+.outline {
+ stroke: #844;
+ stroke-width: 6px;
+}
+.link-group text{
+ font-size: 0.5em;
+ fill: rgba(8, 8, 8, 0.73);
+}
+div.tooltip {
+ position: absolute;
+ text-align: left;
+ font: bold 18px sans-serif !important;
+ !* width: 60px; *!
+ !* height: 28px; *!
+ padding: 2px;
+ font: 16px sans-serif;
+ background: #50DFE0;
+ border: 0px;
+ border-radius: 10px;
+ pointer-events: none;
+}*/
diff --git a/ui/client/style/threeTest2.css b/ui/client/style/threeTest2.css
new file mode 100644
index 0000000..83ec163
--- /dev/null
+++ b/ui/client/style/threeTest2.css
@@ -0,0 +1,41 @@
+/****************************************************************************************/
+/* Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others */
+/* */
+/* All rights reserved. This program and the accompanying materials */
+/* are made available under the terms of the Apache License, Version 2.0 */
+/* which accompanies this distribution, and is available at */
+/* http://www.apache.org/licenses/LICENSE-2.0 */
+/****************************************************************************************/
+#canvas {
+}
+
+#canvas-force {
+ width: 100%;
+ height: 100%;
+ /*float: left;*/
+}
+
+/*
+body {
+ margin: 0px;
+}
+*/
+
+#tooltip-container {
+ position: absolute;
+ background-color: #404040;
+ color: #fff;
+ opacity: 0.9;
+ padding-left: 10px;
+ padding-right: 10px;
+ padding-top: 5px;
+ padding-bottom: 5px;
+ border: 1px solid;
+ display: none;
+ font-family: "Arial";
+ font-size: 12px;
+}
+
+.tooltip_key {
+ font-weight: bold;
+} \ No newline at end of file
diff --git a/ui/client/templates/envdialog.html b/ui/client/templates/envdialog.html
new file mode 100644
index 0000000..77d8375
--- /dev/null
+++ b/ui/client/templates/envdialog.html
@@ -0,0 +1,131 @@
+<!--
+########################################################################################
+# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others #
+# #
+# All rights reserved. This program and the accompanying materials #
+# are made available under the terms of the Apache License, Version 2.0 #
+# which accompanies this distribution, and is available at #
+# http://www.apache.org/licenses/LICENSE-2.0 #
+########################################################################################
+ -->
+<template name="envdialog">
+ <!-- <button id="show-dialog" type="button" class="mdl-button">Show Dialog</button> -->
+ <!-- Colored FAB button with ripple -->
+ <button id="show-dialog" class="mdl-button mdl-js-button mdl-button--fab mdl-js-ripple-effect mdl-button--colored">
+ <i class="material-icons">add</i>
+ </button>
+ <div class="mdl-tooltip" for="show-dialog">
+ Add new environment
+ </div>
+
+ <dialog class="mdl-dialog">
+ <h5 class="mdl-dialog__title">Add new environment</h5>
+
+ <div class="mdl-dialog__content">
+
+ <div class="mdl-tabs mdl-js-tabs mdl-js-ripple-effect">
+ <div class="mdl-tabs__tab-bar">
+ <a href="#main-info-panel" class="mdl-tabs__tab is-active">Main Info</a>
+ <a href="#endpoin-panel" class="mdl-tabs__tab">OS API endpoin</a>
+ <a href="#db-credentials-panel" class="mdl-tabs__tab">OpenStack DB Credentials</a>
+ <a href="#master-host-panel" class="mdl-tabs__tab">Master Host Credentials</a>
+ <a href="#nfv-panel" class="mdl-tabs__tab">NFV Credentials</a>
+ </div>
+
+ <div class="mdl-tabs__panel is-active" id="main-info-panel">
+ <!-- Textfield with Floating Label -->
+ <form action="#">
+ <div class="mdl-grid">
+ <div class="mdl-cell mdl-cell--4-col">
+ <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
+ <input class="mdl-textfield__input" type="text" id="ownerName" value="Koren Lev">
+ <label class="mdl-textfield__label" for="ownerName">Owner</label>
+ </div>
+ </div>
+ <div class="mdl-cell mdl-cell--8-col">
+ <h6>Admin name, owner of project</h6>
+ </div>
+ <div class="mdl-cell mdl-cell--4-col">
+ <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
+ <input class="mdl-textfield__input" type="text" id="initialEnvName">
+ <label class="mdl-textfield__label" for="initialEnvName">Enviroment name</label>
+ </div>
+ </div>
+ <div class="mdl-cell mdl-cell--8-col">
+ <h6>Enter name of your project</h6>
+ </div>
+ </div>
+ </form>
+ </div>
+
+ <div class="mdl-tabs__panel" id="endpoin-panel">
+ <form action="#">
+ <div class="mdl-grid">
+ <div class="mdl-cell mdl-cell--4-col">
+ <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
+ <input class="mdl-textfield__input" type="text" id="apiHost" pattern="-?[0-9]*(\.[0-9]+)?">
+ <label class="mdl-textfield__label" for="apiHost">API host</label>
+ <span class="mdl-textfield__error">Input is not a number!</span>
+ </div>
+ </div>
+ <div class="mdl-cell mdl-cell--8-col">
+ <h6>This is API bla bla...</h6>
+ </div>
+ <div class="mdl-cell mdl-cell--4-col">
+ <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
+ <input class="mdl-textfield__input" type="text" id="adminToken">
+ <label class="mdl-textfield__label" for="adminToken">Admin token</label>
+ </div>
+ </div>
+ <div class="mdl-cell mdl-cell--8-col">
+ <h6>You can find token ..</h6>
+ </div>
+ <div class="mdl-cell mdl-cell--4-col">
+ <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
+ <input class="mdl-textfield__input" type="text" id="apiUsername">
+ <label class="mdl-textfield__label" for="apiUsername">Username</label>
+ </div>
+ </div>
+ <div class="mdl-cell mdl-cell--8-col">
+ <h6>Some info about user name</h6>
+ </div>
+ <div class="mdl-cell mdl-cell--4-col">
+ <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
+ <input class="mdl-textfield__input" type="password" id="apiPassword">
+ <label class="mdl-textfield__label" for="apiPassword">Password</label>
+ </div>
+ </div>
+ <div class="mdl-cell mdl-cell--8-col">
+ <h6></h6>
+ </div>
+ </div>
+ </form>
+ </div>
+
+ <div class="mdl-tabs__panel" id="db-credentials-panel">
+ <ul>
+ <li>Viserys</li>
+ </ul>
+ </div>
+
+ <div class="mdl-tabs__panel" id="master-host-panel">
+ <ul>
+ <li>St</li>
+ </ul>
+ </div>
+
+ <div class="mdl-tabs__panel" id="nfv-panel">
+ <ul>
+ <li>Kor</li>
+ </ul>
+ <div class="mdl-dialog__actions">
+ <button type="button" class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect mdl-button--colored">Submit</button>
+ <button type="button" class="mdl-button close">Close</button>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ </dialog>
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/dialog-polyfill/0.4.3/dialog-polyfill.min.js"></script>
+</template>
diff --git a/ui/client/templates/header.html b/ui/client/templates/header.html
new file mode 100644
index 0000000..0bfda0b
--- /dev/null
+++ b/ui/client/templates/header.html
@@ -0,0 +1,13 @@
+<!--
+########################################################################################
+# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others #
+# #
+# All rights reserved. This program and the accompanying materials #
+# are made available under the terms of the Apache License, Version 2.0 #
+# which accompanies this distribution, and is available at #
+# http://www.apache.org/licenses/LICENSE-2.0 #
+########################################################################################
+ -->
+<template name ="header">
+
+</template> \ No newline at end of file
diff --git a/ui/client/templates/mainPage.html b/ui/client/templates/mainPage.html
new file mode 100644
index 0000000..1b85c15
--- /dev/null
+++ b/ui/client/templates/mainPage.html
@@ -0,0 +1,207 @@
+<!--
+########################################################################################
+# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others #
+# #
+# All rights reserved. This program and the accompanying materials #
+# are made available under the terms of the Apache License, Version 2.0 #
+# which accompanies this distribution, and is available at #
+# http://www.apache.org/licenses/LICENSE-2.0 #
+########################################################################################
+ -->
+<template name="mainPage">
+ <header>
+ <div class="navbar" role="navigation">
+ <div class="container">
+ <div class="navbar-header">
+ {{#if currentUser}}
+ <a class="navbar-brand" id="logo" href="{{pathFor route='home'}}">Calipso - Dashboard</a>
+ {{/if }}
+ <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#mynav">
+ <span class="sr-only">Toggle navigation</span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </button>
+ </div>
+ <div class="collapse navbar-collapse" id="mynav">
+ <ul class="nav navbar-nav navbar-right">
+ <li><a href="#about">About</a></li>
+ <li><a href="#clients">Clients</a></li>
+ <li><a href="#process">Process</a></li>
+ <li><a href="#testimonials">Testimonials</a></li>
+ <li><a href="#blogs">Blogs</a></li>
+ <li><a href="{{pathFor route='landingpage'}}">LandingPage</a></li>
+ <li><a href="#">{{> loginButtons}}</a></li>
+ </ul>
+ </div>
+ </div>
+ </div>
+ <div class="banner">
+ <h2>Calipso - OpenStack Network Discovery N Assurance</h2>
+ <div class="info">
+ <a href="#process" title="">See our process</a>
+ <a href="#clients" title="">See our clients</a>
+ </div>
+ </div>
+ </header>
+ <section class="about" id="about">
+ <div class="container">
+ <div class="row">
+ <div class="col-xs-12">
+ <h2>About</h2>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-xs-12 col-sm-4">
+ <h4>Our history</h4>
+ <p class="visible-lg">We are going to enhance the way Cloud Network Administrators (CNA) and Tenant Network Administrators (TNA) Understands, Monitors and Troubleshoot highly distributed OpenStack Environments.</p>
+ </div>
+ <div class="col-xs-12 col-sm-4">
+ <h4>Our vision & Strategy</h4>
+ <p>Vision: Substantially Simplifying OpenStack networking operations: maintenance, troubleshooting and monitoring, with added-value analytics.</p>
+ <p>Strategy: Drive the adoption of a commercial, cisco-supported neutron networking assurance application.Create Operations application that dynamically discovers, display, monitors and analyze virtual networks in any OpenStack distribution with any Neutron plugin and dynamically analyze the information for assurance reporting, potentially a commercial offering (with limited open sourced functionality), to be sold to any OpenStack customers, admins and operators.</p>
+ <p class="visible-lg">Focus on multiple distributions and multiple plugins per distribution at High performance and scale.</p>
+ </div>
+ <div class="col-xs-12 col-sm-4">
+ <h4>Application Intent</h4>
+ <p>Provide CNA and TNA with support for: </p>
+ <p>1. Building virtual Network inventory and visualizing all low level details, inter-connections in real-time. </p>
+ <p>2. Monitor virtual network objects.</p>
+ <p class="visible-lg">3. Troubleshoot failures and analyzing the root cause of failures in virtual networks. </p>
+ <p class="visible-lg">4. Assess impact of failure in virtual networks.</p>
+
+ </div>
+ </div>
+ </div>
+ </section> <!-- end of about section-->
+ <hr>
+ <section class="clients" id="clients">
+ <div class="container">
+ <div class="row">
+ <div class="col-xs-12">
+ <h2>Our Clients</h2>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-xs-12 col-sm-6 col-md-4 col-lg-3">
+ <div class="clients-info">
+ <h3>Client1</h3>
+ </div>
+ </div>
+ <div class="col-xs-12 col-sm-6 col-md-4 col-lg-3">
+ <div class="clients-info">
+ <h3>Client2</h3>
+ </div>
+ </div>
+ <div class="col-xs-12 col-sm-6 col-md-4 col-lg-3">
+ <div class="clients-info">
+ <h3>Client3</h3>
+ </div>
+ </div>
+ <div class="col-xs-12 col-sm-6 col-md-4 col-lg-3">
+ <div class="clients-info">
+ <h3>Client4</h3>
+ </div>
+ </div>
+ </div>
+ </div>
+ </section> <!-- end of clients section-->
+ <hr>
+ <section class="process" id="process">
+ <div class="container">
+ <div class="row">
+ <div class="col-xs-12">
+ <h2>Our Process</h2>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-xs-12 col-sm-6 col-md-3">
+ <div class="method">
+ <h3>Analyze</h3>
+ <hr>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p>
+ <h4>1</h4>
+ </div>
+ </div>
+ <div class="col-xs-12 col-sm-6 col-md-3">
+ <div class="method">
+ <h3>Develop</h3>
+ <hr>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p>
+ <h4>2</h4>
+ </div>
+ </div>
+ <div class="col-xs-12 col-sm-6 col-md-3">
+ <div class="method">
+ <h3>Test</h3>
+ <hr>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p>
+ <h4>3</h4>
+ </div>
+ </div>
+ <div class="col-xs-12 col-sm-6 col-md-3">
+ <div class="method">
+ <h3>Deploy</h3>
+ <hr>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p>
+ <h4>4</h4>
+ </div>
+ </div>
+ </div>
+ </div>
+ </section> <!-- end of method section-->
+ <hr>
+ <section class="testimonials" id="testimonials">
+ <div class="container">
+ <div class="row">
+ <div class="col-xs-12">
+ <h2>Our Testimonials</h2>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-xs-12 col-sm-4">
+ <blockquote>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tempore eligendi a officiis aspernatur nesciunt ducimus, placeat. Eum, ratione maxime fugiat deserunt doloremque laborum hic quisquam in, libero voluptatibus! Assumenda, eligendi.
+ </p>
+ </blockquote>
+ </div>
+ <div class="col-xs-12 col-sm-4">
+ <blockquote>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tempore eligendi a officiis aspernatur nesciunt ducimus, placeat. Eum, ratione maxime fugiat deserunt doloremque laborum hic quisquam in, libero voluptatibus! Assumenda, eligendi.
+ </p>
+ </blockquote>
+ </div>
+ <div class="col-xs-12 col-sm-4">
+ <blockquote>
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tempore eligendi a officiis aspernatur nesciunt ducimus, placeat. Eum, ratione maxime fugiat deserunt doloremque laborum hic quisquam in, libero voluptatibus! Assumenda, eligendi.
+ </p>
+ </blockquote>
+ </div>
+ </div>
+ </div>
+ </section><!-- end of testimonials section-->
+ <hr>
+ <section class="blogs" id="blogs">
+ <div class="container">
+ <div class="row">
+ <div class="col-xs-12">
+ <h2>Latest Blogs</h2>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-xs-12 col-lg-8">
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin pulvinar scelerisque aliquet. Aliquam eu ultrices nisl. Mauris sit amet odio tincidunt, congue magna a, malesuada odio. Maecenas egestas metus leo, nec vulputate odio pulvinar sodales. Aenean a nunc nisi. Donec eleifend sodales mauris, ut egestas tellus ultrices nec. Integer in nisl vitae libero lobortis egestas. Proin nisi lacus, iaculis sit amet nibh vel, fringilla pellentesque est.</p>
+ <p class="visible-lg">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ut delectus ratione aut est, minus eius, dolorem blanditiis culpa ad iusto, accusamus iure alias nihil voluptatem soluta esse velit laudantium eum.</p>
+ <p class="visible-lg">Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur.</p>
+ <button type="button" class="btn btn-primary">Learn more</button>
+ </div>
+ <div class="visible-lg col-lg-4">
+ <img class="img-responsive" src="http://placehold.it/450x300" />
+ </div>
+ </div>
+ </div>
+ </section><!-- end of blogs section-->
+ <footer>
+ <p>Made by Ofir Ashery.</p>
+ </footer>
+</template>
diff --git a/ui/contributors.md b/ui/contributors.md
new file mode 100644
index 0000000..7453147
--- /dev/null
+++ b/ui/contributors.md
@@ -0,0 +1,6 @@
+meteor Calipso UI branch
+================
+
+contacts:
+* Ofir Ashery
+* Stas Isakov
diff --git a/ui/doc/tasks/refactor-tree-node-to-component.md b/ui/doc/tasks/refactor-tree-node-to-component.md
new file mode 100644
index 0000000..ccdb870
--- /dev/null
+++ b/ui/doc/tasks/refactor-tree-node-to-component.md
@@ -0,0 +1,13 @@
+# Task
+
+## Libraries
+
+### Added
+
+#### Meteor
+
+reactive-dict
+tmeasday:publish-counts
+
+#### Node
+bcrypt - nodejs
diff --git a/ui/imports/api/accounts/methods.js b/ui/imports/api/accounts/methods.js
new file mode 100644
index 0000000..4e1c40a
--- /dev/null
+++ b/ui/imports/api/accounts/methods.js
@@ -0,0 +1,196 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { ValidatedMethod } from 'meteor/mdg:validated-method';
+import { SimpleSchema } from 'meteor/aldeed:simple-schema';
+import * as R from 'ramda';
+import { Roles } from 'meteor/alanning:roles';
+import { Environments } from '/imports/api/environments/environments';
+
+let userSchema = new SimpleSchema({
+ _id: { type: String },
+ username: { type: String },
+ password: { type: String },
+ viewEnvs: { type: [ String ] },
+ editEnvs: { type: [ String ] },
+});
+
+export const insert = new ValidatedMethod({
+ name: 'accounts.insert',
+ validate: userSchema
+ .pick([
+ 'username',
+ 'password',
+ 'viewEnvs',
+ 'viewEnvs.$',
+ 'editEnvs',
+ 'editEnvs.$',
+ ]).validator({ clean: true, filter: false }),
+ run({
+ username,
+ password,
+ viewEnvs,
+ editEnvs,
+ }) {
+ if (! Roles.userIsInRole(Meteor.userId(), 'manage-users', Roles.GLOBAL_GROUP)) {
+ throw new Meteor.Error('unauthorized for removing users');
+ }
+
+ let userId = Accounts.createUser({
+ username: username,
+ password: password
+ });
+
+ addRole(viewEnvs, 'view-env', userId);
+ addRole(editEnvs, 'edit-env', userId);
+ }
+});
+
+
+
+export const update = new ValidatedMethod({
+ name: 'accounts.update',
+ validate: userSchema
+ .pick([
+ '_id',
+ // 'password',
+ 'viewEnvs',
+ 'viewEnvs.$',
+ 'editEnvs',
+ 'editEnvs.$',
+ ]).validator({ clean: true, filter: false }),
+ run({
+ _id,
+ //_password,
+ viewEnvs,
+ editEnvs,
+ }) {
+ console.log('accounts - methods - update - start');
+ //throw new Meteor.Error('unimplemented');
+ if (! Roles.userIsInRole(Meteor.userId(), 'manage-users', Roles.GLOBAL_GROUP)) {
+ throw new Meteor.Error('unauthorized for updating users');
+ }
+
+ /*
+ let item = Meteor.users.findOne({ _id: _id });
+ console.log('user for update: ', item);
+
+ item = R.merge(R.pick([
+ 'password',
+ ], item), {
+ password
+ });
+ */
+
+ /*
+ let item = {
+ //password
+ };
+
+ Meteor.users.update({ _id: _id }, { $set: item });
+ */
+
+ let currentViewEnvs = R.map((env) => {
+ return env.name;
+ }, Environments.find({ 'auth.view-env': { $in: [ _id ] }}).fetch());
+
+ let viewEnvsForDelete = R.difference(currentViewEnvs, viewEnvs);
+ let viewEnvsForAdd = R.difference(viewEnvs, currentViewEnvs);
+
+ removeRole(viewEnvsForDelete, 'view-env', _id);
+ addRole(viewEnvsForAdd, 'view-env', _id);
+
+ //
+
+ let currentEditEnvs = R.map((env) => {
+ return env.name;
+ }, Environments.find({ 'auth.edit-env': { $in: [ _id ] }}).fetch());
+
+ let editEnvsForDelete = R.difference(currentEditEnvs, editEnvs);
+ let editEnvsForAdd = R.difference(editEnvs, currentEditEnvs);
+
+ removeRole(editEnvsForDelete, 'edit-env', _id);
+ addRole(editEnvsForAdd, 'edit-env', _id);
+
+ console.log('accounts - methods - update - end');
+ }
+});
+
+export const remove = new ValidatedMethod({
+ name: 'accounts.remove',
+ validate: userSchema
+ .pick([
+ '_id',
+ ]).validator({ clean: true, filter: false }),
+ run({
+ _id
+ }) {
+ if (! Roles.userIsInRole(Meteor.userId(), 'manage-users', Roles.GLOBAL_GROUP)) {
+ throw new Meteor.Error('unauthorized for removing users');
+ }
+
+ let user = Meteor.users.findOne({ _id: _id });
+ console.log('user for remove: ', user);
+
+ Meteor.users.remove({ _id: _id });
+ }
+});
+
+function removeRole(rolesForRemoval, roleName, userId) {
+ R.forEach((envName) => {
+ let env = Environments.findOne({ name: envName });
+ let auth = env.auth;
+ if (R.isNil(auth)) { auth = { }; }
+ if (R.isNil(R.path([roleName], auth))) {
+ auth = R.assoc(roleName, [], auth);
+ }
+ auth = R.assoc(roleName, R.reject(R.equals(userId), auth[roleName]), auth);
+
+ updateEnv(auth, env);
+ //let newEnv = R.merge(env, { auth: auth });
+
+ }, rolesForRemoval);
+}
+
+function addRole(rolesForAdd, roleName, userId) {
+ R.forEach((envName) => {
+ let env = Environments.findOne({ name: envName });
+ let auth = env.auth;
+ if (R.isNil(auth)) { auth = { }; }
+ if (R.isNil(R.path([roleName], auth))) {
+ auth = R.assoc(roleName, [], auth);
+ }
+ auth = R.assoc(roleName, R.append(userId, auth[roleName]), auth);
+
+ updateEnv(auth, env);
+ //let newEnv = R.merge(env, { auth: auth });
+
+ }, rolesForAdd);
+}
+
+function updateEnv(auth, env) {
+ console.log('update env. set: ' + R.toString(auth));
+ try {
+ Environments.update(env._id, {
+ $set: {
+ auth: auth,
+ configuration: env.configuration,
+ //distribution: distribution,
+ //name: name,
+ type_drivers: env.type_drivers,
+ mechanism_drivers: env.mechanism_drivers,
+ listen: env.listen,
+ enable_monitoring: env.enable_monitoring,
+ }
+ });
+ } catch(e) {
+ console.error('error in update: ' + R.toString(e));
+ throw new Meteor.Error('enviornment update error',
+ `unable to update ACL for environment - ${env.name}. Please check envrironment info. ${e.message}`);
+ }
+}
diff --git a/ui/imports/api/accounts/server/publications.js b/ui/imports/api/accounts/server/publications.js
new file mode 100644
index 0000000..47718d3
--- /dev/null
+++ b/ui/imports/api/accounts/server/publications.js
@@ -0,0 +1,29 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { Meteor } from 'meteor/meteor';
+//import * as R from 'ramda';
+//import { Environments } from '/imports/api/environments/environments';
+//import { Roles } from 'meteor/alanning:roles';
+
+Meteor.publish('users', function () {
+ console.log('server subscribtion to: users');
+ /*
+ let that = this;
+
+ let query = {};
+
+ if (! Roles.userIsInRole(that.userId, 'manage-users', 'default-group')) {
+ query = {
+ _id: that.userId
+ };
+ }
+ */
+
+ return Meteor.users.find({});
+});
diff --git a/ui/imports/api/attributes_for_hover_on_data/attributes_for_hover_on_data.js b/ui/imports/api/attributes_for_hover_on_data/attributes_for_hover_on_data.js
new file mode 100644
index 0000000..ec2f6cd
--- /dev/null
+++ b/ui/imports/api/attributes_for_hover_on_data/attributes_for_hover_on_data.js
@@ -0,0 +1,12 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { Mongo } from 'meteor/mongo';
+
+export const NodeHoverAttr = new Mongo.Collection(
+ 'attributes_for_hover_on_data', { idGeneration: 'MONGO' });
diff --git a/ui/imports/api/attributes_for_hover_on_data/methods.js b/ui/imports/api/attributes_for_hover_on_data/methods.js
new file mode 100644
index 0000000..1eda375
--- /dev/null
+++ b/ui/imports/api/attributes_for_hover_on_data/methods.js
@@ -0,0 +1,8 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
diff --git a/ui/imports/api/attributes_for_hover_on_data/server/publications.js b/ui/imports/api/attributes_for_hover_on_data/server/publications.js
new file mode 100644
index 0000000..bc42d58
--- /dev/null
+++ b/ui/imports/api/attributes_for_hover_on_data/server/publications.js
@@ -0,0 +1,25 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { Meteor } from 'meteor/meteor';
+
+import { NodeHoverAttr } from '../attributes_for_hover_on_data.js';
+
+Meteor.publish('attributes_for_hover_on_data', function () {
+ console.log('server subscribtion to: attributes_for_hover_on_data');
+ //return Inventory.find({$where: 'this.id_path.match('^/WebEX-Mirantis@Cisco/')'});
+ return NodeHoverAttr.find({});
+});
+
+Meteor.publish('attributes_for_hover_on_data?type', function (type) {
+ console.log('server subscribtion to: attributes_for_hover_on_data?type');
+ console.log('- type: ' + type);
+
+ //return Inventory.find({$where: 'this.id_path.match('^/WebEX-Mirantis@Cisco/')'});
+ return NodeHoverAttr.find({ 'type': type});
+});
diff --git a/ui/imports/api/clique-constraints/clique-constraints.js b/ui/imports/api/clique-constraints/clique-constraints.js
new file mode 100644
index 0000000..8641715
--- /dev/null
+++ b/ui/imports/api/clique-constraints/clique-constraints.js
@@ -0,0 +1,48 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { Mongo } from 'meteor/mongo';
+import { SimpleSchema } from 'meteor/aldeed:simple-schema';
+import * as R from 'ramda';
+import { Constants } from '/imports/api/constants/constants';
+
+export const CliqueConstraints = new Mongo.Collection(
+ 'clique_constraints', { idGeneration: 'MONGO' });
+
+let schema = {
+ _id: { type: { _str: { type: String, regEx: SimpleSchema.RegEx.Id } } },
+
+ focal_point_type: {
+ type: String,
+ custom: function () {
+ let that = this;
+ let values = Constants.findOne({ name: 'object_types_for_links' }).data;
+
+ if (R.isNil(R.find(R.propEq('value', that.value), values))) {
+ return 'notAllowed';
+ }
+ }
+ },
+
+ constraints: {
+ type: [String],
+ minCount: 1,
+ custom: function () {
+ let that = this;
+ let objectTypes = Constants.findOne({ name: 'object_types_for_links' }).data;
+
+ let findResult = R.intersection(that.value, R.pluck('value', objectTypes));
+ if (findResult.length !== that.value.length) { return 'notAllowed'; }
+
+ return;
+ },
+ },
+};
+
+CliqueConstraints.schema = new SimpleSchema(schema);
+CliqueConstraints.attachSchema(CliqueConstraints.schema);
diff --git a/ui/imports/api/clique-constraints/methods.js b/ui/imports/api/clique-constraints/methods.js
new file mode 100644
index 0000000..c9ae997
--- /dev/null
+++ b/ui/imports/api/clique-constraints/methods.js
@@ -0,0 +1,99 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { ValidatedMethod } from 'meteor/mdg:validated-method';
+import * as R from 'ramda';
+import { Roles } from 'meteor/alanning:roles';
+
+import { CliqueConstraints } from './clique-constraints';
+
+export const insert = new ValidatedMethod({
+ name: 'clique_constraints.insert',
+ validate: CliqueConstraints.simpleSchema()
+ .pick([
+// 'environment',
+ 'focal_point_type',
+ 'constraints',
+ 'constraints.$',
+ ]).validator({ clean: true, filter: false }),
+ run({
+ // environment,
+ focal_point_type,
+ constraints,
+ }) {
+ if (! Roles.userIsInRole(Meteor.userId(), 'manage-clique-constraints', Roles.GLOBAL_GROUP)) {
+ throw new Meteor.Error('unauthorized for inserting clique constraints');
+ }
+
+ let cliqueConstraint = CliqueConstraints.schema.clean({});
+
+ cliqueConstraint = R.merge(cliqueConstraint, {
+ // environment,
+ focal_point_type,
+ constraints,
+ });
+
+ CliqueConstraints.insert(cliqueConstraint);
+ }
+});
+
+export const remove = new ValidatedMethod({
+ name: 'clique_constraints.remove',
+ validate: CliqueConstraints.simpleSchema()
+ .pick([
+ '_id',
+ ]).validator({ clean: true, filter: false }),
+ run({
+ _id
+ }) {
+ if (! Roles.userIsInRole(Meteor.userId(), 'manage-clique-constraints', Roles.DEFAULT_GROUP)) {
+ throw new Meteor.Error('unauthorized for removing clique constraints');
+ }
+
+ let cliqueConstraint = CliqueConstraints.findOne({ _id: _id });
+ console.log('clique constraint for remove: ', cliqueConstraint);
+
+ CliqueConstraints.remove({ _id: _id });
+ }
+});
+
+export const update = new ValidatedMethod({
+ name: 'clique_constraints.update',
+ validate: CliqueConstraints.simpleSchema()
+ .pick([
+ '_id',
+ 'focal_point_type',
+ 'constraints',
+ 'constraints.$',
+ ]).validator({ clean: true, filter: false }),
+ run({
+ _id,
+ focal_point_type,
+ constraints,
+ }) {
+
+ if (! Roles.userIsInRole(Meteor.userId(), 'manage-clique-constraints', Roles.DEFAULT_GROUP)) {
+ throw new Meteor.Error('unauthorized for removing clique constraints');
+ }
+
+ let item = CliqueConstraints.findOne({ _id: _id });
+ console.log('clique constraints for update: ', item);
+ console.log('current user', Meteor.userId());
+
+ item = R.merge(
+ R.pick([
+ 'focal_point_type',
+ 'constraints',
+ ], item), {
+ focal_point_type,
+ constraints,
+ });
+
+ CliqueConstraints.update({ _id: _id }, { $set: item });
+ }
+});
diff --git a/ui/imports/api/clique-constraints/server/publications.js b/ui/imports/api/clique-constraints/server/publications.js
new file mode 100644
index 0000000..6e4ae1a
--- /dev/null
+++ b/ui/imports/api/clique-constraints/server/publications.js
@@ -0,0 +1,30 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { Meteor } from 'meteor/meteor';
+
+import { CliqueConstraints } from '../clique-constraints.js';
+
+Meteor.publish('clique_constraints', function () {
+ console.log('server subscribtion: clique_constraints');
+
+ //let that = this;
+
+ let query = {};
+ return CliqueConstraints.find(query);
+});
+
+Meteor.publish('clique_constraints?_id', function (_id) {
+ console.log('server subscribtion: clique_constraints?_id');
+ console.log(_id);
+
+ //let that = this;
+
+ let query = { _id: _id };
+ return CliqueConstraints.find(query);
+});
diff --git a/ui/imports/api/clique-types/clique-types.js b/ui/imports/api/clique-types/clique-types.js
new file mode 100644
index 0000000..852c319
--- /dev/null
+++ b/ui/imports/api/clique-types/clique-types.js
@@ -0,0 +1,107 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { Mongo } from 'meteor/mongo';
+import { SimpleSchema } from 'meteor/aldeed:simple-schema';
+import * as R from 'ramda';
+import { Constants } from '/imports/api/constants/constants';
+import { Environments } from '/imports/api/environments/environments';
+import { LinkTypes } from '/imports/api/link-types/link-types';
+
+export const CliqueTypes = new Mongo.Collection(
+ 'clique_types', { idGeneration: 'MONGO' });
+
+let schema = {
+ _id: { type: { _str: { type: String, regEx: SimpleSchema.RegEx.Id } } },
+
+ environment: {
+ type: String,
+ custom: function () {
+ let that = this;
+ let env = Environments.findOne({ name: that.value });
+
+ if (R.isNil(env)) {
+ return 'notAllowed';
+ }
+ }
+ },
+
+ focal_point_type: {
+ type: String,
+ custom: function () {
+ let that = this;
+ let values = Constants.findOne({ name: 'object_types_for_links' }).data;
+
+ if (R.isNil(R.find(R.propEq('value', that.value), values))) {
+ return 'notAllowed';
+ }
+ }
+ },
+
+ link_types: {
+ type: [String],
+ minCount: 1,
+ custom: function () {
+ let that = this;
+ let findResult = R.all(function (pLinkType) {
+ if (R.isNil(LinkTypes.findOne({ type: pLinkType }))) {
+ return false;
+ }
+
+ return true;
+ }, that.value);
+
+ if (! findResult) { return 'notAllowed'; }
+
+ return;
+ },
+ },
+
+ name: {
+ type: String
+ },
+};
+
+let simpleSchema = new SimpleSchema(schema);
+
+simpleSchema.addValidator(function () {
+ let that = this;
+
+ let existing = CliqueTypes.findOne({
+ environment: that.field('environment').value,
+ focal_point_type: that.field('focal_point_type').value
+ });
+
+ if (R.allPass([
+ R.pipe(R.isNil, R.not),
+ R.pipe(R.propEq('_id', that.docId), R.not)
+ ])(existing)) {
+
+ return 'alreadyExists';
+ }
+});
+
+simpleSchema.addValidator(function () {
+ let that = this;
+
+ let existing = CliqueTypes.findOne({
+ environment: that.field('environment').value,
+ name: that.field('name').value
+ });
+
+ if (R.allPass([
+ R.pipe(R.isNil, R.not),
+ R.pipe(R.propEq('_id', that.docId), R.not)
+ ])(existing)) {
+
+ return 'alreadyExists';
+ }
+});
+
+CliqueTypes.schema = simpleSchema;
+CliqueTypes.attachSchema(CliqueTypes.schema);
diff --git a/ui/imports/api/clique-types/methods.js b/ui/imports/api/clique-types/methods.js
new file mode 100644
index 0000000..a62c22f
--- /dev/null
+++ b/ui/imports/api/clique-types/methods.js
@@ -0,0 +1,108 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { ValidatedMethod } from 'meteor/mdg:validated-method';
+import * as R from 'ramda';
+import { Roles } from 'meteor/alanning:roles';
+
+import { CliqueTypes } from './clique-types';
+
+export const insert = new ValidatedMethod({
+ name: 'clique_types.insert',
+ validate: CliqueTypes.simpleSchema()
+ .pick([
+ 'environment',
+ 'focal_point_type',
+ 'link_types',
+ 'link_types.$',
+ 'name',
+ ]).validator({ clean: true, filter: false }),
+ run({
+ environment,
+ focal_point_type,
+ link_types,
+ name,
+ }) {
+ if (! Roles.userIsInRole(Meteor.userId(), 'manage-clique-types', Roles.DEFAULT_GROUP)) {
+ throw new Meteor.Error('unauthorized for adding clique type');
+ }
+
+ let cliqueType = CliqueTypes.schema.clean({});
+
+ cliqueType = R.merge(cliqueType, {
+ environment,
+ focal_point_type,
+ link_types,
+ name,
+ });
+
+ CliqueTypes.insert(cliqueType);
+ }
+});
+
+export const remove = new ValidatedMethod({
+ name: 'clique_types.remove',
+ validate: CliqueTypes.simpleSchema()
+ .pick([
+ '_id',
+ ]).validator({ clean: true, filter: false }),
+ run({
+ _id
+ }) {
+
+ if (! Roles.userIsInRole(Meteor.userId(), 'manage-clique-types', Roles.DEFAULT_GROUP)) {
+ throw new Meteor.Error('unauthorized for removing clique type');
+ }
+
+ let cliqueType = CliqueTypes.findOne({ _id: _id });
+ console.log('clique type for remove: ', cliqueType);
+
+ CliqueTypes.remove({ _id: _id });
+ }
+});
+
+export const update = new ValidatedMethod({
+ name: 'clique_types.update',
+ validate: CliqueTypes.simpleSchema()
+ .pick([
+ '_id',
+ 'environment',
+ 'focal_point_type',
+ 'link_types',
+ 'link_types.$',
+ 'name',
+ ]).validator({ clean: true, filter: false }),
+ run({
+ _id,
+ environment,
+ focal_point_type,
+ link_types,
+ name,
+ }) {
+ if (! Roles.userIsInRole(Meteor.userId(), 'manage-clique-types', Roles.DEFAULT_GROUP)) {
+ throw new Meteor.Error('unauthorized for updating clique type');
+ }
+
+ let cliqueType = CliqueTypes.findOne({ _id: _id });
+ console.log('clique type for remove: ', cliqueType);
+
+ cliqueType = R.merge(R.pick([
+ 'environment',
+ 'focal_point_type',
+ 'link_types',
+ 'name', ],
+ cliqueType), {
+ environment,
+ focal_point_type,
+ link_types,
+ name,
+ });
+
+ CliqueTypes.update({ _id: _id }, { $set: cliqueType });
+ }
+});
diff --git a/ui/imports/api/clique-types/server/publications.js b/ui/imports/api/clique-types/server/publications.js
new file mode 100644
index 0000000..95274b9
--- /dev/null
+++ b/ui/imports/api/clique-types/server/publications.js
@@ -0,0 +1,34 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { Meteor } from 'meteor/meteor';
+import * as R from 'ramda';
+
+import { CliqueTypes } from '../clique-types.js';
+
+Meteor.publish('clique_types?env*', function (env) {
+ console.log('server subscribtion: clique_types?env*');
+ console.log(env);
+
+ //let that = this;
+
+ let query = {};
+ if (! R.isNil(env)) { query = R.assoc('environment', env, query); }
+ console.log('-query: ', query);
+ return CliqueTypes.find(query);
+});
+
+Meteor.publish('clique_types?_id', function (_id) {
+ console.log('server subscribtion: clique_types?_id');
+ console.log(_id);
+
+ //let that = this;
+
+ let query = { _id: _id };
+ return CliqueTypes.find(query);
+});
diff --git a/ui/imports/api/cliques/cliques.js b/ui/imports/api/cliques/cliques.js
new file mode 100644
index 0000000..78fb7ad
--- /dev/null
+++ b/ui/imports/api/cliques/cliques.js
@@ -0,0 +1,12 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { Mongo } from 'meteor/mongo';
+
+export const Cliques = new Mongo.Collection(
+ 'cliques', { idGeneration: 'MONGO' });
diff --git a/ui/imports/api/cliques/methods.js b/ui/imports/api/cliques/methods.js
new file mode 100644
index 0000000..1eda375
--- /dev/null
+++ b/ui/imports/api/cliques/methods.js
@@ -0,0 +1,8 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
diff --git a/ui/imports/api/cliques/server/publications.js b/ui/imports/api/cliques/server/publications.js
new file mode 100644
index 0000000..16a4644
--- /dev/null
+++ b/ui/imports/api/cliques/server/publications.js
@@ -0,0 +1,33 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { Meteor } from 'meteor/meteor';
+
+import { Cliques } from '../cliques.js';
+
+Meteor.publish('cliques', function () {
+ console.log('server subscribtion to: cliques');
+ //return Inventory.find({$where: 'this.id_path.match('^/WebEX-Mirantis@Cisco/')'});
+ return Cliques.find({});
+});
+
+Meteor.publish('cliques?focal_point', function (objId) {
+ var query = {
+ focal_point: new Mongo.ObjectID(objId)
+ };
+/*
+ var counterName = 'inventory?env+type!counter?env=' + env + '&type=' + type;
+
+ console.log('server subscribing to counter: ' + counterName);
+ Counts.publish(this, counterName, Inventory.find(query));
+*/
+
+ console.log('server subscribtion to: cliques?focal_point');
+ console.log('- focal_point: ' + objId);
+ return Cliques.find(query);
+});
diff --git a/ui/imports/api/constants/constants.js b/ui/imports/api/constants/constants.js
new file mode 100644
index 0000000..b3f0407
--- /dev/null
+++ b/ui/imports/api/constants/constants.js
@@ -0,0 +1,22 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { Mongo } from 'meteor/mongo';
+import { SimpleSchema } from 'meteor/aldeed:simple-schema';
+//import * as R from 'ramda';
+
+export const Constants = new Mongo.Collection('constants', { idGeneration: 'MONGO' });
+
+let schema = {
+ _id: { type: { _str: { type: String, regEx: SimpleSchema.RegEx.Id } } },
+ name: { type: String },
+ data: { type: [Object], blackbox: true },
+};
+
+Constants.schema = schema;
+Constants.attachSchema(schema);
diff --git a/ui/imports/api/constants/data/distributions.js b/ui/imports/api/constants/data/distributions.js
new file mode 100644
index 0000000..97ecdb4
--- /dev/null
+++ b/ui/imports/api/constants/data/distributions.js
@@ -0,0 +1,64 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+export const Distributions = [{
+ label: 'Mirantis-6.0',
+ value: 'Mirantis-6.0',
+}, {
+ label: 'Mirantis-7.0',
+ value: 'Mirantis-7.0',
+}, {
+ label: 'Mirantis-8.0',
+ value: 'Mirantis-8.0',
+}, {
+ label: 'Mirantis-9.0',
+ value: 'Mirantis-9.0',
+}, {
+ label: 'RDO-Mitaka',
+ value: 'RDO-Mitaka',
+}, {
+ label: 'RDO-Liberty',
+ value: 'RDO-Liberty',
+}, {
+ label: 'RDO-Juno',
+ value: 'RDO-Juno',
+}, {
+ label: 'RDO-kilo',
+ value: 'RDO-kilo',
+}, {
+ label: 'devstack-liberty',
+ value: 'devstack-liberty',
+}, {
+ label: 'Canonical-icehouse',
+ value: 'Canonical-icehouse',
+}, {
+ label: 'Canonical-juno',
+ value: 'Canonical-juno',
+}, {
+ label: 'Canonical-liberty',
+ value: 'Canonical-liberty',
+}, {
+ label: 'Canonical-mitaka',
+ value: 'Canonical-mitaka',
+}, {
+ label: 'Apex-Mitaka',
+ value: 'Apex-Mitaka',
+}, {
+ label: 'Devstack-Mitaka',
+ value: 'Devstack-Mitaka',
+}, {
+ label: 'packstack-7.0.0-0.10.dev1682',
+ value: 'packstack-7.0.0-0.10.dev1682',
+}, {
+ label: 'Stratoscale-v2.1.6',
+ value: 'Stratoscale-v2.1.6',
+}, {
+ label: 'Mirantis-9.1',
+ value: 'Mirantis-9.1',
+}
+];
diff --git a/ui/imports/api/constants/data/env-types.js b/ui/imports/api/constants/data/env-types.js
new file mode 100644
index 0000000..00b0aaf
--- /dev/null
+++ b/ui/imports/api/constants/data/env-types.js
@@ -0,0 +1,15 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+export const EnvTypes = [{
+ label: 'Production',
+ value: 'production',
+}, {
+ label: 'Development',
+ value: 'development',
+}];
diff --git a/ui/imports/api/constants/data/environment-monitoring-types.js b/ui/imports/api/constants/data/environment-monitoring-types.js
new file mode 100644
index 0000000..e3a573a
--- /dev/null
+++ b/ui/imports/api/constants/data/environment-monitoring-types.js
@@ -0,0 +1,12 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+export const EnvironmentMonitoringTypes = [{
+ label: 'Sensu',
+ value: 'Sensu',
+}];
diff --git a/ui/imports/api/constants/data/environment-provision-types.js b/ui/imports/api/constants/data/environment-provision-types.js
new file mode 100644
index 0000000..5139266
--- /dev/null
+++ b/ui/imports/api/constants/data/environment-provision-types.js
@@ -0,0 +1,21 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+export const EnvProvisionTypes = [{
+ label: 'None',
+ value: 'None',
+}, {
+ label: 'Deploy',
+ value: 'Deploy',
+}, {
+ label: 'Files',
+ value: 'Files',
+}, {
+ label: 'DB',
+ value: 'DB',
+}];
diff --git a/ui/imports/api/constants/data/log-levels.js b/ui/imports/api/constants/data/log-levels.js
new file mode 100644
index 0000000..dee6b6d
--- /dev/null
+++ b/ui/imports/api/constants/data/log-levels.js
@@ -0,0 +1,27 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+export const LogLevels = [{
+ label: 'CRITICAL',
+ value: 'critical',
+}, {
+ label: 'ERROR',
+ value: 'error',
+}, {
+ label: 'WARNING',
+ value: 'warning',
+}, {
+ label: 'INFO',
+ value: 'info',
+}, {
+ label: 'DEBUG',
+ value: 'debug',
+}, {
+ label: 'NOTSET',
+ value: 'notset',
+}];
diff --git a/ui/imports/api/constants/data/mechanism-drivers.js b/ui/imports/api/constants/data/mechanism-drivers.js
new file mode 100644
index 0000000..afa8b01
--- /dev/null
+++ b/ui/imports/api/constants/data/mechanism-drivers.js
@@ -0,0 +1,24 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+export const MechanismDrivers = [{
+ 'label' : 'ovs',
+ 'value' : 'ovs'
+}, {
+ 'label' : 'vpp',
+ 'value' : 'vpp'
+}, {
+ 'label' : 'lxb',
+ 'value' : 'lxb'
+}, {
+ 'label' : 'Arista',
+ 'value' : 'Arista'
+}, {
+ 'label' : 'Nexus',
+ 'value' : 'Nexus'
+}];
diff --git a/ui/imports/api/constants/data/message-source-systems.js b/ui/imports/api/constants/data/message-source-systems.js
new file mode 100644
index 0000000..77ec901
--- /dev/null
+++ b/ui/imports/api/constants/data/message-source-systems.js
@@ -0,0 +1,15 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+export const MessageSourceSystems = [{
+ label: 'OpenStack',
+ value: 'OpenStack',
+}, {
+ label: 'OSDNA_Sensu',
+ value: 'OSDNA_Sensu',
+}];
diff --git a/ui/imports/api/constants/data/network-plugins.js b/ui/imports/api/constants/data/network-plugins.js
new file mode 100644
index 0000000..c89be26
--- /dev/null
+++ b/ui/imports/api/constants/data/network-plugins.js
@@ -0,0 +1,15 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+export const NetworkPlugins = [{
+ label: 'OVS',
+ value: 'OVS',
+}, {
+ label: 'VPP',
+ value: 'VPP',
+}];
diff --git a/ui/imports/api/constants/data/object-types-for-links.js b/ui/imports/api/constants/data/object-types-for-links.js
new file mode 100644
index 0000000..35f1805
--- /dev/null
+++ b/ui/imports/api/constants/data/object-types-for-links.js
@@ -0,0 +1,39 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+export const ObjectTypesForLinks = [{
+ label: 'vnic',
+ value: 'vnic',
+}, {
+ label: 'vconnector',
+ value: 'vconnector',
+}, {
+ label: 'vedge',
+ value: 'vedge',
+}, {
+ label: 'instance',
+ value: 'instance',
+}, {
+ label: 'vservice',
+ value: 'vservice',
+}, {
+ label: 'pnic',
+ value: 'pnic',
+}, {
+ label: 'network',
+ value: 'network',
+}, {
+ label: 'port',
+ value: 'port',
+}, {
+ label: 'otep',
+ value: 'otep',
+}, {
+ label: 'agent',
+ value: 'agent',
+}];
diff --git a/ui/imports/api/constants/data/scans-statuses.js b/ui/imports/api/constants/data/scans-statuses.js
new file mode 100644
index 0000000..778f256
--- /dev/null
+++ b/ui/imports/api/constants/data/scans-statuses.js
@@ -0,0 +1,30 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+export const Statuses = [{
+ value: 'draft',
+ label: 'Draft',
+}, {
+ value: 'pending',
+ label: 'Pending',
+}, {
+ value: 'running',
+ label: 'Running',
+}, {
+ value: 'completed',
+ label: 'Completed',
+}, {
+ value: 'failed',
+ label: 'Failed',
+}, {
+ value: 'aborted',
+ label: 'Aborted',
+}
+];
+
+export const StatusesInOperation = ['pending', 'running'];
diff --git a/ui/imports/api/constants/data/type-drivers.js b/ui/imports/api/constants/data/type-drivers.js
new file mode 100644
index 0000000..efc7f7d
--- /dev/null
+++ b/ui/imports/api/constants/data/type-drivers.js
@@ -0,0 +1,24 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+export const TypeDrivers = [{
+ 'label' : 'local',
+ 'value' : 'local'
+}, {
+ 'label' : 'vlan',
+ 'value' : 'vlan'
+}, {
+ 'label' : 'vxlan',
+ 'value' : 'vxlan'
+}, {
+ 'label' : 'gre',
+ 'value' : 'gre'
+}, {
+ 'label' : 'flat',
+ 'value' : 'flat'
+}];
diff --git a/ui/imports/api/constants/server/publications.js b/ui/imports/api/constants/server/publications.js
new file mode 100644
index 0000000..3ace17f
--- /dev/null
+++ b/ui/imports/api/constants/server/publications.js
@@ -0,0 +1,16 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { Meteor } from 'meteor/meteor';
+
+import { Constants } from '../constants.js';
+
+Meteor.publish('constants', function () {
+ console.log('server subscribtion to: constants');
+ return Constants.find({});
+});
diff --git a/ui/imports/api/environments/configuration-groups/aci-configuration.js b/ui/imports/api/environments/configuration-groups/aci-configuration.js
new file mode 100644
index 0000000..10b749e
--- /dev/null
+++ b/ui/imports/api/environments/configuration-groups/aci-configuration.js
@@ -0,0 +1,29 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { SimpleSchema } from 'meteor/aldeed:simple-schema';
+
+export const AciSchema = new SimpleSchema({
+ name: {
+ type: String,
+ autoValue: function () { return 'ACI'; }
+ },
+ host: {
+ type: String,
+ regEx: SimpleSchema.RegEx.IP,
+ defaultValue: '10.56.0.104',
+ },
+ user: {
+ type: String,
+ defaultValue: 'admin'
+ },
+ pwd: {
+ type: String,
+ defaultValue: 'C1sco12345'
+ },
+});
diff --git a/ui/imports/api/environments/configuration-groups/amqp-configuration.js b/ui/imports/api/environments/configuration-groups/amqp-configuration.js
new file mode 100644
index 0000000..83a15cf
--- /dev/null
+++ b/ui/imports/api/environments/configuration-groups/amqp-configuration.js
@@ -0,0 +1,29 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { SimpleSchema } from 'meteor/aldeed:simple-schema';
+import { portRegEx } from '/imports/lib/general-regex';
+
+export const AMQPSchema = new SimpleSchema({
+ name: { type: String, autoValue: function () { return 'AMQP'; } },
+ host: {
+ type: String,
+ regEx: SimpleSchema.RegEx.IP,
+ defaultValue: '10.0.0.1',
+ },
+ port: {
+ type: String,
+ regEx: portRegEx,
+ defaultValue: '5673',
+ },
+ user: {
+ type: String,
+ defaultValue: 'rabbitmquser'
+ },
+ password: { type: String },
+});
diff --git a/ui/imports/api/environments/configuration-groups/cli-configuration.js b/ui/imports/api/environments/configuration-groups/cli-configuration.js
new file mode 100644
index 0000000..c651359
--- /dev/null
+++ b/ui/imports/api/environments/configuration-groups/cli-configuration.js
@@ -0,0 +1,69 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import * as R from 'ramda';
+import { SimpleSchema } from 'meteor/aldeed:simple-schema';
+import { pathRegEx } from '/imports/lib/general-regex';
+
+export const CLISchema = new SimpleSchema({
+ name: { type: String, autoValue: function () { return 'CLI'; } },
+ host: {
+ type: String,
+ defaultValue: '10.0.0.1'
+ },
+ key: {
+ type: String,
+ regEx: pathRegEx,
+ optional: true
+ },
+ user: {
+ type: String,
+ defaultValue: 'sshuser'
+ },
+ pwd: {
+ type: String,
+ optional: true
+ },
+});
+
+CLISchema.addValidator(function () {
+ let that = this;
+
+ let conf = {};
+ if (isConfEmpty(conf)) {
+ return;
+ }
+
+ let validationResult = R.find((validationFn) => {
+ return validationFn(that).isError;
+ }, [ keyPasswordValidation ]);
+
+ if (R.isNil(validationResult)) { return; }
+
+ throw validationResult(that);
+});
+
+function keyPasswordValidation(schemaItem) {
+ let password = schemaItem.field('pwd');
+ let key = schemaItem.field('key');
+
+ if (key.value || password.value) { return { isError: false }; }
+
+ return {
+ isError: true,
+ type: 'subGroupError',
+ data: [],
+ message: 'Master Host Group: At least one required: key or password'
+ };
+}
+
+function isConfEmpty(conf) {
+ return R.find((key) => {
+ return !(R.isNil(conf[key]));
+ }, R.keys(conf));
+}
diff --git a/ui/imports/api/environments/configuration-groups/monitoring-configuration.js b/ui/imports/api/environments/configuration-groups/monitoring-configuration.js
new file mode 100644
index 0000000..2b27f8a
--- /dev/null
+++ b/ui/imports/api/environments/configuration-groups/monitoring-configuration.js
@@ -0,0 +1,119 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { SimpleSchema } from 'meteor/aldeed:simple-schema';
+import * as R from 'ramda';
+import { Constants } from '/imports/api/constants/constants';
+import { portRegEx } from '/imports/lib/general-regex';
+import { hostnameRegex } from '/imports/lib/general-regex';
+import { ipAddressRegex } from '/imports/lib/general-regex';
+import { pathRegEx } from '/imports/lib/general-regex';
+
+export const MonitoringSchema = new SimpleSchema({
+ name: { type: String, autoValue: function () { return 'Monitoring'; } },
+ //app_path: { type: String, autoValue: function () { return '/etc/calipso/monitoring'; } },
+
+ config_folder: {
+ type: String,
+ defaultValue: '/local_dir/sensu_config',
+ regEx: pathRegEx,
+ },
+
+ env_type: {
+ type: String,
+ defaultValue: 'production',
+ custom: function () {
+ let that = this;
+ let EnvTypesRec = Constants.findOne({ name: 'env_types' });
+
+ if (R.isNil(EnvTypesRec.data)) { return 'notAllowed'; }
+ let EnvTypes = EnvTypesRec.data;
+
+ if (R.isNil(R.find(R.propEq('value', that.value), EnvTypes))) {
+ return 'notAllowed';
+ }
+ },
+ },
+
+ rabbitmq_port: {
+ type: String,
+ defaultValue: '5671',
+ regEx: portRegEx,
+ },
+
+ rabbitmq_user: {
+ type: String,
+ defaultValue: 'sensu'
+ },
+
+ rabbitmq_pass: { type: String },
+
+ server_ip: {
+ type: String,
+ regEx: new RegExp(hostnameRegex.source + '|' + ipAddressRegex.soure),
+ defaultValue: '10.0.0.1',
+ },
+
+ server_name: {
+ type: String,
+ defaultValue: 'sensu_server',
+ },
+
+ type: {
+ type: String,
+ defaultValue: 'Sensu',
+ custom: function () {
+ let that = this;
+ let values = Constants.findOne({ name: 'environment_monitoring_types' }).data;
+
+ if (R.isNil(values)) { return 'notAllowed'; }
+
+ if (R.isNil(R.find(R.propEq('value', that.value), values))) {
+ return 'notAllowed';
+ }
+ },
+ },
+
+ provision: {
+ type: String,
+ defaultValue: 'None',
+ custom: function () {
+ let that = this;
+ let values = Constants.findOne({ name: 'environment_provision_types' }).data;
+
+ if (R.isNil(values)) { return 'notAllowed'; }
+
+ if (R.isNil(R.find(R.propEq('value', that.value), values))) {
+ return 'notAllowed';
+ }
+ },
+ },
+
+ ssh_port: {
+ type: String,
+ defaultValue: '20022',
+ optional: true
+ },
+
+ ssh_user: {
+ type: String,
+ defaultValue: 'root',
+ optional: true
+ },
+
+ ssh_password: {
+ type: String,
+ defaultValue: 'calipso',
+ optional: true
+ },
+
+ api_port: {
+ type: Number,
+ defaultValue: 4567,
+ },
+});
diff --git a/ui/imports/api/environments/configuration-groups/mysql-configuration.js b/ui/imports/api/environments/configuration-groups/mysql-configuration.js
new file mode 100644
index 0000000..1921432
--- /dev/null
+++ b/ui/imports/api/environments/configuration-groups/mysql-configuration.js
@@ -0,0 +1,33 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { SimpleSchema } from 'meteor/aldeed:simple-schema';
+import { portRegEx } from '/imports/lib/general-regex';
+
+export const MysqlSchema = new SimpleSchema({
+ name: {
+ type: String,
+ autoValue: function () { return 'mysql'; }
+ },
+ host: {
+ type: String,
+ regEx: SimpleSchema.RegEx.IP,
+ defaultValue: '10.0.0.1'
+ },
+ password: { type: String },
+ port: {
+ type: String,
+ regEx: portRegEx,
+ defaultValue: '3307'
+ },
+ user: {
+ type: String,
+ min: 3,
+ defaultValue: 'mysqluser'
+ },
+});
diff --git a/ui/imports/api/environments/configuration-groups/nfv-provider-configuration.js b/ui/imports/api/environments/configuration-groups/nfv-provider-configuration.js
new file mode 100644
index 0000000..3638e3b
--- /dev/null
+++ b/ui/imports/api/environments/configuration-groups/nfv-provider-configuration.js
@@ -0,0 +1,25 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { SimpleSchema } from 'meteor/aldeed:simple-schema';
+import { portRegEx } from '/imports/lib/general-regex';
+
+export const NfvProviderSchema = new SimpleSchema({
+ name: { type: String, autoValue: function () { return 'NFV_provider'; } },
+ host: {
+ type: String,
+ regEx: SimpleSchema.RegEx.IP,
+ },
+ nfv_token: { type: String },
+ port: {
+ type: String,
+ regEx: portRegEx
+ },
+ user: { type: String },
+ pwd: { type: String },
+});
diff --git a/ui/imports/api/environments/configuration-groups/open-stack-configuration.js b/ui/imports/api/environments/configuration-groups/open-stack-configuration.js
new file mode 100644
index 0000000..a0d710f
--- /dev/null
+++ b/ui/imports/api/environments/configuration-groups/open-stack-configuration.js
@@ -0,0 +1,30 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { SimpleSchema } from 'meteor/aldeed:simple-schema';
+import { portRegEx } from '/imports/lib/general-regex';
+
+export const OpenStackSchema = new SimpleSchema({
+ name: { type: String, autoValue: function () { return 'OpenStack'; } },
+ host: {
+ type: String,
+ regEx: SimpleSchema.RegEx.IP,
+ defaultValue: '10.0.0.1',
+ },
+ admin_token: { type: String },
+ port: {
+ type: String,
+ regEx: portRegEx,
+ defaultValue: '5000',
+ },
+ user: {
+ type: String,
+ defaultValue: 'adminuser'
+ },
+ pwd: { type: String },
+});
diff --git a/ui/imports/api/environments/environments.js b/ui/imports/api/environments/environments.js
new file mode 100644
index 0000000..d616960
--- /dev/null
+++ b/ui/imports/api/environments/environments.js
@@ -0,0 +1,457 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { Mongo } from 'meteor/mongo';
+import { SimpleSchema } from 'meteor/aldeed:simple-schema';
+import * as R from 'ramda';
+import { Constants } from '/imports/api/constants/constants';
+import { MysqlSchema } from './configuration-groups/mysql-configuration';
+import { OpenStackSchema } from './configuration-groups/open-stack-configuration';
+import { MonitoringSchema } from './configuration-groups/monitoring-configuration';
+import { CLISchema } from './configuration-groups/cli-configuration';
+import { AMQPSchema } from './configuration-groups/amqp-configuration';
+//import { NfvProviderSchema } from './configuration-groups/nfv-provider-configuration';
+import { AciSchema } from './configuration-groups/aci-configuration';
+import {
+ isMonitoringSupported,
+ isListeningSupported,
+} from '/imports/api/supported_environments/supported_environments';
+
+export const Environments = new Mongo.Collection(
+ 'environments_config', { idGeneration: 'MONGO' });
+
+export const requiredConfGroups = [
+ 'mysql',
+ 'OpenStack',
+ 'CLI',
+];
+
+export const optionalConfGroups = [
+ // 'NFV_provider',
+ 'AMQP',
+ 'Monitoring',
+ 'ACI',
+];
+
+let simpleSchema = new SimpleSchema({
+ _id: { type: { _str: { type: String, regEx: SimpleSchema.RegEx.Id } } },
+ auth: {
+ type: Object,
+ blackbox: true,
+ defaultValue: {
+ 'view-env': [
+ ],
+ 'edit-env': [
+ ]
+ }
+ },
+ configuration: {
+ type: [Object],
+ blackbox: true,
+ autoValue: function () {
+ console.log('start - autovalue - environment - configuration');
+ //console.log(this);
+ let that = this;
+
+ if (that.isSet) {
+ let confGroups = that.value;
+
+ let {
+ isMonitoringSupportedRes,
+ isListeningSupportedRes,
+ enable_monitoring,
+ listen
+ } = extractCalcEnvSupportedRelatedValues(that);
+ let dbNode = getDbNode(that);
+ let aci = extractValue('aci', that, dbNode);
+
+ if (enable_monitoring && isMonitoringSupportedRes) {
+ if (! R.find(R.propEq('name', 'Monitoring'), confGroups)) {
+ confGroups = R.append(createNewConfGroup('Monitoring'), confGroups);
+ }
+ } else {
+ console.log('env - configurations - autovalue - monitoring not supported');
+ confGroups = R.reject(R.propEq('name', 'Monitoring'), confGroups);
+ }
+
+ if (listen && isListeningSupportedRes) {
+ if (! R.find(R.propEq('name', 'AMQP'), confGroups)) {
+ confGroups = R.append(createNewConfGroup('AMQP'), confGroups);
+ }
+ } else {
+ console.log('env - configurations - autovalue - listening not supported');
+ confGroups = R.reject(R.propEq('name', 'AMQP'), confGroups);
+ }
+
+ if (aci) {
+ if (! R.find(R.propEq('name', 'ACI'), confGroups)) {
+ confGroups = R.append(createNewConfGroup('ACI'), confGroups);
+ }
+ } else {
+ console.log('env - configurations - autovalue - aci not requested');
+ confGroups = R.reject(R.propEq('name', 'ACI'), confGroups);
+ }
+
+ confGroups = cleanOptionalGroups(confGroups, optionalConfGroups);
+ console.log('env - configurations - autovalue - after clean optional groups');
+
+ let newValue = R.map(function(confGroup) {
+ let schema = getSchemaForGroupName(confGroup.name);
+ return schema.clean(confGroup);
+ }, confGroups);
+
+ console.log('end - autovalue - environment - configurations');
+ console.log(newValue);
+ return newValue;
+
+ } else {
+ console.log('env - configurations - autovalue - is not set');
+ let newValue = R.map((confName) => {
+ let schema = getSchemaForGroupName(confName);
+ return schema.clean({});
+ }, requiredConfGroups);
+ console.log('end - autovalue - environment - configurations');
+ console.log(newValue);
+ return newValue;
+ }
+ },
+ custom: function () {
+ console.log('start - custom - environment - configurations');
+ //console.log(this);
+ let that = this;
+ let configurationGroups = that.value;
+
+ let subErrors = [];
+
+ let {
+ isMonitoringSupportedRes,
+ isListeningSupportedRes,
+ enable_monitoring,
+ listen
+ } = extractCalcEnvSupportedRelatedValues(that);
+
+ let requiredConfGroupsTemp = R.clone(requiredConfGroups);
+ if (enable_monitoring && isMonitoringSupportedRes) {
+ requiredConfGroupsTemp = R.append('Monitoring', requiredConfGroupsTemp);
+ }
+ if (listen && isListeningSupportedRes) {
+ requiredConfGroupsTemp = R.append('AMQP', requiredConfGroupsTemp);
+ }
+
+ console.log('env - configurations - custom - after mon & listen check');
+
+ let invalidResult = R.find(function(groupName) {
+ subErrors = checkGroup(groupName, configurationGroups, true);
+ if (subErrors.length > 0) { return true; }
+ return false;
+ }, requiredConfGroupsTemp);
+
+ console.log(`env - configurations - custom - after require groups check`);
+
+ if (R.isNil(invalidResult)) {
+ invalidResult = R.find(function(groupName) {
+ subErrors = checkGroup(groupName, configurationGroups, false);
+ if (subErrors.length > 0) { return true; }
+ return false;
+ }, optionalConfGroups);
+ }
+
+ console.log(`env - configurations - custom - after optional groups check`);
+
+ if (! R.isNil(invalidResult)) {
+ console.log(`env - configrations - custom - invalid result end: ${R.toString(subErrors)}`);
+ throw {
+ isError: true,
+ type: 'subGroupError',
+ data: subErrors,
+ message: constructSubGroupErrorMessage(subErrors)
+ };
+ }
+ },
+
+ },
+ user: {
+ type: String,
+ },
+ distribution: {
+ type: String,
+ defaultValue: 'Mirantis-8.0',
+ custom: function () {
+ let that = this;
+ let constsDist = Constants.findOne({ name: 'distributions' });
+
+ if (R.isNil(constsDist.data)) { return 'notAllowed'; }
+ let distributions = constsDist.data;
+
+ if (R.isNil(R.find(R.propEq('value', that.value), distributions))) {
+ return 'notAllowed';
+ }
+ },
+ },
+ last_scanned: {
+ type: String, defaultValue: ''
+ },
+ name: {
+ type: String,
+ defaultValue: 'MyEnvironmentName',
+ min: 6,
+ },
+ type_drivers: {
+ type: String,
+ defaultValue: 'gre',
+ custom: function () {
+ let that = this;
+ let TypeDriversRec = Constants.findOne({ name: 'type_drivers' });
+
+ if (R.isNil(TypeDriversRec.data)) { return 'notAllowed'; }
+ let TypeDrivers = TypeDriversRec.data;
+
+ if (R.isNil(R.find(R.propEq('value', that.value), TypeDrivers))) {
+ return 'notAllowed';
+ }
+ },
+ },
+
+ mechanism_drivers: {
+ type: [String],
+ defaultValue: ['ovs'],
+ minCount: 1,
+ custom: function () {
+ let that = this;
+ let consts = Constants.findOne({ name: 'mechanism_drivers' });
+
+ if (R.isNil(consts.data)) { return 'notAllowed'; }
+ let mechanismDrivers = consts.data;
+
+ let result = R.find((driver) => {
+ if (R.find(R.propEq('value', driver), mechanismDrivers)) {
+ return false;
+ }
+ return true;
+ }, that.value);
+
+ if (result) { return 'notAllowed'; }
+
+ },
+ },
+
+ operational: {
+ type: String,
+ allowedValues: ['stopped', 'running', 'error'],
+ defaultValue: 'stopped'
+ },
+
+ scanned: { type: Boolean, defaultValue: false },
+
+ type: {
+ type: String,
+ autoValue: function () {
+ return 'environment';
+ },
+ },
+
+ app_path: {
+ type: String,
+ autoValue: function () {
+ return '/home/scan/calipso_prod/app';
+ }
+ },
+
+ listen: {
+ type: Boolean,
+ autoValue: function () {
+ console.log('env - listen - autoValue - start');
+ let that = this;
+ let newValue = that.value;
+ console.log(`- current value: ${R.toString(newValue)}`);
+
+ let { isListeningSupportedRes } = extractCalcEnvSupportedRelatedValues(that);
+
+ if (!isListeningSupportedRes) {
+ console.log('* listening not supported');
+ console.log(`* ${R.toString(isListeningSupportedRes)}`);
+ newValue = false;
+ }
+
+ return newValue;
+ },
+ },
+
+ enable_monitoring: {
+ type: Boolean,
+ autoValue: function () {
+ console.log('env - enable_monitoring - autoValue - start');
+ let that = this;
+ let newValue = that.value;
+ console.log(`- current value: ${R.toString(newValue)}`);
+
+ let { isMonitoringSupportedRes } = extractCalcEnvSupportedRelatedValues(that);
+
+ if (!isMonitoringSupportedRes) {
+ console.log('* monitoring not supported');
+ console.log(`* ${R.toString(isMonitoringSupportedRes)}`);
+ newValue = false;
+ }
+
+ return newValue;
+ },
+ },
+ aci: {
+ type: Boolean,
+ defaultValue: false,
+ },
+});
+
+/*
+simpleSchema.addValidator(function () {
+ //let that = this;
+});
+*/
+
+// Bug in simple schema. cant add custom message to instance specific
+// schema.
+// https://github.com/aldeed/meteor-simple-schema/issues/559
+// Version 2 fixes it but it is rc.
+//Environments.schema.messages({
+SimpleSchema.messages({
+ confGroupInvalid: 'Configuration group is invalid.'
+});
+
+Environments.schema = simpleSchema;
+Environments.attachSchema(Environments.schema);
+
+function getSchemaForGroupName(groupName) {
+ switch (groupName) {
+ case 'mysql':
+ return MysqlSchema;
+ case 'OpenStack':
+ return OpenStackSchema;
+ case 'CLI':
+ return CLISchema;
+ case 'AMQP':
+ return AMQPSchema;
+// case 'NFV_provider':
+// return NfvProviderSchema;
+ case 'ACI':
+ return AciSchema;
+ case 'Monitoring':
+ return MonitoringSchema;
+ default:
+ throw 'group name is not recognized. group: ' + groupName;
+ }
+}
+
+function constructSubGroupErrorMessage(errors) {
+ let message = 'Validation errors on sub groups:';
+ message = message + R.reduce((acc, item) => {
+ return acc + '\n- ' + item.group + ': ' + item.message;
+ }, '', errors);
+
+ return message;
+}
+
+function checkGroup(groupName, configurationGroups, groupRequired) {
+ let subErrors = [];
+ let confGroup = R.find(R.propEq('name', groupName), configurationGroups);
+
+ if (R.isNil(confGroup)) {
+ if (groupRequired) {
+ subErrors = R.append({
+ field: 'configuration',
+ group: groupName,
+ message: 'group ' + groupName + ' is required'
+ }, subErrors);
+ }
+ return subErrors;
+ }
+
+ let validationContext = getSchemaForGroupName(groupName).newContext();
+
+ if (! validationContext.validate(confGroup)) {
+ subErrors = R.reduce(function (acc, invalidField) {
+ return R.append({
+ field: invalidField,
+ group: groupName,
+ message: validationContext.keyErrorMessage(invalidField.name),
+ }, acc);
+ }, [], validationContext.invalidKeys());
+
+ return subErrors;
+ }
+
+ return subErrors;
+}
+
+export function createNewConfGroup(groupName) {
+ let schema = getSchemaForGroupName(groupName);
+ return schema.clean({});
+}
+
+function cleanOptionalGroups(confGroups, optionalConfGroups) {
+ return R.filter((conf) => {
+ if (R.contains(conf.name, optionalConfGroups)) {
+ return !isConfEmpty(conf);
+ }
+
+ return true;
+ }, confGroups);
+}
+
+function isConfEmpty(conf) {
+ return ! R.any((key) => {
+ if (key === 'name') { return false; } // We ignore the key 'name'. It is a 'type' key.
+ let val = conf[key];
+ return ! ( R.isNil(val) || R.isEmpty(val));
+ })(R.keys(conf));
+}
+
+function extractValue(name, schemaValidator, dbNode) {
+ console.log('env - extract value');
+ console.log(`-name: ${R.toString(name)}`);
+ //console.log(`-schemaValidator: ${R.toString(schemaValidator)}`);
+ console.log(`-dbNode: ${R.toString(dbNode)}`);
+
+ let field = schemaValidator.field(name);
+ let value = field.value;
+
+ console.log(`extract value - schema value: ${R.toString(value)}`);
+
+ if (R.isNil(field.value) && !field.isSet && dbNode) {
+ console.log(`extract value - db value: ${R.toString(dbNode[name])}`);
+ value = dbNode[name];
+ }
+
+ console.log(`extract value - result: ${R.toString(value)}`);
+ return value;
+}
+
+function getDbNode(schemaHelper) {
+ let _id = R.defaultTo(schemaHelper.docId, R.path(['value'], schemaHelper.field('_id')));
+ let dbNode = R.defaultTo(null, Environments.findOne({ _id: _id }));
+ return dbNode;
+}
+
+function extractCalcEnvSupportedRelatedValues(schemaHelper) {
+ let dbNode = getDbNode(schemaHelper);
+
+ let dist = extractValue('distribution', schemaHelper, dbNode);
+ let typeDrivers = extractValue('type_drivers', schemaHelper, dbNode);
+ let mechDrivers = extractValue('mechanism_drivers', schemaHelper, dbNode);
+ let enable_monitoring = extractValue('enable_monitoring', schemaHelper, dbNode);
+ let listen = extractValue('listen', schemaHelper, dbNode);
+
+ let isMonitoringSupportedRes = isMonitoringSupported(dist, typeDrivers, mechDrivers);
+ let isListeningSupportedRes = isListeningSupported(dist, typeDrivers, mechDrivers);
+
+ return {
+ enable_monitoring,
+ listen,
+ isMonitoringSupportedRes,
+ isListeningSupportedRes,
+ };
+}
diff --git a/ui/imports/api/environments/methods.js b/ui/imports/api/environments/methods.js
new file mode 100644
index 0000000..22a1e8b
--- /dev/null
+++ b/ui/imports/api/environments/methods.js
@@ -0,0 +1,154 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+//import { Meteor } from 'meteor/meteor';
+import * as R from 'ramda';
+import { ValidatedMethod } from 'meteor/mdg:validated-method';
+
+//import { SimpleSchema } from 'meteor/aldeed:simple-schema';
+
+import { Environments } from './environments';
+import { Inventory } from '/imports/api/inventories/inventories';
+import { Links } from '/imports/api/links/links';
+import { Cliques } from '/imports/api/cliques/cliques';
+import { CliqueTypes } from '/imports/api/clique-types/clique-types';
+import { Messages } from '/imports/api/messages/messages';
+import { Scans } from '/imports/api/scans/scans';
+import { Roles } from 'meteor/alanning:roles';
+
+export const insert = new ValidatedMethod({
+ name: 'environments.insert',
+ validate: Environments.simpleSchema()
+ .pick([
+ 'configuration',
+ 'configuration.$',
+ 'distribution',
+ 'name',
+ 'type_drivers',
+ 'mechanism_drivers',
+ 'mechanism_drivers.$',
+ 'listen',
+ 'enable_monitoring',
+ 'aci',
+ ]).validator({ clean: true, filter: false }),
+ //validate: null,
+ run({
+ configuration,
+ distribution,
+ name,
+ type_drivers,
+ mechanism_drivers,
+ listen,
+ enable_monitoring,
+ aci,
+ }) {
+ // todo: create clean object instance.
+ let environment = Environments.schema.clean({
+ user: Meteor.userId()
+ });
+
+ let auth = {
+ 'view-env': [
+ Meteor.userId()
+ ],
+ 'edit-env': [
+ Meteor.userId()
+ ]
+ };
+
+ environment = R.merge(environment, {
+ configuration,
+ distribution,
+ name,
+ type_drivers,
+ mechanism_drivers,
+ listen,
+ enable_monitoring,
+ auth,
+ aci,
+ });
+
+ Environments.insert(environment);
+ },
+});
+
+export const update = new ValidatedMethod({
+ name: 'environments.update',
+ validate: Environments.simpleSchema().pick([
+ '_id',
+ 'configuration',
+ 'configuration.$',
+ //'distribution',
+ //'name',
+ 'type_drivers',
+ 'mechanism_drivers',
+ 'mechanism_drivers.$',
+ 'listen',
+ 'enable_monitoring',
+ 'aci',
+ ]).validator({ clean: true, filter: false }),
+ run({
+ _id,
+ configuration,
+ //distribution,
+ //name,
+ type_drivers,
+ mechanism_drivers,
+ listen,
+ enable_monitoring,
+ aci,
+ }) {
+ let env = Environments.findOne({ _id: _id });
+
+ if (! Roles.userIsInRole(Meteor.userId(), 'edit-env', 'default-group')) {
+ if (! R.contains(Meteor.userId(), R.path(['auth', 'edit-env'], env) )) {
+ throw new Meteor.Error('not-auth', 'unauthorized for updating env');
+ }
+ }
+
+ Environments.update(_id, {
+ $set: {
+ configuration: configuration,
+ //distribution: distribution,
+ //name: name,
+ type_drivers,
+ mechanism_drivers,
+ listen,
+ enable_monitoring,
+ aci,
+ },
+ });
+ }
+});
+
+export const remove = new ValidatedMethod({
+ name: 'environments.remove',
+ validate: Environments.simpleSchema().pick([
+ '_id',
+ ]).validator({ clean: true, filter: false }),
+ run({
+ _id,
+ }) {
+ const env = Environments.findOne({ _id: _id });
+ console.log('environment for remove: ', env);
+
+ if (! Roles.userIsInRole(Meteor.userId(), 'edit-env', 'default-group')) {
+ if (! R.contains(Meteor.userId(), R.path(['auth', 'edit-env'], env) )) {
+ throw new Meteor.Error('not-auth', 'unauthorized for updating env');
+ }
+ }
+
+ Inventory.remove({ environment: env.name });
+ Links.remove({ environment: env.name });
+ Cliques.remove({ environment: env.name });
+ CliqueTypes.remove({ environment: env.name });
+ Messages.remove({ environment: env.name });
+ Scans.remove({ environment: env.name });
+ Environments.remove({ _id: _id });
+ }
+});
diff --git a/ui/imports/api/environments/server/publications.js b/ui/imports/api/environments/server/publications.js
new file mode 100644
index 0000000..667ee8e
--- /dev/null
+++ b/ui/imports/api/environments/server/publications.js
@@ -0,0 +1,102 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { Meteor } from 'meteor/meteor';
+import * as R from 'ramda';
+import { Roles } from 'meteor/alanning:roles';
+
+import { Environments } from '../environments.js';
+
+Meteor.publish('environments_config', function () {
+ console.log('server subscribtion to: environments_config');
+ let userId = this.userId;
+
+ let query = {
+ type: 'environment',
+ };
+
+ if (! Roles.userIsInRole(userId, 'view-env', null)) {
+ query = R.merge(query, {
+ 'auth.view-env': {
+ $in: [ userId ]
+ }
+ });
+ }
+
+ console.log('-query: ', R.toString(query));
+ return Environments.find(query);
+});
+
+const subsEnvViewEnvUserId = 'environments.view-env&userId';
+Meteor.publish(subsEnvViewEnvUserId, function (userId) {
+ console.log(`subscription - ${subsEnvViewEnvUserId} `);
+ console.log(`-userId: ${R.toString(userId)}`);
+
+ let query = {};
+
+ let currentUser = this.userId;
+ if (! Roles.userIsInRole(currentUser, 'manage-users', Roles.GLOBAL_GROUP)) {
+ console.log(`* error: unauth`);
+ console.log(`- currentUser: ${R.toString(currentUser)}`);
+ this.error('unauthorized for this subscription');
+ return;
+ }
+
+ query = R.merge(query, {
+ 'auth.view-env': {
+ $in: [ userId ]
+ }
+ });
+
+ console.log(`* query: ${R.toString(query)}`);
+ return Environments.find(query);
+});
+
+const subsEnvEditEnvUserId = 'environments.edit-env&userId';
+Meteor.publish(subsEnvEditEnvUserId, function (userId) {
+ console.log(`subscription - ${subsEnvEditEnvUserId} `);
+ console.log(`-userId: ${R.toString(userId)}`);
+ let query = {};
+
+ let currentUser = this.userId;
+ if (! Roles.userIsInRole(currentUser, 'manage-users', Roles.GLOBAL_GROUP)) {
+ console.log(`* error: unauth`);
+ console.log(`- currentUser: ${R.toString(currentUser)}`);
+ this.error('unauthorized for this subscription');
+ return;
+ }
+
+ query = R.merge(query, {
+ 'auth.edit-env': {
+ $in: [ userId ]
+ }
+ });
+
+ console.log(`* query: ${R.toString(query)}`);
+ return Environments.find(query);
+});
+
+Meteor.publish('environments?name', function (name) {
+ console.log('server subscribtion to: environments?name=' + name.toString());
+ let query = {
+ name: name,
+ user: this.userId
+ };
+ return Environments.find(query);
+});
+
+Meteor.publish('environments?_id', function (_id) {
+ console.log('server subscribtion to: environments?_id');
+ console.log('-_id: ', R.toString(_id));
+
+ let query = {
+ _id: _id,
+ user: this.userId
+ };
+ return Environments.find(query);
+});
diff --git a/ui/imports/api/inventories/inventories.js b/ui/imports/api/inventories/inventories.js
new file mode 100644
index 0000000..114f5ef
--- /dev/null
+++ b/ui/imports/api/inventories/inventories.js
@@ -0,0 +1,11 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { Mongo } from 'meteor/mongo';
+
+export const Inventory = new Mongo.Collection('inventory', { idGeneration: 'MONGO' });
diff --git a/ui/imports/api/inventories/server/methods.js b/ui/imports/api/inventories/server/methods.js
new file mode 100644
index 0000000..ec2f27d
--- /dev/null
+++ b/ui/imports/api/inventories/server/methods.js
@@ -0,0 +1,151 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { check } from 'meteor/check';
+import * as R from 'ramda';
+import { Inventory } from '../inventories';
+import { Environments } from '/imports/api/environments/environments';
+import { regexEscape } from '/imports/lib/regex-utils';
+import { NodeHoverAttr } from '/imports/api/attributes_for_hover_on_data/attributes_for_hover_on_data';
+const AUTO_COMPLETE_RESULTS_LIMIT = 15;
+
+Meteor.methods({
+ 'inventorySearch': function(searchTerm, envId, opCounter) {
+ console.log('inventorySearch');
+ console.log('searchTerm', R.toString(searchTerm));
+ console.log('envId', R.toString(envId));
+ console.log('opCounter', R.toString(opCounter));
+
+ this.unblock();
+
+ if (R.anyPass([R.isNil, R.isEmpty])(searchTerm)) {
+ return {
+ searchResults: [],
+ opCounter: opCounter
+ };
+ }
+
+ let searchExp = new RegExp(regexEscape(searchTerm), 'i');
+
+ let query = {
+ name: searchExp
+ };
+
+ if (! R.isNil(envId)) {
+ let env = Environments.findOne({ _id: envId });
+ query = R.merge(query, {
+ environment: env.name
+ });
+ }
+
+ let searchResults = Inventory.find(query, {
+ limit: AUTO_COMPLETE_RESULTS_LIMIT
+ }).fetch();
+
+ searchResults = R.map((inventory) => {
+ console.log('search result');
+ console.log(R.toString(inventory));
+
+ let itemEnv = Environments.findOne({ name: inventory.environment });
+
+ return R.merge(inventory, {
+ _envId: itemEnv._id
+ });
+ }, searchResults);
+
+ return {
+ opCounter: opCounter,
+ searchResults: searchResults,
+ };
+ },
+
+ 'expandNodePath': function(nodeId) {
+ console.log('method server: expandNodePath', R.toString(nodeId));
+
+ //check(nodeId, MongoI);
+ this.unblock();
+
+ let node = Inventory.findOne({ _id: nodeId });
+ if (R.isNil(node)) {
+ console.log('method server: expandNodePath - no node');
+ return null;
+ }
+
+ let idList = R.pipe(R.split('/'), R.drop(2))(node.id_path);
+ let result = R.map((partId) => {
+ return Inventory.findOne({ environment: node.environment, id: partId });
+ }, idList);
+
+ console.log('method server: expandNodePath - results', result);
+ return result;
+ },
+
+ 'inventoryFindNode?type&env&name': function(type, envName, nodeName) {
+ console.log('method server: inventoryFindNode',
+ R.toString(type), R.toString(envName), R.toString(nodeName));
+
+ check(envName, String);
+ check(nodeName, String);
+ this.unblock();
+
+ let query = { type: type, environment: envName, name: nodeName };
+ let node = Inventory.findOne(query);
+
+ return {
+ node: node
+ };
+ },
+
+ 'inventoryFindNode?env&id': function (envName, nodeId) {
+ console.log('method server: inventoryFindNode?env&id',
+ R.toString(envName), R.toString(nodeId));
+
+ check(envName, String);
+ check(nodeId, String);
+ this.unblock();
+
+ let query = { environment: envName, id: nodeId };
+ let node = Inventory.findOne(query);
+
+ return {
+ node: node
+ };
+ },
+
+ 'inventoryFindNode?DataAndAttrs': function (nodeId) {
+ console.log(`method server: inventoryFindNode?DataAndAttrs. ${R.toString(nodeId)}`);
+ //check(nodeId, ObjectId);
+ this.unblock();
+
+ let query = { _id: nodeId };
+ let node = Inventory.findOne(query);
+ let attrsDefs = NodeHoverAttr.findOne({ 'type': node.type });
+ let attributes = calcAttrsForNode(node, attrsDefs);
+
+ return {
+ node: node,
+ nodeName: node.name,
+ attributes: attributes
+ };
+ },
+});
+
+function calcAttrsForNode(node, attrsDefsRec) {
+ if (R.isNil(attrsDefsRec)) {
+ return [];
+ }
+
+ let attrsDefs = attrsDefsRec.attributes;
+
+ return R.reduce((acc, attrDef) => {
+ return R.ifElse(R.isNil,
+ R.always(acc),
+ (attrVal) => R.append(R.assoc(attrDef, attrVal, {}), acc)
+ )(R.prop(attrDef, node));
+ }, [], attrsDefs);
+}
diff --git a/ui/imports/api/inventories/server/publications.js b/ui/imports/api/inventories/server/publications.js
new file mode 100644
index 0000000..f35ff30
--- /dev/null
+++ b/ui/imports/api/inventories/server/publications.js
@@ -0,0 +1,250 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { Meteor } from 'meteor/meteor';
+import { Counts } from 'meteor/tmeasday:publish-counts';
+import { check } from 'meteor/check';
+import * as R from 'ramda';
+
+import { Inventory } from '../inventories.js';
+import { regexEscape } from '/imports/lib/regex-utils';
+
+Meteor.publish('inventory', function () {
+ console.log('server subscribtion to: inventory');
+ //return Inventory.find({$where: 'this.id_path.match('^/WebEX-Mirantis@Cisco/')'});
+ //return Inventory.find({ 'show_in_tree': true });
+ return Inventory.find({});
+});
+
+Meteor.publish('inventory?_id', function (_id) {
+ console.log('server subscribtion to: inventory?_id');
+ console.log('_id:', R.toString(_id));
+
+ return Inventory.find({ _id: _id });
+});
+
+Meteor.publish('inventory?id', function (id) {
+ console.log('server subscribtion to: inventory?id');
+ return Inventory.find({id: id});
+});
+
+Meteor.publish('inventory?env&id', function (env, id) {
+ console.log('server subscribtion to: inventory?env&id');
+ console.log(`-env: ${R.toString(env)}`);
+ console.log(`-id: ${R.toString(id)}`);
+
+ return Inventory.find({environment: env, id: id});
+});
+
+Meteor.publish('inventory?id_path', function (id_path) {
+ console.log('server subscribtion to: inventory?id_path');
+ return Inventory.find({id_path: id_path});
+});
+
+Meteor.publish('inventory?name&env&type', function (name, env, type) {
+ console.log('server subscribtion to: inventory?name&env&type');
+ console.log('-name:', R.toString(name));
+ console.log('-env:', R.toString(env));
+ console.log('-type:', R.toString(type));
+
+ let query = {
+ name: name,
+ environment: env,
+ type: type
+ };
+
+ console.log('query', R.toString(query));
+ return Inventory.find(query);
+});
+
+Meteor.publish('inventory?_id-in', function (idsList) {
+ var query = {
+ _id: { $in: idsList }
+ };
+ /*
+ var counterName = 'inventory?env+type!counter?env=' + env + '&type=' + type;
+
+ console.log('server subscribing to counter: ' + counterName);
+ Counts.publish(this, counterName, Inventory.find(query));
+ */
+ console.log('server subscribtion to: inventory?_id-in');
+ console.log('- id-in: ' + idsList);
+
+ return Inventory.find(query);
+});
+
+Meteor.publish('inventory?env+type', function (env, type) {
+ var query = {
+ environment: env,
+ type: type
+ };
+ var counterName = 'inventory?env+type!counter?env=' + env + '&type=' + type;
+
+ console.log('server subscribing to counter: ' + counterName);
+ Counts.publish(this, counterName, Inventory.find(query));
+
+ console.log('server subscribtion to: inventory-by-env-and-type');
+ console.log('-env: ' + env);
+ console.log('-type: ' + type);
+
+ return Inventory.find(query);
+});
+
+Meteor.publish('inventory?env&binding:host_id&type', function (env, host_id, type) {
+ var query = {
+ environment: env,
+ 'binding:host_id': host_id,
+ type: type
+ };
+ console.log('server subscribtion to: inventory?env&binding:host_id&type');
+ console.log('-env: ' + env);
+ console.log('-binding:host_id: ' + host_id);
+ console.log('-type: ' + type);
+
+ return Inventory.find(query);
+});
+
+Meteor.publish('inventory?env+name', function (env, name) {
+ var query = {
+ name: name,
+ environment: env
+ };
+
+ console.log('server subscribtion to: inventory?env+name');
+ console.log('- name: ' + name);
+ console.log('- env: ' + env);
+
+ return Inventory.find(query);
+});
+
+Meteor.publish('inventory?type+host', function (type, host) {
+ var query = {
+ type: type,
+ host: host
+ };
+/*
+ var counterName = 'inventory?env+type!counter?env=' + env + '&type=' + type;
+
+ console.log('server subscribing to counter: ' + counterName);
+ Counts.publish(this, counterName, Inventory.find(query));
+*/
+
+ console.log('server subscribtion to: inventory?type+host');
+ console.log('- type: ' + type);
+ console.log('- host: ' + host);
+ return Inventory.find(query);
+});
+
+Meteor.publish('inventory?id_path_start&type', function (id_path, type) {
+ check(id_path, String);
+ check(type, String);
+
+ let idPathExp = new RegExp(`^${regexEscape(id_path)}`);
+
+ let query = {
+ id_path: idPathExp,
+ type: type
+ };
+
+ var counterName = 'inventory?id_path_start&type!counter?id_path_start=' +
+ id_path + '&type=' + type;
+
+ console.log('server subscribing to counter: ' + counterName);
+ Counts.publish(this, counterName, Inventory.find(query));
+
+ console.log('server subscribtion to: inventory?id_path_start&type');
+ console.log('-id_path_start: ' + id_path);
+ console.log('-type: ' + type);
+ return Inventory.find(query);
+});
+
+
+Meteor.publish('inventory.children', function (id, type, name, env) {
+ console.log('server subscribtion to: inventory.children');
+ console.log('node id: ' + R.toString(id));
+ console.log('node type: ' + R.toString(type));
+ console.log('node name: ' + R.toString(name));
+ console.log('node env: ' + R.toString(env));
+
+ let query = {
+ $or:
+ [
+ {
+ environment: env,
+ parent_id: id
+ },
+ ]
+ };
+
+ if (R.equals('host_ref', type)) {
+ let realParent = Inventory.findOne({
+ name: name,
+ environment: env,
+ type: 'host'
+ });
+
+ query = R.merge(query, {
+ $or: R.append({
+ environment: env,
+ parent_id: realParent.id
+ }, query.$or)
+ });
+ }
+
+ console.log('query: ', R.toString(query));
+
+ return Inventory.find(query);
+});
+
+Meteor.publish('inventory.first-child', function (id, type, name, env) {
+ console.log('server subscribing to: inventory.first-child');
+ console.log('node id: ' + R.toString(id));
+ console.log('node type: ' + R.toString(type));
+ console.log('node name: ' + R.toString(name));
+ console.log('node env: ' + R.toString(env));
+
+ var counterName = 'inventory.first-child!counter!id=' + id;
+ var query = {
+ $or: [
+ {
+ environment: env,
+ parent_id: id
+ }
+ ]
+ };
+
+ if (R.equals('host_ref', type)) {
+ let realParent = Inventory.findOne({
+ name: name,
+ environment: env,
+ type: 'host'
+ });
+
+ query = R.merge(query, {
+ $or: R.append({
+ environment: env,
+ parent_id: realParent.id
+ }, query.$or)
+ });
+ }
+
+ Counts.publish(this, counterName, Inventory.find(query, { limit: 1 }));
+ console.log('server subscribing to counter: ' + counterName);
+
+// todo: eyaltask: all criteria
+ console.log('query: ', R.toString(query));
+ return Inventory.find(query, { limit: 1 });
+});
+
+Meteor.publish('inventoryByEnv', function (env) {
+ console.log('server subscribtion to: inventoryByEnv');
+ //return Inventory.find({$where: 'this.id_path.match('^/WebEX-Mirantis@Cisco/')'});
+ //return Inventory.find({ 'show_in_tree': true });
+ return Inventory.find({'environment':env});
+});
+
diff --git a/ui/imports/api/link-types/link-types.js b/ui/imports/api/link-types/link-types.js
new file mode 100644
index 0000000..94d6ddd
--- /dev/null
+++ b/ui/imports/api/link-types/link-types.js
@@ -0,0 +1,86 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { Mongo } from 'meteor/mongo';
+import { SimpleSchema } from 'meteor/aldeed:simple-schema';
+import * as R from 'ramda';
+import { Constants } from '/imports/api/constants/constants';
+//import { Environments } from '/imports/api/environments/environments';
+
+export const LinkTypes = new Mongo.Collection(
+ 'link_types', { idGeneration: 'MONGO' });
+
+let schema = {
+ _id: { type: { _str: { type: String, regEx: SimpleSchema.RegEx.Id } } },
+ description: {
+ type: String
+ },
+ type: {
+ type: String
+ },
+ endPointA: {
+ type: String,
+ custom: function () {
+ let that = this;
+ let values = Constants.findOne({ name: 'object_types_for_links' }).data;
+
+ if (R.isNil(R.find(R.propEq('value', that.value), values))) {
+ return 'notAllowed';
+ }
+ }
+ },
+ endPointB: {
+ type: String,
+ custom: function () {
+ let that = this;
+ let values = Constants.findOne({ name: 'object_types_for_links' }).data;
+
+ if (R.isNil(R.find(R.propEq('value', that.value), values))) {
+ return 'notAllowed';
+ }
+ }
+ }
+};
+
+let simpleSchema = new SimpleSchema(schema);
+
+simpleSchema.addValidator(function () {
+ let that = this;
+
+ let existing = LinkTypes.findOne({
+ _id: { $ne: that.docId },
+ endPointA: that.field('endPointA').value,
+ endPointB: that.field('endPointB').value
+ });
+
+ if (R.allPass([
+ R.pipe(R.isNil, R.not),
+ R.pipe(R.propEq('_id', that.docId), R.not)
+ ])(existing)) {
+
+ return 'alreadyExists';
+ }
+
+ existing = LinkTypes.findOne({
+ _id: { $ne: that.docId },
+ endPointA: that.field('endPointB').value,
+ endPointB: that.field('endPointA').value
+ });
+
+ if (R.allPass([
+ R.pipe(R.isNil, R.not),
+ R.pipe(R.propEq('_id', that.docId), R.not)
+ ])(existing)) {
+
+ return 'alreadyExists';
+ }
+});
+
+LinkTypes.schema = simpleSchema;
+
+LinkTypes.attachSchema(LinkTypes.schema);
diff --git a/ui/imports/api/link-types/methods.js b/ui/imports/api/link-types/methods.js
new file mode 100644
index 0000000..846c28b
--- /dev/null
+++ b/ui/imports/api/link-types/methods.js
@@ -0,0 +1,114 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { ValidatedMethod } from 'meteor/mdg:validated-method';
+import * as R from 'ramda';
+import { Roles } from 'meteor/alanning:roles';
+
+import { LinkTypes } from './link-types';
+
+export const insert = new ValidatedMethod({
+ name: 'links_types.insert',
+ validate: LinkTypes.simpleSchema()
+ .pick([
+ //'environment',
+ 'description',
+ 'endPointA',
+ 'endPointB',
+ ]).validator({ clean: true, filter: false }),
+ run({
+ //environment,
+ description,
+ endPointA,
+ endPointB
+ }) {
+ if (! Roles.userIsInRole(Meteor.userId(), 'manage-link-types', Roles.GLOBAL_GROUP)) {
+ throw new Meteor.Error('unauthorized for inserting link type');
+ }
+
+ let linkType = LinkTypes.schema.clean({
+ });
+
+ let type = calcTypeFromEndPoints(endPointA, endPointB);
+
+ linkType = R.merge(linkType, {
+ description,
+ endPointA,
+ endPointB,
+ type
+ });
+
+ LinkTypes.insert(linkType);
+ }
+});
+
+export const remove = new ValidatedMethod({
+ name: 'links_types.remove',
+ validate: LinkTypes.simpleSchema()
+ .pick([
+ '_id',
+ ]).validator({ clean: true, filter: false }),
+ run({
+ _id
+ }) {
+ if (! Roles.userIsInRole(Meteor.userId(), 'manage-link-types', Roles.DEFAULT_GROUP)) {
+ throw new Meteor.Error('unauthorized for removing link type');
+ }
+
+ let linkType = LinkTypes.findOne({ _id: _id });
+ console.log('link type for remove: ', linkType);
+ console.log('current user', Meteor.userId());
+
+ LinkTypes.remove({ _id: _id });
+ }
+});
+
+export const update = new ValidatedMethod({
+ name: 'links_types.update',
+ validate: LinkTypes.simpleSchema()
+ .pick([
+ '_id',
+ 'description',
+ 'endPointA',
+ 'endPointB',
+ ]).validator({ clean: true, filter: false }),
+ run({
+ _id,
+ description,
+ endPointA,
+ endPointB
+ }) {
+ if (! Roles.userIsInRole(Meteor.userId(), 'manage-link-types', Roles.DEFAULT_GROUP)) {
+ throw new Meteor.Error('unauthorized for updating link type');
+ }
+
+ let linkType = LinkTypes.findOne({ _id: _id });
+ console.log('link type for update: ', linkType);
+ console.log('current user', Meteor.userId());
+
+ let type = calcTypeFromEndPoints(endPointA, endPointB);
+
+ linkType = R.merge(R.pick([
+ 'description',
+ 'endPointA',
+ 'endPointB',
+ 'type'
+ ], linkType), {
+ description,
+ endPointA,
+ endPointB,
+ type
+ });
+
+ LinkTypes.update({ _id: _id }, { $set: linkType });
+ }
+});
+
+function calcTypeFromEndPoints(endPointA, endPointB) {
+ return `${endPointA}-${endPointB}`;
+}
diff --git a/ui/imports/api/link-types/server/publications.js b/ui/imports/api/link-types/server/publications.js
new file mode 100644
index 0000000..6c6278f
--- /dev/null
+++ b/ui/imports/api/link-types/server/publications.js
@@ -0,0 +1,46 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { Meteor } from 'meteor/meteor';
+import * as R from 'ramda';
+
+import { LinkTypes } from '../link-types.js';
+
+Meteor.publish('link_types', function () {
+ console.log('server subscribtion: link_types');
+
+ //let that = this;
+
+ let query = {};
+ return LinkTypes.find(query);
+});
+
+Meteor.publish('link_types?env*', function (env) {
+ console.log('server subscribtion: link_types?env*');
+ console.log(env);
+
+ //let that = this;
+
+ let query = {};
+ if (! R.isNil(env)) { query = R.assoc('environment', env, query); }
+ console.log('-query: ', query);
+ return LinkTypes.find(query);
+});
+
+Meteor.publish('link_types?_id', function (_id) {
+ console.log('server subscribtion: link_types?_id');
+ console.log(_id);
+
+ //let that = this;
+
+ let query = {
+ _id: _id,
+ };
+ console.log('-query: ', query);
+ return LinkTypes.find(query);
+});
diff --git a/ui/imports/api/links/links.js b/ui/imports/api/links/links.js
new file mode 100644
index 0000000..2baf58c
--- /dev/null
+++ b/ui/imports/api/links/links.js
@@ -0,0 +1,11 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { Mongo } from 'meteor/mongo';
+
+export const Links = new Mongo.Collection('links', { idGeneration: 'MONGO' });
diff --git a/ui/imports/api/links/methods.js b/ui/imports/api/links/methods.js
new file mode 100644
index 0000000..1eda375
--- /dev/null
+++ b/ui/imports/api/links/methods.js
@@ -0,0 +1,8 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
diff --git a/ui/imports/api/links/server/publications.js b/ui/imports/api/links/server/publications.js
new file mode 100644
index 0000000..78d0c26
--- /dev/null
+++ b/ui/imports/api/links/server/publications.js
@@ -0,0 +1,32 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { Meteor } from 'meteor/meteor';
+import { Links } from '../links.js';
+
+Meteor.publish('links', function () {
+ console.log('server subscribtion to: links');
+ //return Inventory.find({$where: 'this.id_path.match('^/WebEX-Mirantis@Cisco/')'});
+ return Links.find({});
+});
+
+Meteor.publish('links?_id-in', function (idsList) {
+ var query = {
+ _id: { $in: idsList}
+ };
+/*
+ var counterName = 'inventory?env+type!counter?env=' + env + '&type=' + type;
+
+ console.log('server subscribing to counter: ' + counterName);
+ Counts.publish(this, counterName, Inventory.find(query));
+*/
+
+ console.log('server subscribtion to: links?_id-in');
+ console.log('- _id-in: ' + idsList);
+ return Links.find(query);
+});
diff --git a/ui/imports/api/messages/messages.js b/ui/imports/api/messages/messages.js
new file mode 100644
index 0000000..5a028b0
--- /dev/null
+++ b/ui/imports/api/messages/messages.js
@@ -0,0 +1,125 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { Mongo } from 'meteor/mongo';
+import { SimpleSchema } from 'meteor/aldeed:simple-schema';
+import * as R from 'ramda';
+import { Environments } from '/imports/api/environments/environments';
+import { Constants } from '/imports/api/constants/constants';
+
+export const Messages = new Mongo.Collection('messages', { idGeneration: 'MONGO' });
+
+let schema = {
+ _id: { type: { _str: { type: String, regEx: SimpleSchema.RegEx.Id } } },
+
+ environment: {
+ type: String,
+ custom: function () {
+ let that = this;
+ let env = Environments.findOne({ name: that.value });
+
+ if (R.isNil(env)) {
+ return 'notAllowed';
+ }
+ }
+ },
+
+ id: {
+ type: String
+ },
+
+ viewed: {
+ type: Boolean,
+ defaultValue: false
+ },
+
+ display_context: {
+ type: String
+ },
+
+ message: {
+ type: Object,
+ blackbox: true
+ },
+
+ source_system: {
+ type: String,
+ custom: function () {
+ let that = this;
+ let values = Constants.findOne({ name: 'message_source_systems' }).data;
+
+ if (R.isNil(R.find(R.propEq('value', that.value), values))) {
+ return 'notAllowed';
+ }
+ }
+ },
+
+ level: {
+ type: String
+ },
+
+ timestamp: {
+ type: Date
+ },
+
+ related_object_type: {
+ type: String
+ },
+
+ related_object: {
+ type: String
+ },
+
+ scan_id: {
+ type: Date
+ }
+};
+
+let simpleSchema = new SimpleSchema(schema);
+
+Messages.schema = simpleSchema;
+Messages.attachSchema(Messages.schema);
+
+export function calcIconForMessageLevel(level) {
+ switch (level) {
+ case 'info':
+ return 'notifications';
+ case 'warning':
+ return 'warning';
+ case 'error':
+ return 'error';
+ default:
+ return 'notifications';
+ }
+}
+
+export function lastMessageTimestamp (level, envName) {
+ let query = { level: level };
+ query = R.ifElse(R.isNil, R.always(query), R.assoc('environment', R.__, query))(envName);
+
+ let message = Messages.findOne(query, {
+ sort: { timestamp: -1 }
+ });
+
+ let res = R.path(['timestamp'], message);
+ if (R.isNil(res)) { return null; }
+ return (res instanceof String) ? res : res.toString();
+}
+
+export function calcColorClassForMessagesInfoBox(level) {
+ switch (level) {
+ case 'info':
+ return 'green-text';
+ case 'warning':
+ return 'orange-text';
+ case 'error':
+ return 'red-text';
+ default:
+ return 'green-text';
+ }
+}
diff --git a/ui/imports/api/messages/methods.js b/ui/imports/api/messages/methods.js
new file mode 100644
index 0000000..1eda375
--- /dev/null
+++ b/ui/imports/api/messages/methods.js
@@ -0,0 +1,8 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
diff --git a/ui/imports/api/messages/server/methods.js b/ui/imports/api/messages/server/methods.js
new file mode 100644
index 0000000..119e6b0
--- /dev/null
+++ b/ui/imports/api/messages/server/methods.js
@@ -0,0 +1,49 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import * as R from 'ramda';
+import { Messages } from '/imports/api/messages/messages';
+
+Meteor.methods({
+ 'messages/get?level&env&page&amountPerPage&sortField&sortDirection': function (
+ level, env, page, amountPerPage, sortField, sortDirection) {
+
+ logMethodCall('messages/get?level&env&page&amountPerPage&sortField&sortDirection',
+ {level, env, page, amountPerPage});
+
+ this.unblock();
+
+ let skip = (page - 1) * amountPerPage;
+
+ let query = {};
+ let sortParams = {};
+
+ query = R.ifElse(R.isNil, R.always(query),R.assoc('environment', R.__, query))(env);
+ query = R.ifElse(R.isNil, R.always(query),R.assoc('level', R.__, query))(level);
+
+ sortParams = R.ifElse(R.isNil, R.always(sortParams),
+ R.assoc(R.__, sortDirection, sortParams))(sortField);
+
+ console.log('sort params:', sortParams);
+
+ let qParams = {
+ limit: amountPerPage,
+ skip: skip,
+ sort: sortParams,
+ };
+
+ return Messages.find(query, qParams).fetch();
+ }
+});
+
+function logMethodCall(name, args) {
+ console.log(`method call: ${name}`);
+ R.forEachObjIndexed((value, key) => {
+ console.log(`${key}: ${R.toString(value)}`);
+ }, args);
+}
diff --git a/ui/imports/api/messages/server/publications.js b/ui/imports/api/messages/server/publications.js
new file mode 100644
index 0000000..13c7c50
--- /dev/null
+++ b/ui/imports/api/messages/server/publications.js
@@ -0,0 +1,98 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { Meteor } from 'meteor/meteor';
+//import { Counts } from 'meteor/tmeasday:publish-counts';
+import { Counter } from 'meteor/natestrauser:publish-performant-counts';
+import { Messages } from '../messages.js';
+import * as R from 'ramda';
+
+Meteor.publish('messages', function () {
+ console.log('server subscribtion to: messages');
+ //return Inventory.find({$where: 'this.id_path.match('^/WebEX-Mirantis@Cisco/')'});
+ //return Inventory.find({ 'show_in_tree': true });
+ return Messages.find({});
+});
+
+Meteor.publish('messages?_id', function (_id) {
+ console.log('server subscribtion to: messages?_id');
+ console.log('_id', _id);
+
+ let query = { _id: _id };
+ return Messages.find(query);
+});
+
+Meteor.publish('messages?level', function (level) {
+ var query = {
+ level: level
+ };
+
+ /*
+ var counterName = 'messages?level!counter?' +
+ 'level=' + level;
+
+ console.log('server subscription to: ' + counterName);
+ Counts.publish(this, counterName, Messages.find(query));
+ */
+
+ console.log('server subscribtion to: messages?level');
+ console.log('- level: ' + level);
+ return Messages.find(query);
+});
+
+Meteor.publish('messages?env+level', function (env, level) {
+ var query = {
+ environment: env,
+ level: level
+ };
+ /*
+ var counterName = 'messages?env+level!counter?env=' +
+ env + '&level=' + level;
+
+ console.log('server subscription to: messages - counter');
+ console.log(' - name: ' + counterName);
+ Counts.publish(this, counterName, Messages.find(query));
+ */
+
+ console.log('server subscribtion to: messages');
+ console.log('- env: ' + env);
+ console.log('- level: ' + level);
+ return Messages.find(query);
+});
+
+Meteor.publish('messages/count', function () {
+ const counterName = `messages/count`;
+ console.log(`subscribe - counter: ${counterName}`);
+
+ return new Counter(counterName, Messages.find({ }));
+});
+
+Meteor.publish('messages/count?env', function (env) {
+ const counterName = `messages/count?env`;
+ console.log(`subscribe - counter: ${counterName}`);
+
+ let query = {};
+ query = R.ifElse(R.isNil, R.always(query), R.assoc('environment', R.__, query))(env);
+ return new Counter(counterName, Messages.find(query));
+});
+
+Meteor.publish('messages/count?level', function (level) {
+ const counterName = `messages/count?level=${level}`;
+ console.log(`subscribe - counter: ${counterName}`);
+
+ return new Counter(counterName, Messages.find({ level: level }));
+});
+
+Meteor.publish('messages/count?level&env', function (level, env) {
+ const counterName = `messages/count?level=${level}&env=${env}`;
+ console.log(`subscribe - counter: ${counterName}`);
+
+ let query = { level: level };
+ query = R.ifElse(R.isNil, R.always(query), R.assoc('environment', R.__, query))(env);
+
+ return new Counter(counterName, Messages.find(query)); });
diff --git a/ui/imports/api/migrations/migrations.js b/ui/imports/api/migrations/migrations.js
new file mode 100644
index 0000000..79411b1
--- /dev/null
+++ b/ui/imports/api/migrations/migrations.js
@@ -0,0 +1,20 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { CliqueTypes } from '/imports/api/clique-types/clique-types';
+
+Migrations.add({
+ version: 1,
+ up: () => {
+ console.log('migrating: add clique type constaints for env+name, env+focal_point_type');
+ CliqueTypes._ensureIndex({ environment: 1, name: 1 });
+ CliqueTypes._ensureIndex({ environment: 1, focal_point_type: 1 });
+ },
+ down: () => {
+ }
+});
diff --git a/ui/imports/api/scans/methods.js b/ui/imports/api/scans/methods.js
new file mode 100644
index 0000000..82af820
--- /dev/null
+++ b/ui/imports/api/scans/methods.js
@@ -0,0 +1,55 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { ValidatedMethod } from 'meteor/mdg:validated-method';
+import * as R from 'ramda';
+
+import { Scans } from './scans';
+
+export const insert = new ValidatedMethod({
+ name: 'scans.insert',
+ validate: Scans.simpleSchema()
+ .pick([
+ 'environment',
+ 'object_id',
+ 'log_level',
+ 'clear',
+ 'loglevel',
+ 'scan_only_inventory',
+ 'scan_only_links',
+ 'scan_only_cliques',
+ ]).validator({ clean: true, filter: false }),
+ run({
+ environment,
+ object_id,
+ log_level,
+ clear,
+ loglevel,
+ scan_only_inventory,
+ scan_only_links,
+ scan_only_cliques,
+ }) {
+ let scan = Scans.schema.clean({
+ status: 'pending'
+ });
+ scan = R.merge(scan, {
+ environment,
+ object_id,
+ log_level,
+ clear,
+ loglevel,
+ scan_only_inventory,
+ scan_only_links,
+ scan_only_cliques,
+ submit_timestamp: Date.now()
+ });
+
+ Scans.insert(scan);
+ },
+
+});
diff --git a/ui/imports/api/scans/scans.js b/ui/imports/api/scans/scans.js
new file mode 100644
index 0000000..857c2ea
--- /dev/null
+++ b/ui/imports/api/scans/scans.js
@@ -0,0 +1,159 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { Mongo } from 'meteor/mongo';
+import { SimpleSchema } from 'meteor/aldeed:simple-schema';
+import * as R from 'ramda';
+
+import { Constants } from '/imports/api/constants/constants';
+import { StatusesInOperation } from '/imports/api/constants/data/scans-statuses';
+
+export const Scans = new Mongo.Collection('scans', { idGeneration: 'MONGO' });
+
+Scans.schemaRelated = {
+ environment: {
+ label: 'Environment',
+ description: 'Name of environment to scan',
+ disabled: true,
+ },
+ status: {
+ label: 'Status',
+ description: 'Scan lifecycle status',
+ subtype: 'select',
+ options: 'scans_statuses',
+ disabled: true,
+ },
+ object_id: {
+ label: 'Scan specific object',
+ description: 'Object ID',
+ },
+ log_level: {
+ label: 'Log level',
+ description: 'logging level',
+ subtype: 'select',
+ options: 'log_levels',
+ },
+ clear: {
+ label: 'Clear data',
+ description: 'clear all data prior to scanning',
+ },
+ scan_only_inventory: {
+ label: 'Scan only inventory',
+ description: 'do only scan to inventory',
+ },
+ scan_only_links: {
+ label: 'Scan only links',
+ description: 'do only links creation',
+ },
+ scan_only_cliques: {
+ label: 'Scan only cliques',
+ description: 'do only cliques creation',
+ },
+};
+
+Scans.scansOnlyFields = ['scan_only_inventory', 'scan_only_links', 'scan_only_cliques'];
+
+let schema = {
+ _id: { type: { _str: { type: String, regEx: SimpleSchema.RegEx.Id } } },
+ environment: {
+ type: String
+ },
+ status: {
+ type: String,
+ defaultValue: 'draft',
+ custom: function () {
+ let that = this;
+ let statuses = Constants.findOne({ name: 'scans_statuses' }).data;
+
+ if (R.isNil(R.find(R.propEq('value', that.value), statuses))) {
+ return 'notAllowed';
+ }
+ },
+ },
+ object_id: {
+ type: String,
+ optional: true,
+ },
+ log_level: {
+ type: String,
+ defaultValue: 'warning',
+ custom: function () {
+ let that = this;
+ let logLevels = Constants.findOne({ name: 'log_levels' }).data;
+
+ if (R.isNil(R.find(R.propEq('value', that.value), logLevels))) {
+ return 'notAllowed';
+ }
+ },
+ },
+ clear: {
+ type: Boolean,
+ defaultValue: true,
+ },
+ scan_only_inventory: {
+ type: Boolean,
+ defaultValue: false,
+ },
+ scan_only_links: {
+ type: Boolean,
+ defaultValue: false,
+ },
+ scan_only_cliques: {
+ type: Boolean,
+ defaultValue: false,
+ },
+ submit_timestamp: {
+ type: Date,
+ defaultValue: null
+ },
+
+};
+
+Scans.schema = new SimpleSchema(schema);
+Scans.schema.addValidator(function () {
+ let that = this;
+ let env = that.field('environment').value;
+
+ let currentScansCount = Scans.find({
+ environment: env,
+ status: { $in: StatusesInOperation }
+ }).count();
+
+ if (currentScansCount > 0) {
+ throw {
+ isError: true,
+ type: 'notUinque',
+ data: [],
+ message: 'There is already a scan in progress.'
+ };
+ }
+
+ let scanOnlyFields = R.filter( f => that.field(f).value, Scans.scansOnlyFields);
+
+ if(scanOnlyFields.length > 1) {
+ throw {
+ isError: true,
+ type: 'conflict',
+ data: scanOnlyFields,
+ message: 'Only one of the scan only fields can be selected'
+ };
+ }
+
+});
+
+Scans.attachSchema(Scans.schema);
+
+Scans.schemaRelated = R.mapObjIndexed((relatedItem, key) => {
+ return R.merge(relatedItem, {
+ type: schema[key].type
+ });
+
+}, Scans.schemaRelated);
+
+export const subsScansEnvPageAmountSorted = 'scans?env*&page&amount&sortField&sortDirection';
+export const subsScansEnvPageAmountSortedCounter = `${subsScansEnvPageAmountSorted}!counter`;
diff --git a/ui/imports/api/scans/server/methods.js b/ui/imports/api/scans/server/methods.js
new file mode 100644
index 0000000..0fe43c2
--- /dev/null
+++ b/ui/imports/api/scans/server/methods.js
@@ -0,0 +1,44 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { check } from 'meteor/check';
+import * as R from 'ramda';
+import { Scans } from '../scans';
+import { Environments } from '/imports/api/environments/environments';
+
+Meteor.methods({
+ 'scansFind?start-timestamp-before': function (startTimestamp) {
+ console.log('method server: scanFind?start-timestamp-before',
+ R.toString(startTimestamp));
+
+ check(startTimestamp, Date);
+ this.unblock();
+
+ let query = { start_timestamp: { $lt: startTimestamp }};
+ let scan = Scans.findOne(query, {
+ sort: { start_timestamp: -1 }
+ });
+
+ let environment = R.ifElse(
+ R.isNil,
+ R.always(null),
+ (scan) => {
+ console.log('finding environment:', scan.environment);
+ let env = Environments.findOne({ name: scan.environment });
+ console.log('found env:', env);
+ return env;
+ })(scan);
+
+ console.log('found scan', scan);
+
+ return {
+ environment: environment,
+ scan: scan,
+ };
+ },
+});
diff --git a/ui/imports/api/scans/server/publications.js b/ui/imports/api/scans/server/publications.js
new file mode 100644
index 0000000..774fe3d
--- /dev/null
+++ b/ui/imports/api/scans/server/publications.js
@@ -0,0 +1,82 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { Meteor } from 'meteor/meteor';
+import * as R from 'ramda';
+import { Counts } from 'meteor/tmeasday:publish-counts';
+
+import { Scans,
+ subsScansEnvPageAmountSorted,
+ subsScansEnvPageAmountSortedCounter,
+} from '../scans.js';
+
+Meteor.publish('scans?env', function (env_name) {
+ console.log('server subscribtion: scans?env');
+ console.log(env_name);
+
+ return Scans.find({
+ environment: env_name,
+ });
+});
+
+Meteor.publish('scans?env*', function (env) {
+ console.log('server subscribtion: scans?env*');
+ console.log(env);
+
+ //let that = this;
+
+ let query = {};
+ if (! R.isNil(env)) { query = R.assoc('environment', env, query); }
+ console.log('-query: ', query);
+ return Scans.find(query);
+});
+
+Meteor.publish(subsScansEnvPageAmountSorted, function (
+ env, page, amountPerPage, sortField, sortDirection) {
+
+ console.log(`server subscribtion: ${subsScansEnvPageAmountSorted}`);
+ console.log(env);
+ console.log('page: ', page);
+ console.log('amount: ', amountPerPage);
+ console.log('sortField: ', sortField, R.isNil(sortField));
+ console.log('sortDirection: ', sortDirection);
+
+ let skip = (page - 1) * amountPerPage;
+
+ let query = {};
+ if (! R.isNil(env)) { query = R.assoc('environment', env, query); }
+ console.log('-query: ', query);
+ let sortParams = {};
+
+ sortParams = R.ifElse(R.isNil, R.always(sortParams),
+ R.assoc(R.__, sortDirection, sortParams))(sortField);
+
+ console.log('sort params:', sortParams);
+
+ let qParams = {
+ limit: amountPerPage,
+ skip: skip,
+ sort: sortParams,
+ };
+
+ Counts.publish(this, subsScansEnvPageAmountSortedCounter, Scans.find(query), {
+ noReady: true
+ });
+
+ return Scans.find(query, qParams);
+});
+
+Meteor.publish('scans?id', function (id) {
+ console.log('server subscribtion: scans?id');
+ console.log('-id: ', id);
+
+ //let that = this;
+
+ let query = { _id: id };
+ return Scans.find(query);
+});
diff --git a/ui/imports/api/scheduled-scans/methods.js b/ui/imports/api/scheduled-scans/methods.js
new file mode 100644
index 0000000..22f8110
--- /dev/null
+++ b/ui/imports/api/scheduled-scans/methods.js
@@ -0,0 +1,131 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { ValidatedMethod } from 'meteor/mdg:validated-method';
+import * as R from 'ramda';
+
+import { ScheduledScans } from './scheduled-scans';
+
+export const insert = new ValidatedMethod({
+ name: 'scheduled-scans.insert',
+ validate: ScheduledScans.simpleSchema()
+ .pick([
+ 'environment',
+ 'object_id',
+ 'log_level',
+ 'clear',
+ 'loglevel',
+ 'scan_only_inventory',
+ 'scan_only_links',
+ 'scan_only_cliques',
+ 'freq',
+ ]).validator({ clean: true, filter: false }),
+ run({
+ environment,
+ object_id,
+ log_level,
+ clear,
+ loglevel,
+ scan_only_inventory,
+ scan_only_links,
+ scan_only_cliques,
+ freq,
+ }) {
+ let scan = ScheduledScans.schema.clean({ });
+
+ scan = R.merge(scan, {
+ environment,
+ object_id,
+ log_level,
+ clear,
+ loglevel,
+ scan_only_inventory,
+ scan_only_links,
+ scan_only_cliques,
+ freq,
+ submit_timestamp: Date.now()
+ });
+
+ ScheduledScans.insert(scan);
+ },
+
+});
+
+export const update = new ValidatedMethod({
+ name: 'scheduled_scans.update',
+ validate: ScheduledScans.simpleSchema()
+ .pick([
+ '_id',
+ 'environment',
+ 'object_id',
+ 'log_level',
+ 'clear',
+ 'loglevel',
+ 'scan_only_inventory',
+ 'scan_only_links',
+ 'scan_only_cliques',
+ 'freq',
+ ]).validator({ clean: true, filter: false }),
+ run({
+ _id,
+ environment,
+ object_id,
+ log_level,
+ clear,
+ loglevel,
+ scan_only_inventory,
+ scan_only_links,
+ scan_only_cliques,
+ freq,
+ }) {
+ let item = ScheduledScans.findOne({ _id: _id });
+ console.log('scheduled scan for update: ', item);
+
+ item = R.merge(R.pick([
+ 'environment',
+ 'object_id',
+ 'log_level',
+ 'clear',
+ 'loglevel',
+ 'scan_only_inventory',
+ 'scan_only_links',
+ 'scan_only_cliques',
+ 'submit_timestamp',
+ 'freq',
+ ], item), {
+ environment,
+ object_id,
+ log_level,
+ clear,
+ loglevel,
+ scan_only_inventory,
+ scan_only_links,
+ scan_only_cliques,
+ freq,
+ submit_timestamp: Date.now()
+ });
+
+ ScheduledScans.update({ _id: _id }, { $set: item });
+ }
+});
+
+export const remove = new ValidatedMethod({
+ name: 'scheduled_scans.remove',
+ validate: ScheduledScans.simpleSchema()
+ .pick([
+ '_id',
+ ]).validator({ clean: true, filter: false }),
+ run({
+ _id
+ }) {
+ let item = ScheduledScans.findOne({ _id: _id });
+ console.log('scheduled scan for remove: ', item);
+
+ ScheduledScans.remove({ _id: _id });
+ }
+});
diff --git a/ui/imports/api/scheduled-scans/scheduled-scans.js b/ui/imports/api/scheduled-scans/scheduled-scans.js
new file mode 100644
index 0000000..66ae5d1
--- /dev/null
+++ b/ui/imports/api/scheduled-scans/scheduled-scans.js
@@ -0,0 +1,91 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { Mongo } from 'meteor/mongo';
+import { SimpleSchema } from 'meteor/aldeed:simple-schema';
+import { Constants } from '/imports/api/constants/constants';
+import * as R from 'ramda';
+
+export const ScheduledScans = new Mongo.Collection('scheduled_scans', { idGeneration: 'MONGO' });
+
+export const scansOnlyFields = ['scan_only_inventory', 'scan_only_links', 'scan_only_cliques'];
+
+let schema = new SimpleSchema({
+ _id: { type: { _str: { type: String, regEx: SimpleSchema.RegEx.Id } } },
+ environment: {
+ type: String
+ },
+ object_id: {
+ type: String,
+ optional: true,
+ },
+ log_level: {
+ type: String,
+ defaultValue: 'warning',
+ custom: function () {
+ let that = this;
+ let logLevels = Constants.findOne({ name: 'log_levels' }).data;
+
+ if (R.isNil(R.find(R.propEq('value', that.value), logLevels))) {
+ return 'notAllowed';
+ }
+ },
+ },
+ clear: {
+ type: Boolean,
+ defaultValue: true,
+ },
+ scan_only_inventory: {
+ type: Boolean,
+ defaultValue: true,
+ },
+ scan_only_links: {
+ type: Boolean,
+ defaultValue: false,
+ },
+ scan_only_cliques: {
+ type: Boolean,
+ defaultValue: false,
+ },
+ freq: {
+ type: String,
+ defaultValue: 'WEEKLY',
+ },
+ submit_timestamp: {
+ type: Date,
+ defaultValue: null
+ },
+ scheduled_timestamp: {
+ type: Date,
+ defaultValue: null,
+ optional: true,
+ }
+});
+
+schema.addValidator(function () {
+ let that = this;
+ let currentScansOnlyFields =
+ R.reject( f => that.field(f).value == false, scansOnlyFields);
+
+ if(currentScansOnlyFields.length > 1) {
+ throw {
+ isError: true,
+ type: 'conflict',
+ data: currentScansOnlyFields,
+ message: `Only one of the scan only fields can be selected. ${R.toString(currentScansOnlyFields)}`
+ };
+ }
+});
+
+ScheduledScans.schema = schema;
+ScheduledScans.attachSchema(ScheduledScans.schema);
+
+export const subsScheduledScansPageAmountSorted = 'scheduled_scans?page&amount&sortField&sortDirection';
+export const subsScheduledScansPageAmountSortedCounter = `${subsScheduledScansPageAmountSorted}!counter`;
+
+export const subsScheduledScansId = 'scheduled_scans?_id';
diff --git a/ui/imports/api/scheduled-scans/server/methods.js b/ui/imports/api/scheduled-scans/server/methods.js
new file mode 100644
index 0000000..17ed990
--- /dev/null
+++ b/ui/imports/api/scheduled-scans/server/methods.js
@@ -0,0 +1,27 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { check } from 'meteor/check';
+import * as R from 'ramda';
+import { ScheduledScans } from '../scheduled-scans';
+
+Meteor.methods({
+ 'scheduledScansFind?env': function (env) {
+ console.log('method server: scheduledScansFind?env', R.toString(env));
+
+ check(env, String);
+ this.unblock();
+
+ let query = { environment: env };
+ let scheduledScan = ScheduledScans.findOne(query, {});
+
+ return {
+ item: scheduledScan
+ };
+ }
+});
diff --git a/ui/imports/api/scheduled-scans/server/publications.js b/ui/imports/api/scheduled-scans/server/publications.js
new file mode 100644
index 0000000..97acc21
--- /dev/null
+++ b/ui/imports/api/scheduled-scans/server/publications.js
@@ -0,0 +1,60 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { Meteor } from 'meteor/meteor';
+import * as R from 'ramda';
+import { Counts } from 'meteor/tmeasday:publish-counts';
+
+import { ScheduledScans,
+ subsScheduledScansPageAmountSorted,
+ subsScheduledScansPageAmountSortedCounter,
+ subsScheduledScansId,
+} from '../scheduled-scans.js';
+
+Meteor.publish(subsScheduledScansPageAmountSorted, function (
+ page, amountPerPage, sortField, sortDirection) {
+
+ console.log(`server subscribtion: ${subsScheduledScansPageAmountSorted}`);
+ console.log('page: ', page);
+ console.log('amount: ', amountPerPage);
+ console.log('sortField: ', sortField, R.isNil(sortField));
+ console.log('sortDirection: ', sortDirection);
+
+ let skip = (page - 1) * amountPerPage;
+
+ let query = {};
+ console.log('-query: ', query);
+ let sortParams = {};
+
+ sortParams = R.ifElse(R.isNil, R.always(sortParams),
+ R.assoc(R.__, sortDirection, sortParams))(sortField);
+
+ console.log('sort params:', sortParams);
+
+ let qParams = {
+ limit: amountPerPage,
+ skip: skip,
+ sort: sortParams,
+ };
+
+ Counts.publish(this, subsScheduledScansPageAmountSortedCounter, ScheduledScans.find(query), {
+ noReady: true
+ });
+
+ return ScheduledScans.find(query, qParams);
+});
+
+Meteor.publish(subsScheduledScansId, function (_id) {
+ console.log(`server subscribtion: ${subsScheduledScansId}`);
+ console.log('-id: ', _id);
+
+ //let that = this;
+
+ let query = { _id: _id };
+ return ScheduledScans.find(query);
+});
diff --git a/ui/imports/api/simple-schema.init.js b/ui/imports/api/simple-schema.init.js
new file mode 100644
index 0000000..4f5addb
--- /dev/null
+++ b/ui/imports/api/simple-schema.init.js
@@ -0,0 +1,13 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { SimpleSchema } from 'meteor/aldeed:simple-schema';
+
+SimpleSchema.messages({
+ 'alreadyExists': 'item already exists'
+});
diff --git a/ui/imports/api/statistics/helpers.js b/ui/imports/api/statistics/helpers.js
new file mode 100644
index 0000000..7cb78e8
--- /dev/null
+++ b/ui/imports/api/statistics/helpers.js
@@ -0,0 +1,64 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import * as R from 'ramda';
+
+export function createGraphQuerySchema(
+ env,
+ object_id,
+ type,
+ flowType,
+ timeStart,
+ timeEnd,
+ sourceMacAddress,
+ destinationMacAddress,
+ sourceIPv4Address,
+ destinationIPv4Address) {
+
+ let schema = {
+ environment: env,
+ object_id: object_id,
+ type: type,
+ flowType: flowType,
+ /*
+ averageArrivalNanoSeconds: {
+ $gte: timeStart,
+ //$lt: timeEnd
+ }
+ */
+ data_arrival_avg: {
+ $gte: timeStart,
+ }
+ };
+
+ if (! R.isNil(timeEnd)) {
+ //schema = R.assocPath(['averageArrivalNanoSeconds', '$lt'], timeEnd, schema);
+ schema = R.assocPath(['data_arrival_avg', '$lt'], timeEnd, schema);
+ }
+
+ switch (flowType) {
+ case 'L2':
+ schema = R.merge(schema, {
+ sourceMacAddress: sourceMacAddress,
+ destinationMacAddress: destinationMacAddress
+ });
+ break;
+
+ case 'L3':
+ schema = R.merge(schema, {
+ sourceIPv4Address: sourceIPv4Address,
+ destinationIPv4Address: destinationIPv4Address
+ });
+ break;
+
+ default:
+ break;
+ }
+
+ return schema;
+}
diff --git a/ui/imports/api/statistics/methods.js b/ui/imports/api/statistics/methods.js
new file mode 100644
index 0000000..23a216d
--- /dev/null
+++ b/ui/imports/api/statistics/methods.js
@@ -0,0 +1,159 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import * as R from 'ramda';
+import { Statistics } from './statistics';
+import { createGraphQuerySchema } from './helpers';
+
+Meteor.methods({
+ 'statistics.flowTypes?env&object_id&type'({ env, object_id, type}) {
+ console.log('subscribe: statistics.flowTypes?env&object_id&type');
+ console.log(`- env: ${env}`);
+ console.log(`- object_id: ${object_id}`);
+ console.log(`- type: ${type}`);
+
+ let pipeline = [{
+ $match: {
+ environment: env,
+ object_id: object_id,
+ type: type
+ }
+ }, {
+ $group: {
+ _id: { flowType: '$flowType' },
+ flowType: { $first: '$flowType' }
+ }
+ }];
+
+ return Statistics.aggregate(pipeline);
+ },
+
+ 'statistics.srcMacAddresses?env&object_id&type&flowType'(
+ { env, object_id, type, flowType }) {
+
+ let pipeline = [{
+ $match: {
+ environment: env,
+ object_id: object_id,
+ type: type,
+ flowType: flowType
+ }
+ }, {
+ $group: {
+ _id: { sourceMacAddress: '$sourceMacAddress' },
+ sourceMacAddress: { $first: '$sourceMacAddress' }
+ }
+ }];
+
+ return Statistics.aggregate(pipeline);
+ },
+
+ 'statistics.dstMacAddresses?env&object_id&type&flowType'(
+ { env, object_id, type, flowType }) {
+
+ let pipeline = [{
+ $match: {
+ environment: env,
+ object_id: object_id,
+ type: type,
+ flowType: flowType
+ }
+ }, {
+ $group: {
+ _id: { destinationMacAddress: '$destinationMacAddress' },
+ destinationMacAddress: { $first: '$destinationMacAddress' }
+ }
+ }];
+
+ return Statistics.aggregate(pipeline);
+ },
+
+ 'statistics.srcIPv4Addresses?env&object_id&type&flow_typw'(
+ { env, object_id, type, flowType }) {
+ let pipeline = [{
+ $match: {
+ environment: env,
+ object_id: object_id,
+ type: type,
+ flowType: flowType
+ }
+ }, {
+ $group: {
+ _id: { sourceIPv4Address: '$sourceIPv4Address' },
+ sourceIPv4Address: { $first: '$sourceIPv4Address' }
+ }
+ }];
+
+ return Statistics.aggregate(pipeline);
+ },
+
+ 'statistics.dstIPv4Addresses?env&object_id&type&flowType'(
+ { env, object_id, type, flowType }) {
+ let pipeline = [{
+ $match: {
+ environment: env,
+ object_id: object_id,
+ type: type,
+ flowType: flowType
+ }
+ }, {
+ $group: {
+ _id: { destinationIPv4Address: '$destinationIPv4Addres' },
+ destinationIPv4Address: { $first: '$destinationIPv4Addres' }
+ }
+ }];
+
+ return Statistics.aggregate(pipeline);
+ },
+
+ 'statistics!graph-frames'({
+ env,
+ object_id,
+ type,
+ flowType,
+ timeStart,
+ timeEnd,
+ sourceMacAddress,
+ destinationMacAddress,
+ sourceIPv4Address,
+ destinationIPv4Address
+ }) {
+ let schema = createGraphQuerySchema(
+ env,
+ object_id,
+ type,
+ flowType,
+ timeStart,
+ timeEnd,
+ sourceMacAddress,
+ destinationMacAddress,
+ sourceIPv4Address,
+ destinationIPv4Address);
+
+ console.log('statistics!graph-frames');
+ console.log(`- env: ${env}`);
+ console.log(`- object_id: ${object_id}`);
+ console.log(`- type: ${type}`);
+ console.log(`- flowType: ${flowType}`);
+ console.log(`- timeStart: ${timeStart}`);
+ console.log(`- timeEnd: ${timeEnd}`);
+ console.log(`- sourceMacAddress: ${sourceMacAddress}`);
+ console.log(`- destinationMacAddress: ${destinationMacAddress}`);
+ console.log(`- sourceIPv4Address: ${sourceIPv4Address}`);
+ console.log(`- destinationIPv4Address: ${destinationIPv4Address}`);
+
+ //let data = Statistics.find(schema).fetch();
+ let data = Statistics.findOne(schema);
+ console.log(`- averageArrivalNanoSeconds: ${R.path([0, 'averageArrivalNanoSeconds'], data)}`);
+
+ return data;
+ }
+});
+
+
+
diff --git a/ui/imports/api/statistics/server/publications.js b/ui/imports/api/statistics/server/publications.js
new file mode 100644
index 0000000..f69be56
--- /dev/null
+++ b/ui/imports/api/statistics/server/publications.js
@@ -0,0 +1,52 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { Meteor } from 'meteor/meteor';
+//import { Counts } from 'meteor/tmeasday:publish-counts';
+import { Statistics } from '../statistics.js';
+import { createGraphQuerySchema } from '../helpers';
+
+Meteor.publish('statistics!graph-frames', function ({
+ env,
+ object_id,
+ type,
+ flowType,
+ timeStart,
+ sourceMacAddress,
+ destinationMacAddress,
+ sourceIPv4Address,
+ destinationIPv4Address
+}) {
+ console.log('server subscribe: statistics?graph-frames');
+
+ let schema = createGraphQuerySchema(
+ env,
+ object_id,
+ type,
+ flowType,
+ timeStart,
+ null,
+ sourceMacAddress,
+ destinationMacAddress,
+ sourceIPv4Address,
+ destinationIPv4Address);
+
+ console.log('statistics!graph-frames');
+ console.log(`- env: ${env}`);
+ console.log(`- object_id: ${object_id}`);
+ console.log(`- type: ${type}`);
+ console.log(`- flowType: ${flowType}`);
+ console.log(`- timeStart: ${timeStart}`);
+ console.log(`- sourceMacAddress: ${sourceMacAddress}`);
+ console.log(`- destinationMacAddress: ${destinationMacAddress}`);
+ console.log(`- sourceIPv4Address: ${sourceIPv4Address}`);
+ console.log(`- destinationIPv4Address: ${destinationIPv4Address}`);
+
+ return Statistics.find(schema);
+});
+
diff --git a/ui/imports/api/statistics/statistics.js b/ui/imports/api/statistics/statistics.js
new file mode 100644
index 0000000..3391933
--- /dev/null
+++ b/ui/imports/api/statistics/statistics.js
@@ -0,0 +1,14 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { Mongo } from 'meteor/mongo';
+//import { SimpleSchema } from 'meteor/aldeed:simple-schema';
+//import * as R from 'ramda';
+
+export const Statistics = new Mongo.Collection(
+ 'statistics', { idGeneration: 'MONGO' });
diff --git a/ui/imports/api/supported_environments/methods.js b/ui/imports/api/supported_environments/methods.js
new file mode 100644
index 0000000..1eda375
--- /dev/null
+++ b/ui/imports/api/supported_environments/methods.js
@@ -0,0 +1,8 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
diff --git a/ui/imports/api/supported_environments/server/publications.js b/ui/imports/api/supported_environments/server/publications.js
new file mode 100644
index 0000000..8fef880
--- /dev/null
+++ b/ui/imports/api/supported_environments/server/publications.js
@@ -0,0 +1,17 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { Meteor } from 'meteor/meteor';
+import { SupportedEnvironments,
+ subsNameSupportedEnvs
+} from '../supported_environments.js';
+
+Meteor.publish(subsNameSupportedEnvs, function () {
+ console.log(`server subscribtion to: ${subsNameSupportedEnvs}`);
+ return SupportedEnvironments.find({});
+});
diff --git a/ui/imports/api/supported_environments/supported_environments.js b/ui/imports/api/supported_environments/supported_environments.js
new file mode 100644
index 0000000..55c5745
--- /dev/null
+++ b/ui/imports/api/supported_environments/supported_environments.js
@@ -0,0 +1,49 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { Mongo } from 'meteor/mongo';
+import * as R from 'ramda';
+
+export const SupportedEnvironments = new Mongo.Collection(
+ 'supported_environments', { idGeneration: 'MONGO' });
+
+export const subsNameSupportedEnvs = 'supported-environments';
+
+export function isMonitoringSupported(distribution, type_drivers, mechanism_drivers) {
+ console.log('isMonitoringSupported');
+ console.log(`distribution: ${R.toString(distribution)}`);
+ console.log(`type_drivers: ${R.toString(type_drivers)}`);
+ console.log(`mechanism_drivers: ${R.toString(mechanism_drivers)}`);
+
+ let result = SupportedEnvironments.find({
+ 'environment.distribution': distribution,
+ 'environment.type_drivers': type_drivers,
+ 'environment.mechanism_drivers': { $in: mechanism_drivers },
+ 'features.monitoring': true
+ }).count() > 0;
+
+ console.log(`result: ${R.toString(result)}`);
+ return result;
+}
+
+export function isListeningSupported(distribution, type_drivers, mechanism_drivers) {
+ console.log('isListeningSupported');
+ console.log(`distribution: ${R.toString(distribution)}`);
+ console.log(`type_drivers: ${R.toString(type_drivers)}`);
+ console.log(`mechanism_drivers: ${R.toString(mechanism_drivers)}`);
+
+ let result = SupportedEnvironments.find({
+ 'environment.distribution': distribution,
+ 'environment.type_drivers': type_drivers,
+ 'environment.mechanism_drivers': { $in: mechanism_drivers },
+ 'features.listening': true
+ }).count() > 0;
+
+ console.log(`result: ${R.toString(result)}`);
+ return result;
+}
diff --git a/ui/imports/index.styl b/ui/imports/index.styl
new file mode 100644
index 0000000..d810c63
--- /dev/null
+++ b/ui/imports/index.styl
@@ -0,0 +1 @@
+@import 'ui/*'
diff --git a/ui/imports/lib/d3-graph.js b/ui/imports/lib/d3-graph.js
new file mode 100644
index 0000000..311ad95
--- /dev/null
+++ b/ui/imports/lib/d3-graph.js
@@ -0,0 +1,573 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { Inventory } from '/imports/api/inventories/inventories';
+import { Cliques } from '/imports/api/cliques/cliques';
+import { Links } from '/imports/api/links/links';
+import { NodeHoverAttr } from '/imports/api/attributes_for_hover_on_data/attributes_for_hover_on_data';
+import * as cola from 'webcola';
+import { store } from '/imports/ui/store/store';
+import { activateGraphTooltipWindow } from '/imports/ui/actions/graph-tooltip-window.actions';
+import { closeGraphTooltipWindow } from '/imports/ui/actions/graph-tooltip-window.actions';
+import { activateVedgeInfoWindow } from '/imports/ui/actions/vedge-info-window.actions';
+import * as R from 'ramda';
+
+let d3Graph = {
+ color:'',
+
+ zoomer:function(){
+ var width = '100%',
+ height = '100%';
+ var xScale = d3.scale.linear()
+ .domain([0,width]).range([0,width]);
+ var yScale = d3.scale.linear()
+ .domain([0,height]).range([0, height]);
+ return d3.behavior.zoom().
+ scaleExtent([0.1,10]).
+ x(xScale).
+ y(yScale).
+ on('zoomstart', d3Graph.zoomstart).
+ on('zoom', d3Graph.redraw);
+ },
+
+ svg:'',
+ force:'',
+ link:'',
+ node:'',
+ linkText:'',
+
+ graph:{
+ nodes:[],
+ links:[],
+ },
+
+ zoomstart:function () {
+ var node = d3Graph.svg.selectAll('.node');
+ node.each(function(d) {
+ d.selected = false;
+ d.previouslySelected = false;
+ });
+ node.classed('selected', false);
+ },
+
+ /* depreacted - not used ?
+ getGraphData:function(nodeId){
+
+ var invNodes = Inventory.find({ 'type': 'instance', $and: [ { 'host': nodeId } ] });
+
+ var edges = [];
+ var nodes = [];
+
+ invNodes.forEach(function(n){
+ nodes = n['Entities'];
+ edges = n['Relations'];
+ });
+
+ nodes.forEach(function(n){
+ n.name = n.object_name;
+ });
+
+ var edges_new = [];
+ edges.forEach(function(e) {
+ var sourceNode = nodes.filter(function(n) { return n.id === e.from; })[0],
+ targetNode = nodes.filter(function(n) { return n.id === e.to; })[0];
+
+ edges_new.push({source: sourceNode, target: targetNode, value: 1,label: e.label,attributes: e.attributes});
+ });
+//any links with duplicate source and target get an incremented 'linknum'
+ for (var i=0; i<edges_new.length; i++) {
+ if (i != 0 &&
+ edges_new[i].source == edges_new[i-1].source &&
+ edges_new[i].target == edges_new[i-1].target) {
+
+ edges_new[i].linknum = edges_new[i-1].linknum + 1;
+ }
+ else {edges_new[i].linknum = 1;}
+ }
+ //var graph = {};
+ this.graph.nodes = nodes;
+ this.graph.links = edges_new;
+
+ },
+ */
+
+ getGraphDataByClique:function(nodeObjId){
+ // Clique: one instance per graph. A focal point describing a node, and with links data.
+ // TODO: findOne or .each.
+ var cliques = Cliques.find({ focal_point: new Mongo.ObjectID(nodeObjId) }).fetch();
+
+
+ var nodes = [];
+ var edges_new = [];
+
+ if (R.length(cliques) === 0) {
+ return;
+ }
+
+ // CliquesLinks: All the links for a specific clique
+ var cliquesLinks = [];
+ cliques[0].links.forEach(function(n){
+ cliquesLinks.push(n);
+ });
+
+ // LinksList = Map(Clique.links -> links collection)
+ var linksList = Links.find({ _id: {$in: cliquesLinks}}).fetch();
+ //console.log(linksList);
+
+ // Create nodes from the links endpoints.
+ // Nodes = link source & target (objectid)
+ linksList.forEach(function(linkItem){
+ nodes.push(linkItem['source']);
+ nodes.push(linkItem['target']);
+ });
+
+ // NodesList = Nodes exapneded.
+ var nodesList = Inventory.find({ _id: {$in: nodes}}).fetch();
+
+ // Links list: expanend source/target nodes to create in memory data graph - links,nodes.
+ linksList.forEach(function(linkItem){
+ var sourceNode = nodesList.filter(function(n) {
+ return n._id._str === linkItem.source._str;
+ })[0];
+
+ var targetNode = nodesList.filter(function(n) {
+ return n._id._str === linkItem.target._str;
+ })[0];
+
+ edges_new.push({
+ source: sourceNode,
+ target: targetNode,
+ value: 1,
+ label: linkItem.link_name,
+ attributes: linkItem
+ });
+
+ });
+
+ // Expend nodeslist to include linked attributes.
+ nodesList.forEach(function(nodeItem){
+ nodeItem.attributes = [];
+ var attrHoverFields = NodeHoverAttr.find({ 'type': nodeItem['type']}).fetch();
+ if(attrHoverFields.length){
+ attrHoverFields[0].attributes.forEach(function(field){
+ if(nodeItem[field]){
+ var object = {};
+ object[field] = nodeItem[field];
+ nodeItem.attributes.push(object);
+ }
+ });
+ }
+ });
+
+ this.graph.nodes = nodesList;
+ this.graph.links = edges_new;
+
+ },
+
+ createGraphData: function (width, height){
+ //var self = this;
+ //var width = 500;
+ //var height = 900;
+
+ this.color = d3.scale.category20();
+ /*
+ this.svg = d3.select('#dgraphid').append('svg')
+ .attr('width', '100%')
+ .attr('height', '100%')
+ .attr('pointer-events', 'all')
+ //.attr('transform', 'translate(250,250) scale(0.3)')
+ .call(d3.behavior.zoom().on('zoom', this.redraw))
+ .append('svg:g');
+
+ //.append('g');
+
+ this.force = cola.d3adaptor().convergenceThreshold(0.1)
+ //.linkDistance(200)
+ .size([width, height]);
+ */
+ //var focused = null;
+
+ this.force = cola.d3adaptor().convergenceThreshold(0.1)
+ //.linkDistance(200)
+ .size([width, height]);
+
+ var outer = d3.select('#dgraphid')
+ .append('svg')
+ .attr({
+ width: '100%',
+ height: '100%',
+ 'pointer-events': 'all',
+ class: 'os-d3-graph'
+ });
+
+ outer.append('rect')
+ .attr({ class: 'background', width: '100%', height: '100%' })
+ .call(this.zoomer());
+ /*.call(d3.behavior.zoom()
+ .on('zoom', function(d) {
+ d3Graph.svg.attr('transform', 'translate(' + d3.event.translate + ')' + ' scale(' + d3.event.scale + ')');
+ }))*/
+ //.on('mouseover', function () { focused = this; });
+
+ //d3.select('body').on('keydown', function () { d3.select(focused); /* then do something with it here */ });
+ //d3.select('#dgraphid').on('keydown', d3Graph.keydown());
+
+ let scale = 0.5;
+
+ this.svg = outer
+ .append('g')
+ //.attr('transform', 'translate(250,250) scale(0.3)');
+ .attr('transform', 'translate(250,250) scale(' + scale.toString() + ')');
+
+ let fontSize = Math.floor(16 / scale);
+ d3Graph.svg.selectAll('.link-group text')
+ .style('font-size', fontSize + 'px');
+ d3Graph.svg.selectAll('.node text')
+ .style('font-size', fontSize + 'px');
+
+ },
+
+ redraw: function(){
+ //console.log('here', d3.event.translate, d3.event.scale);
+
+ d3Graph.svg.attr('transform',
+ 'translate(' + d3.event.translate + ')'
+ + ' scale(' + d3.event.scale + ')');
+
+ let fontSize = Math.floor(16 / d3.event.scale);
+ d3Graph.svg.selectAll('.link-group text')
+ .style('font-size', fontSize + 'px');
+ d3Graph.svg.selectAll('.node text')
+ .style('font-size', fontSize + 'px');
+
+ },
+
+ updateNetworkGraph:function (){
+ var self = this;
+
+ if (R.isNil(this.svg)) {
+ return;
+ }
+
+ this.svg.selectAll('g').remove();
+ //this.svg.exit().remove();
+
+ this.force
+ .nodes(this.graph.nodes)
+ .links(this.graph.links)
+ .symmetricDiffLinkLengths(250)
+ //.jaccardLinkLengths(300)
+ //.jaccardLinkLengths(80,0.7)
+ .handleDisconnected(true)
+ .avoidOverlaps(true)
+ .start(50, 100, 200);
+
+ /*
+ this.force
+ .on('dragstart', function (d) { d3.event.sourceEvent.stopPropagation(); d3.select(this).classed('dragging', true); } )
+ .on('drag', function (d) { d3.select(this).attr('cx', d.x = d3.event.x).attr('cy', d.y = d3.event.y); } )
+ .on('dragend', function (d) { d3.select(this).classed('dragging', false); });
+ */
+
+
+ // Define the div for the tooltip
+
+ //svg.exit().remove();
+ //graph.constraints = [{'axis':'y', 'left':0, 'right':1, 'gap':25},];
+
+ //.start(10,15,20);
+ /*var path = svg.append('svg:g')
+ .selectAll('path')
+ .data(force.links())
+ .enter().append('svg:path')
+ .attr('class', 'link');;
+ */
+ var link = this.svg.selectAll('.link')
+ .data(this.force.links())
+ .enter()
+ .append('g')
+ .attr('class', 'link-group')
+ .append('line')
+ .attr('class', 'link')
+ .style('stroke-width', function(_d) { return 3; })
+ //.style('stroke-width', function(d) { return Math.sqrt(d.stroke); })
+ .attr('stroke', function (d) {
+ if(d.attributes.state == 'error'){
+ self.blinkLink(d);
+ return 'red';
+ }
+ else if(d.attributes.state == 'warn'){
+ self.blinkLink(d);
+ return 'orange';
+ }
+ else if(d.source.level === d.target.level) {
+ return self.color(d.source.level);
+ }
+ else {
+ return self.color(d.level);
+ //d3.select(this).classed('different-groups', true);
+ }
+ });
+ /*.style('stroke', function(d) {
+ if(d.label == 'net-103'){
+ self.blinkLink(d);
+ return 'red';
+ }
+ //return 'red';
+ //return self.color(d.level);
+ })*/
+
+ var linkText = this.svg.selectAll('.link-group')
+ .append('text')
+ .data(this.force.links())
+ .text(function(d) { return d.label; })
+ .attr('x', function(d) { return (d.source.x + (d.target.x - d.source.x) * 0.5); })
+ .attr('y', function(d) { return (d.source.y + (d.target.y - d.source.y) * 0.5); })
+ .attr('dy', '.25em')
+ .attr('text-anchor', 'right')
+ .on('mouseover', function(d) {
+ store.dispatch(activateGraphTooltipWindow(
+ d.label,
+ d.attributes,
+ d3.event.pageX,
+ d3.event.pageY
+ ));
+ })
+ .on('mouseout', function(_d) {
+ store.dispatch(closeGraphTooltipWindow());
+ });
+
+ var node = this.svg.selectAll('.node')
+ .data(this.force.nodes())
+ .enter().append('g')
+ .attr('class', 'node')
+ .call(this.force.drag);
+
+ // A map from group ID to image URL.
+ var imageByGroup = {
+ 'instance': 'ic_computer_black_48dp_2x.png',
+ 'pnic': 'ic_dns_black_48dp_2x.png',
+ 'vconnector': 'ic_settings_input_composite_black_48dp_2x.png',
+ // 'network': 'ic_cloud_queue_black_48dp_2x.png',
+ 'network': 'ic_cloud_queue_black_48dp_2x.png',
+ 'vedge': 'ic_gamepad_black_48dp_2x.png',
+ 'vservice': 'ic_storage_black_48dp_2x.png',
+ 'vnic': 'ic_settings_input_hdmi_black_48dp_2x.png',
+ 'otep':'ic_keyboard_return_black_48dp_2x.png',
+ 'default':'ic_lens_black_48dp_2x.png'
+ };
+
+ node.append('image')
+ //.attr('xlink:href', 'https://github.com/favicon.ico')
+ .attr('xlink:href', function(d) {
+ if(imageByGroup[d.type]){
+ return `/${imageByGroup[d.type]}`;
+ }
+ else{
+ return `/${imageByGroup['default']}`;
+ }
+
+ })
+ .attr('x', -8)
+ .attr('y', -8)
+ .attr('width', 36)
+ .attr('height', 36)
+ //node.append('circle')
+ .attr('class', 'node')
+ //.attr('r', function(d){return 13;})
+ .on('mouseover', function(d) {
+ store.dispatch(activateGraphTooltipWindow(
+ d.name,
+ d.attributes,
+ d3.event.pageX,
+ d3.event.pageY));
+ })
+ .on('mouseout', function(_d) {
+ store.dispatch(closeGraphTooltipWindow());
+ })
+ .on('click', function(d) {
+ if (d.type === 'vedge') {
+ store.dispatch(activateVedgeInfoWindow(
+ d,
+ d3.event.pageX,
+ d3.event.pageY));
+ }
+ })
+ .style('fill', function(d) {
+ if(d.state == 'error'){
+ self.blinkNode(d);
+ return 'red';
+ }
+ return self.color(d.group);
+ })
+ .call(this.force.drag);
+
+
+ /*
+ .each(function() {
+ var sel = d3.select(this);
+ var state = false;
+ sel.on('dblclick', function () {
+ state = !state;
+ if (state) {
+ sel.style('fill', 'black');
+ } else {
+ sel.style('fill', function (d) {
+ return d.colr;
+ });
+ }
+ });
+ });
+ */
+
+ node.append('text')
+ .attr('dx', 0)
+ .attr('dy', 40)
+ .text(function(d) { return d.object_name; });
+
+
+ this.force.on('tick', function() {
+ link.attr('x1', function(d) { return d.source.x; })
+ .attr('y1', function(d) { return d.source.y; })
+ .attr('x2', function(d) { return d.target.x; })
+ .attr('y2', function(d) { return d.target.y; });
+ /*
+ .attr('dr1', function(d) { return 75/d.source.linknum; })
+ .attr('dr2', function(d) { return 75/d.target.linknum; });
+ */
+
+ node.attr('transform', function(d) {
+ return 'translate(' + d.x + ',' + d.y + ')';
+ });
+
+ linkText
+ .attr('x', function(d) {
+ return (d.source.x + (d.target.x - d.source.x) * 0.5);
+ })
+ .attr('y', function(d) {
+ return (d.source.y + (d.target.y - d.source.y) * 0.5);
+ });
+ });
+
+ },
+
+ centerview: function () {
+ // Center the view on the molecule(s) and scale it so that everything
+ // fits in the window
+ var width = 500;
+ var height = 500;
+
+ if (this.graph === null) return;
+
+ var nodes = this.graph.nodes;
+
+ //no molecules, nothing to do
+ if (nodes.length === 0) return;
+
+ // Get the bounding box
+ var min_x = d3.min(nodes.map(function(d) {return d.x;}));
+ var min_y = d3.min(nodes.map(function(d) {return d.y;}));
+
+ var max_x = d3.max(nodes.map(function(d) {return d.x;}));
+ var max_y = d3.max(nodes.map(function(d) {return d.y;}));
+
+
+ // The width and the height of the graph
+ var mol_width = max_x - min_x;
+ var mol_height = max_y - min_y;
+
+ // how much larger the drawing area is than the width and the height
+ var width_ratio = width / mol_width;
+ var height_ratio = height / mol_height;
+
+ // we need to fit it in both directions, so we scale according to
+ // the direction in which we need to shrink the most
+ var min_ratio = Math.min(width_ratio, height_ratio) * 0.8;
+
+ // the new dimensions of the molecule
+ var new_mol_width = mol_width * min_ratio;
+ var new_mol_height = mol_height * min_ratio;
+
+ // translate so that it's in the center of the window
+ var x_trans = -(min_x) * min_ratio + (width - new_mol_width) / 2;
+ var y_trans = -(min_y) * min_ratio + (height - new_mol_height) / 2;
+
+
+ // do the actual moving
+ d3Graph.svg.attr('transform',
+ 'translate(' + [x_trans, y_trans] + ')' + ' scale(' + min_ratio + ')');
+
+ // tell the zoomer what we did so that next we zoom, it uses the
+ // transformation we entered here
+ //d3Graph.zoomer.translate([x_trans, y_trans ]);
+ //d3Graph.zoomer.scale(min_ratio);
+ },
+
+ keydown:function() {
+/*
+ shiftKey = d3.event.shiftKey || d3.event.metaKey;
+ ctrlKey = d3.event.ctrlKey;
+*/
+ if(d3.event===null) return;
+
+ console.log('d3.event', d3.event);
+
+ if (d3.event.keyCode == 67) { //the 'c' key
+ this.centerview();
+ }
+
+ },
+
+ blinkNode: function(node){
+ var nodeList = this.svg.selectAll('.node');
+ var selected = nodeList.filter(function (d, _i) {
+ return d.id == node.id;
+ //return d.name != findFromParent;
+ });
+ selected.forEach(function(n){
+ for (var i = 0; i != 30; i++) {
+ $(n[1]).fadeTo('slow', 0.1).fadeTo('slow', 5.0);
+ }
+ });
+ },
+
+ blinkLink: function(link){
+ var linkList = this.svg.selectAll('.link');
+ var selected = linkList.filter(function (d, _i) {
+ return d.label == link.label;
+ //return d.id == link.id;
+ //return d.name != findFromParent;
+ });
+ selected.forEach(function(n){
+ for (var i = 0; i != 30; i++) {
+ $(n[0]).fadeTo('slow', 0.1).fadeTo('slow', 5.0);
+ }
+ });
+ },
+
+ tick:function(obj){
+ obj.link.attr('x1', function(d) { return d.source.x; })
+ .attr('y1', function(d) { return d.source.y; })
+ .attr('x2', function(d) { return d.target.x; })
+ .attr('y2', function(d) { return d.target.y; });
+
+ obj.node.attr('transform', function(d) {
+ return 'translate(' + d.x + ',' + d.y + ')';
+ });
+
+ obj.linkText
+ .attr('x', function(d) {
+ return (d.source.x + (d.target.x - d.source.x) * 0.5);
+ })
+ .attr('y', function(d) {
+ return (d.source.y + (d.target.y - d.source.y) * 0.5);
+ });
+ }
+};
+
+export { d3Graph };
diff --git a/ui/imports/lib/d3three.js b/ui/imports/lib/d3three.js
new file mode 100644
index 0000000..51493f2
--- /dev/null
+++ b/ui/imports/lib/d3three.js
@@ -0,0 +1,789 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+var chartOffset = 0;
+
+// D3.layout.force3d.js
+// (C) 2012 ziggy.jonsson.nyc@gmail.com
+// BSD license (http://opensource.org/licenses/BSD-3-Clause)
+
+d3.layout.force3d = function() {
+ var forceXY = d3.layout.force()
+ ,forceZ = d3.layout.force()
+ ,zNodes = {}
+ ,zLinks = {}
+ ,nodeID = 1
+ ,linkID = 1
+ ,tickFunction = Object
+
+ var force3d = {}
+
+ Object.keys(forceXY).forEach(function(d) {
+ force3d[d] = function() {
+ var result = forceXY[d].apply(this,arguments)
+ if (d !="nodes" && d!="links") forceZ[d].apply(this,arguments)
+ return (result == forceXY) ? force3d : result
+ }
+ })
+
+
+ force3d.on = function(name,fn) {
+ tickFunction = fn
+ return force3d
+ }
+
+
+ forceXY.on("tick",function() {
+
+ // Refresh zNodes add new, delete removed
+ var _zNodes = {}
+ forceXY.nodes().forEach(function(d,i) {
+ if (!d.id) d.id = nodeID++
+ _zNodes[d.id] = zNodes[d.id] || {x:d.z,px:d.z,py:d.z,y:d.z,id:d.id}
+ d.z = _zNodes[d.id].x
+ })
+ zNodes = _zNodes
+
+ // Refresh zLinks add new, delete removed
+ var _zLinks = {}
+ forceXY.links().forEach(function(d) {
+ var nytt = false
+ if (!d.linkID) { d.linkID = linkID++;nytt=true}
+ _zLinks[d.linkID] = zLinks[d.linkID] || {target:zNodes[d.target.id],source:zNodes[d.source.id]}
+
+ })
+ zLinks = _zLinks
+
+ // Update the nodes/links in forceZ
+ forceZ.nodes(d3.values(zNodes))
+ forceZ.links(d3.values(zLinks))
+ forceZ.start() // Need to kick forceZ so we don't lose the update mechanism
+
+ // And run the user defined function, if defined
+ tickFunction()
+ })
+
+ // Expose the sub-forces for debugging purposes
+ force3d.xy = forceXY
+ force3d.z = forceZ
+
+ return force3d
+}
+// end of d3.layout.force3d.js
+
+// Override default functions for d3
+THREE.Object3D.prototype.appendChild = function (c) {
+ this.add(c);
+ return c;
+};
+THREE.Object3D.prototype.querySelectorAll = function () { return []; };
+
+// this one is to use D3's .attr() on THREE's objects
+THREE.Object3D.prototype.setAttribute = function (name, value) {
+ var chain = name.split('.');
+ var object = this;
+ for (var i = 0; i < chain.length - 1; i++) {
+ object = object[chain[i]];
+ }
+ object[chain[chain.length - 1]] = value;
+}
+
+// d3three object
+D3THREE = function(singleton) {
+ this.labelGroup = new THREE.Object3D();
+ this.maxY = 0;
+ this.axisObjects = {};
+
+ this.running = true;
+
+ if (singleton) {
+ if (typeof(d3three) !== 'undefined') {
+ d3three.stop();
+ }
+ d3three = this;
+ }
+
+ //if (!singleton) {
+ // d3threes.push(this);
+ //}
+}
+
+D3THREE.prototype.init = function(divId) {
+ // standard THREE stuff, straight from examples
+ this.renderer = new THREE.WebGLRenderer({antialias: true, alpha : true, preserveDrawingBuffer: true});
+ this.renderer.shadowMap.enabled = true;
+ this.renderer.shadowMap.type = THREE.PCFSoftShadow;
+ this.renderer.shadowMapSoft = true;
+ this.renderer.shadowCameraNear = 1000;
+ this.renderer.shadowCameraFar = 10000;
+ this.renderer.shadowCameraFov = 50;
+ this.renderer.shadowMapBias = 0.0039;
+ this.renderer.shadowMapDarkness = 0.25;
+ this.renderer.shadowMapWidth = 10000;
+ this.renderer.shadowMapHeight = 10000;
+ this.renderer.physicallyBasedShading = true;
+
+ this.divId = divId;
+ this.width = document.getElementById(divId).offsetWidth;
+ this.height = document.getElementById(divId).offsetHeight;
+
+ this.renderer.setSize( this.width, this.height );
+
+ document.getElementById(divId).appendChild( this.renderer.domElement );
+
+ this.camera = new THREE.PerspectiveCamera( 30, this.width / this.height, 1, 100000 );
+ this.camera.position.z = -1000;
+ this.camera.position.x = -800;
+ this.camera.position.y = 600;
+
+ this.controls = new THREE.OrbitControls( this.camera, this.renderer.domElement );
+
+ this.scene = new THREE.Scene();
+
+ this.defaultLight = new THREE.AmbientLight( 0xbbbbb ); // soft white light
+ this.scene.add( this.defaultLight );
+
+ this.scene.add(this.labelGroup);
+
+ var self = this;
+ window.addEventListener( 'resize', self.onWindowResize.bind(self), false );
+}
+
+D3THREE.prototype.onWindowResize = function() {
+ var self = this;
+ self.camera.aspect = self.width / self.height;
+ self.camera.updateProjectionMatrix();
+
+ self.renderer.setSize( self.width, self.height );
+}
+
+D3THREE.prototype.animate = function() {
+ var self = this;
+ if (this.running) {
+ setTimeout( function() {
+ this.requestId = requestAnimationFrame( self.animate.bind(self) );
+ }, 1000 / 15 );
+
+ self.renderer.render( self.scene, self.camera );
+ self.controls.update();
+
+ self.labelGroup.children.forEach(function(l){
+ l.rotation.setFromRotationMatrix(self.camera.matrix, "YXZ");
+ l.rotation.x = 0;
+ l.rotation.z = 0;
+ });
+ } else {
+ window.removeEventListener( 'resize', self.onWindowResize.bind(self) );
+ while (self.scene.children.length > 0) {
+ var childObject = self.scene.children[0];
+ if (childObject.geometry) {
+ childObject.geometry.dispose();
+ }
+ if (childObject.material) {
+ childObject.material.dispose();
+ }
+ self.scene.remove(childObject);
+ delete(childObject);
+ }
+
+ self.renderer.context = null;
+ self.renderer.domElement = null;
+ self.renderer = null;
+
+ self.camera = null;
+ self.controls = null;
+ self.scene = null;
+ self.labelGroup = null;
+
+ cancelAnimationFrame(self.requestId);
+ }
+}
+
+D3THREE.prototype.stop = function() {
+ this.running = false;
+}
+
+D3THREE.prototype.render = function(element, data) {
+ element.render(data);
+}
+
+D3THREE.createAxis = function(dt) {
+ return new D3THREE.Axis(dt);
+}
+
+// d3three axis
+D3THREE.Axis = function(dt) {
+ this._scale = d3.scale.linear();
+ this._orient = "x";
+ this._tickFormat = function(d) { return d };
+ this._dt = dt;
+}
+
+D3THREE.Axis.prototype.orient = function(o) {
+ if (o) {
+ this._dt.axisObjects[o] = this;
+ this._orient = o;
+ }
+ return this;
+}
+
+D3THREE.Axis.prototype.scale = function(s) {
+ if (s) {
+ this._scale = s;
+ }
+ return this;
+}
+
+D3THREE.Axis.prototype.tickFormat = function(f) {
+ if (f) {
+ this._tickFormat = f;
+ }
+ return this;
+}
+
+D3THREE.Axis.prototype.interval = function() {
+ var interval;
+ if (typeof(this._scale.rangeBand) === 'function') {
+ // ordinal scale
+ interval = this._scale.range()[1];
+ } else {
+ interval = this._scale.range()[1] / (this._scale.ticks().length - 1);
+ }
+ return interval;
+}
+
+D3THREE.Axis.prototype.ticks = function() {
+ var ticks;
+ if (typeof(this._scale.rangeBand) === 'function') {
+ // ordinal scale
+ ticks = this._scale.domain();
+ } else {
+ ticks = this._scale.ticks();
+ }
+ return ticks;
+}
+
+D3THREE.Axis.prototype.getRotationShift = function() {
+ return this.interval() * (this.ticks().length - 1) / 2;
+}
+
+D3THREE.Axis.prototype.render = function() {
+ var material = new THREE.LineBasicMaterial({
+ color: 0xbbbbbb,
+ linewidth: 2
+ });
+
+ var tickMaterial = new THREE.LineBasicMaterial({
+ color: 0xbbbbbb,
+ linewidth: 1
+ });
+
+ var geometry = new THREE.Geometry();
+
+ interval = this.interval();
+
+ var interval = this.interval(), ticks = this.ticks();
+
+ // x,y axis shift, so rotation is from center of screen
+ var xAxisShift = this._dt.axisObjects.x.getRotationShift(),
+ yAxisShift = this._dt.axisObjects.y.getRotationShift();
+
+ for (var i = 0; i < ticks.length; i++) {
+ var tickMarGeometry = new THREE.Geometry();
+
+ var shape = new THREE.TextGeometry(this._tickFormat(ticks[i]),
+ {
+ size: 5,
+ height: 1,
+ curveSegments: 20
+ });
+ var wrapper = new THREE.MeshBasicMaterial({color: 0xbbbbbb});
+ var words = new THREE.Mesh(shape, wrapper);
+
+ if (this._orient === "y") {
+ // tick
+ geometry.vertices.push(new THREE.Vector3(i * interval - yAxisShift, chartOffset, 0 - xAxisShift));
+
+ tickMarGeometry.vertices.push(new THREE.Vector3(i * interval - yAxisShift, chartOffset, 0 - xAxisShift));
+ tickMarGeometry.vertices.push(new THREE.Vector3(i * interval - yAxisShift, -10 + chartOffset, 0 - xAxisShift));
+ var tickLine = new THREE.Line(tickMarGeometry, tickMaterial);
+ this._dt.scene.add(tickLine);
+
+ if (i * interval > this._dt.maxY) {
+ this._dt.maxY = i * interval;
+ }
+
+ words.position.set(i * interval - yAxisShift, -20 + chartOffset, 0 - xAxisShift);
+ } else if (this._orient === "z") {
+ // tick
+ geometry.vertices.push(new THREE.Vector3(0 + this._dt.maxY - yAxisShift, i * interval + chartOffset, 0 - xAxisShift));
+
+ tickMarGeometry.vertices.push(new THREE.Vector3(0 + this._dt.maxY - yAxisShift, i * interval + chartOffset, 0 - xAxisShift));
+ tickMarGeometry.vertices.push(new THREE.Vector3(10 + this._dt.maxY - yAxisShift, i * interval + chartOffset, 0 - xAxisShift));
+ var tickLine = new THREE.Line(tickMarGeometry, tickMaterial);
+ this._dt.scene.add(tickLine);
+
+ words.position.set(20 + this._dt.maxY - yAxisShift, i * interval + chartOffset, 0 - xAxisShift);
+ } else if (this._orient === "x") {
+ // tick
+ geometry.vertices.push(new THREE.Vector3(0 - yAxisShift, chartOffset, i * interval - xAxisShift));
+
+ tickMarGeometry.vertices.push(new THREE.Vector3(0 - yAxisShift, 0 + chartOffset, i * interval - xAxisShift));
+ tickMarGeometry.vertices.push(new THREE.Vector3(0 - yAxisShift, -10 + chartOffset, i * interval - xAxisShift));
+ var tickLine = new THREE.Line(tickMarGeometry, tickMaterial);
+ this._dt.scene.add(tickLine);
+
+ words.position.set(0 - yAxisShift, -20 + chartOffset, i * interval - xAxisShift);
+ }
+
+ this._dt.labelGroup.add(words);
+ }
+
+ var line = new THREE.Line(geometry, material);
+
+ this._dt.scene.add(line);
+}
+
+// Chart object
+D3THREE.Chart = function() {
+}
+
+D3THREE.Chart.prototype.config = function(c) {
+ this._config = $.extend(this._config, c);
+}
+
+D3THREE.Chart.prototype.init = function(dt) {
+ this._dt = dt;
+ // mouse move
+ var self = this;
+ this._dt.renderer.domElement.addEventListener( 'mousemove', function(e) {
+ self.onDocumentMouseMove(e);
+ }, false );
+}
+
+var cumulativeOffset = function(element) {
+ var top = 0, left = 0;
+ do {
+ top += element.offsetTop || 0;
+ left += element.offsetLeft || 0;
+ element = element.offsetParent;
+ } while(element);
+
+ return {
+ top: top,
+ left: left
+ };
+};
+
+D3THREE.Chart.prototype.detectNodeHover = function(e) {
+ var boundingRect = this._dt.renderer.domElement.getBoundingClientRect();
+
+ var vector = new THREE.Vector3();
+ vector.x = ( (e.clientX - boundingRect.left) / this._dt.renderer.domElement.width ) * 2 - 1;
+ vector.y = 1 - ( (e.clientY - boundingRect.top) / this._dt.renderer.domElement.height ) * 2;
+ vector.z = 1;
+
+ // create a check ray
+ vector.unproject( this._dt.camera );
+ var ray = new THREE.Raycaster( this._dt.camera.position,
+ vector.sub( this._dt.camera.position ).normalize() );
+
+ var intersects = ray.intersectObjects( this._nodeGroup.children );
+
+ for (var i = 0; i < this._nodeGroup.children.length; i++) {
+ this._nodeGroup.children[i].material.opacity = 1;
+ }
+
+ if (intersects.length > 0) {
+ var obj = intersects[0].object;
+ obj.material.opacity = 0.5;
+
+ var html = "";
+
+ html += "<div class=\"tooltip_kv\">";
+ html += "<span>";
+ html += "x: " + this._dt.axisObjects.x._tickFormat(obj.userData.x);
+ html += "</span><br>";
+ html += "<span>";
+ html += "y: " + this._dt.axisObjects.y._tickFormat(obj.userData.y);
+ html += "</span><br>";
+ html += "<span>";
+ html += "z: " + this._dt.axisObjects.z._tickFormat(obj.userData.z);
+ html += "</span><br>";
+ html += "</div>";
+
+ document.getElementById("tooltip-container").innerHTML = html;
+ document.getElementById("tooltip-container").style.display = "block";
+
+ document.getElementById("tooltip-container").style.top = (e.pageY + 10) + "px";
+ document.getElementById("tooltip-container").style.left = (e.pageX + 10) + "px";
+ } else {
+ document.getElementById("tooltip-container").style.display = "none";
+ }
+}
+
+// Scatter plot
+D3THREE.Scatter = function(dt) {
+ this.init(dt);
+
+ this._nodeGroup = new THREE.Object3D();
+
+ this._config = {color: 0x4682B4, pointRadius: 5};
+}
+
+D3THREE.Scatter.prototype = new D3THREE.Chart();
+
+D3THREE.Scatter.prototype.onDocumentMouseMove = function(e) {
+ // detect intersected spheres
+ this.detectNodeHover(e);
+}
+
+D3THREE.Scatter.prototype.render = function(data) {
+ var geometry = new THREE.SphereGeometry( this._config.pointRadius, 32, 32 );
+
+ this._dt.scene.add(this._nodeGroup);
+
+ // x,y axis shift, so rotation is from center of screen
+ var xAxisShift = this._dt.axisObjects.x.getRotationShift(),
+ yAxisShift = this._dt.axisObjects.y.getRotationShift();
+
+ var self = this;
+ d3.select(this._nodeGroup)
+ .selectAll()
+ .data(data)
+ .enter().append( function(d) {
+ var material = new THREE.MeshBasicMaterial( {
+ color: self._config.color } );
+ var mesh = new THREE.Mesh( geometry, material );
+ mesh.userData = {x: d.x, y: d.y, z: d.z};
+ return mesh;
+ } )
+ .attr("position.z", function(d) {
+ return self._dt.axisObjects.x._scale(d.x) - xAxisShift;
+ })
+ .attr("position.x", function(d) {
+ return self._dt.axisObjects.y._scale(d.y) - yAxisShift;
+ })
+ .attr("position.y", function(d) {
+ return self._dt.axisObjects.z._scale(d.z) + chartOffset;
+ });
+}
+
+// Surface plot
+D3THREE.Surface = function(dt) {
+ this.init(dt);
+
+ this._nodeGroup = new THREE.Object3D();
+
+ this._config = {color: 0x4682B4, pointColor: 0xff7f0e, pointRadius: 2};
+}
+
+D3THREE.Surface.prototype = new D3THREE.Chart();
+
+D3THREE.Surface.prototype.onDocumentMouseMove = function(e) {
+ // detect intersected spheres
+ var boundingRect = this._dt.renderer.domElement.getBoundingClientRect();
+
+ var vector = new THREE.Vector3();
+ vector.x = ( (e.clientX - boundingRect.left) / this._dt.renderer.domElement.width ) * 2 - 1;
+ vector.y = 1 - ( (e.clientY - boundingRect.top) / this._dt.renderer.domElement.height ) * 2;
+ vector.z = 1;
+
+ // create a check ray
+ vector.unproject( this._dt.camera );
+ var ray = new THREE.Raycaster( this._dt.camera.position,
+ vector.sub( this._dt.camera.position ).normalize() );
+
+ var meshIntersects = ray.intersectObjects( [this._meshSurface] );
+
+ if (meshIntersects.length > 0) {
+ for (var i = 0; i < this._nodeGroup.children.length; i++) {
+ this._nodeGroup.children[i].visible = true;
+ this._nodeGroup.children[i].material.opacity = 1;
+ }
+
+ this.detectNodeHover(e);
+ } else {
+ // hide nodes
+ for (var i = 0; i < this._nodeGroup.children.length; i++) {
+ this._nodeGroup.children[i].visible = false;
+ }
+ }
+}
+
+D3THREE.Surface.prototype.render = function(threeData) {
+ /* render data points */
+ var geometry = new THREE.SphereGeometry( this._config.pointRadius, 32, 32 );
+
+ this._dt.scene.add(this._nodeGroup);
+
+ // x,y axis shift, so rotation is from center of screen
+ var xAxisShift = this._dt.axisObjects.x.getRotationShift(),
+ yAxisShift = this._dt.axisObjects.y.getRotationShift();
+
+ var self = this;
+ d3.select(this._nodeGroup)
+ .selectAll()
+ .data(threeData)
+ .enter().append( function(d) {
+ var material = new THREE.MeshBasicMaterial( {
+ color: self._config.pointColor } );
+ var mesh = new THREE.Mesh( geometry, material );
+ mesh.userData = {x: d.x, y: d.y, z: d.z};
+ mesh.visible = false;
+ return mesh;
+ } )
+ .attr("position.z", function(d) {
+ return self._dt.axisObjects.x._scale(d.x) - xAxisShift;
+ })
+ .attr("position.x", function(d) {
+ return self._dt.axisObjects.y._scale(d.y) - yAxisShift;
+ })
+ .attr("position.y", function(d) {
+ return self._dt.axisObjects.z._scale(d.z) + chartOffset;
+ });
+
+ /* custom surface */
+ function distance (v1, v2)
+ {
+ var dx = v1.x - v2.x;
+ var dy = v1.y - v2.y;
+ var dz = v1.z - v2.z;
+
+ return Math.sqrt(dx*dx+dz*dz);
+ }
+
+ var vertices = [];
+ var holes = [];
+ var triangles, mesh;
+ var geometry = new THREE.Geometry();
+ var material = new THREE.MeshBasicMaterial({color: this._config.color});
+
+ for (var i = 0; i < threeData.length; i++) {
+ vertices.push(new THREE.Vector3(
+ self._dt.axisObjects.y._scale(threeData[i].y) - yAxisShift,
+ self._dt.axisObjects.z._scale(threeData[i].z) + chartOffset,
+ self._dt.axisObjects.x._scale(threeData[i].x) - xAxisShift));
+ }
+
+ geometry.vertices = vertices;
+
+ for (var i = 0; i < vertices.length; i++) {
+ // find three closest vertices to generate surface
+ var v1, v2, v3;
+ var distances = [];
+
+ // find vertices in same y or y + 1 row
+ var minY = Number.MAX_VALUE;
+ for (var j = i + 1; j < vertices.length; j++) {
+ if (i !== j && vertices[j].x > vertices[i].x) {
+ if (vertices[j].x < minY) {
+ minY = vertices[j].x;
+ }
+ }
+ }
+
+ var rowVertices = [], row2Vertices = [];
+ for (var j = i + 1; j < vertices.length; j++) {
+ if (i !== j && (vertices[j].x === vertices[i].x)) {
+ rowVertices.push({index: j, v: vertices[j]});
+ }
+ if (i !== j && (vertices[j].x === minY)) {
+ row2Vertices.push({index: j, v: vertices[j]});
+ }
+ }
+
+ if (rowVertices.length >= 1 && row2Vertices.length >= 2) {
+ // find smallest x
+ rowVertices.sort(function(a, b) {
+ if (a.v.z < b.v.z) {
+ return -1;
+ } else if (a.v.z === b.v.z) {
+ return 0;
+ } else {
+ return 1;
+ }
+ });
+
+ v1 = rowVertices[0].index;
+
+ row2Vertices.sort(function(a, b) {
+ if (a.v.z < b.v.z) {
+ return -1;
+ } else if (a.v.z === b.v.z) {
+ return 0;
+ } else {
+ return 1;
+ }
+ });
+
+ v2 = row2Vertices[0].index;
+ v3 = row2Vertices[1].index;
+
+ var fv = [i, v1, v2, v3];
+ fv = fv.sort(function(a, b) {
+ if (a < b) return -1;
+ else if (a === b) return 0;
+ else return 1;
+ });
+
+ geometry.faces.push( new THREE.Face3(fv[1], fv[0], fv[3]));
+ geometry.faces.push( new THREE.Face3(fv[0], fv[2], fv[3]));
+ }
+ }
+
+ this._meshSurface = new THREE.Mesh( geometry, material );
+ this._dt.scene.add(this._meshSurface);
+}
+
+// Bar plot
+D3THREE.Bar = function(dt) {
+ this.init(dt);
+
+ this._nodeGroup = new THREE.Object3D();
+
+ this._config = {color: 0x4682B4, barSize: 5};
+}
+
+D3THREE.Bar.prototype = new D3THREE.Chart();
+
+D3THREE.Bar.prototype.onDocumentMouseMove = function(e) {
+ this.detectNodeHover(e);
+}
+
+D3THREE.Bar.prototype.render = function(threeData) {
+ /* render data points */
+ this._dt.scene.add(this._nodeGroup);
+
+ // x,y axis shift, so rotation is from center of screen
+ var xAxisShift = this._dt.axisObjects.x.getRotationShift(),
+ yAxisShift = this._dt.axisObjects.y.getRotationShift();
+
+ var self = this;
+ d3.select(this._nodeGroup)
+ .selectAll()
+ .data(threeData)
+ .enter().append( function(d) {
+ var height = self._dt.axisObjects.z._scale(d.z) + chartOffset;
+ var geometry = new THREE.BoxGeometry( self._config.barSize, height, self._config.barSize );
+ var material = new THREE.MeshBasicMaterial( {
+ color: self._config.color } );
+ var mesh = new THREE.Mesh( geometry, material );
+ mesh.userData = {x: d.x, y: d.y, z: d.z};
+ return mesh;
+ } )
+ .attr("position.z", function(d) {
+ return self._dt.axisObjects.x._scale(d.x) - xAxisShift;
+ })
+ .attr("position.x", function(d) {
+ return self._dt.axisObjects.y._scale(d.y) - yAxisShift;
+ })
+ .attr("position.y", function(d) {
+ var height = self._dt.axisObjects.z._scale(d.z) + chartOffset;
+ return height / 2;
+ });
+}
+
+// Force layout plot
+D3THREE.Force = function(dt) {
+ this.init(dt);
+
+ this._nodeGroup = new THREE.Object3D();
+
+ this._config = {color: 0x4682B4, linkColor: 0xcccccc, linkWidth: 1};
+}
+
+D3THREE.Force.prototype = new D3THREE.Chart();
+
+D3THREE.Force.prototype.onDocumentMouseMove = function(e) {
+}
+
+D3THREE.Force.prototype.render = function(threeData) {
+ var spheres = [], three_links = [];
+ // Define the 3d force
+ var force = d3.layout.force3d()
+ .nodes(sort_data=[])
+ .links(links=[])
+ .size([50, 50])
+ .gravity(0.3)
+ .charge(-400)
+
+ var DISTANCE = 1;
+
+ for (var i = 0; i < threeData.nodes.length; i++) {
+ sort_data.push({x:threeData.nodes.x + DISTANCE,y:threeData.nodes.y + DISTANCE,z:0})
+
+ // set up the sphere vars
+ var radius = 5,
+ segments = 16,
+ rings = 16;
+
+ // create the sphere's material
+ var sphereMaterial = new THREE.MeshLambertMaterial({ color: this._config.color });
+
+ var sphere = new THREE.Mesh(
+ new THREE.SphereGeometry(
+ radius,
+ segments,
+ rings),
+ sphereMaterial);
+
+ spheres.push(sphere);
+
+ // add the sphere to the scene
+ this._dt.scene.add(sphere);
+ }
+
+ for (var i = 0; i < threeData.links.length; i++) {
+ links.push({target:sort_data[threeData.links[i].target],source:sort_data[threeData.links[i].source]});
+
+ var material = new THREE.LineBasicMaterial({ color: this._config.linkColor,
+ linewidth: this._config.linkWidth});
+ var geometry = new THREE.Geometry();
+
+ geometry.vertices.push( new THREE.Vector3( 0, 0, 0 ) );
+ geometry.vertices.push( new THREE.Vector3( 0, 0, 0 ) );
+ var line = new THREE.Line( geometry, material );
+ line.userData = { source: threeData.links[i].source,
+ target: threeData.links[i].target };
+ three_links.push(line);
+ this._dt.scene.add(line);
+
+ force.start();
+ }
+
+ // set up the axes
+ var x = d3.scale.linear().domain([0, 350]).range([0, 10]),
+ y = d3.scale.linear().domain([0, 350]).range([0, 10]),
+ z = d3.scale.linear().domain([0, 350]).range([0, 10]);
+
+ var self = this;
+ force.on("tick", function(e) {
+ for (var i = 0; i < sort_data.length; i++) {
+ spheres[i].position.set(x(sort_data[i].x) * 40 - 40, y(sort_data[i].y) * 40 - 40,z(sort_data[i].z) * 40 - 40);
+
+ for (var j = 0; j < three_links.length; j++) {
+ var line = three_links[j];
+ var vi = -1;
+ if (line.userData.source === i) {
+ vi = 0;
+ }
+ if (line.userData.target === i) {
+ vi = 1;
+ }
+
+ if (vi >= 0) {
+ line.geometry.vertices[vi].x = x(sort_data[i].x) * 40 - 40;
+ line.geometry.vertices[vi].y = y(sort_data[i].y) * 40 - 40;
+ line.geometry.vertices[vi].z = y(sort_data[i].z) * 40 - 40;
+ line.geometry.verticesNeedUpdate = true;
+ }
+ }
+ }
+ });
+}
diff --git a/ui/imports/lib/general-regex.js b/ui/imports/lib/general-regex.js
new file mode 100644
index 0000000..184a63a
--- /dev/null
+++ b/ui/imports/lib/general-regex.js
@@ -0,0 +1,15 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+export const portRegEx = /^0*(?:6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}|[1-5][0-9]{4}|[1-9][0-9]{1,3}|[0-9])$/;
+
+export const pathRegEx = /^(\/){1}([^\/\0]+(\/)?)+$/;
+
+export const hostnameRegex= new RegExp('^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$');
+
+export const ipAddressRegex = new RegExp('(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}');
diff --git a/ui/imports/lib/icon.js b/ui/imports/lib/icon.js
new file mode 100644
index 0000000..1653bc2
--- /dev/null
+++ b/ui/imports/lib/icon.js
@@ -0,0 +1,14 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+export class Icon {
+ constructor({type, name}) {
+ this.type = type;
+ this.name = name;
+ }
+}
diff --git a/ui/imports/lib/images-for-node-type.js b/ui/imports/lib/images-for-node-type.js
new file mode 100644
index 0000000..5846f46
--- /dev/null
+++ b/ui/imports/lib/images-for-node-type.js
@@ -0,0 +1,22 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+export let imagesForNodeType = {
+ 'instance': 'ic_computer_black_48dp_2x.png',
+ 'pnic': 'ic_dns_black_48dp_2x.png',
+ 'vconnector': 'ic_settings_input_composite_black_48dp_2x.png',
+ // 'network': 'ic_cloud_queue_black_48dp_2x.png',
+ 'network': 'ic_cloud_queue_black_48dp_2x.png',
+ 'vedge': 'ic_gamepad_black_48dp_2x.png',
+ 'vservice': 'ic_storage_black_48dp_2x.png',
+ 'vnic': 'ic_settings_input_hdmi_black_48dp_2x.png',
+ 'otep':'ic_keyboard_return_black_48dp_2x.png',
+};
+
+export let defaultNodeTypeImage = 'ic_lens_black_48dp_2x.png';
+
diff --git a/ui/imports/lib/regex-utils.js b/ui/imports/lib/regex-utils.js
new file mode 100644
index 0000000..fd9bce2
--- /dev/null
+++ b/ui/imports/lib/regex-utils.js
@@ -0,0 +1,11 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+export function regexEscape(s) {
+ return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
+}
diff --git a/ui/imports/lib/simple-schema-utils.js b/ui/imports/lib/simple-schema-utils.js
new file mode 100644
index 0000000..3f2840b
--- /dev/null
+++ b/ui/imports/lib/simple-schema-utils.js
@@ -0,0 +1,15 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { SimpleSchema } from 'meteor/aldeed:simple-schema';
+
+export let _idFieldDef = {
+ type: {
+ _str: { type: String, regEx: SimpleSchema.RegEx.Id }
+ }
+};
diff --git a/ui/imports/lib/utilities.js b/ui/imports/lib/utilities.js
new file mode 100644
index 0000000..e1143a3
--- /dev/null
+++ b/ui/imports/lib/utilities.js
@@ -0,0 +1,54 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import * as R from 'ramda';
+
+export function idToStr(orgId) {
+ return R.ifElse(R.is(Mongo.ObjectID),
+ function (id) { return id.toHexString() + ':' + 'objectid'; },
+ R.identity
+ )(orgId);
+}
+
+export function parseReqId(pId) {
+ let idMatch = R.match(/(.*):objectid$/, pId);
+ if (idMatch.length === 0) {
+ return {
+ type: 'string',
+ id: pId
+ };
+ } else {
+ return {
+ type: 'objectid',
+ id: new Mongo.ObjectID(idMatch[1])
+ };
+ }
+}
+
+function calcColor(level) {
+ let r = 11;
+ let g = 122;
+ let b = 209;
+ //let a = 1;
+ let factor = level / 15;
+ factor = factor < 0 ? 0 : 1 - factor;
+
+ let nR = Math.floor(r * factor);
+ let nG = Math.floor(g * factor);
+ let nB = Math.floor(b * factor);
+ //let nA = a;
+ let colorStr = R.reduce((acc, colorPart) => {
+ let digits = colorPart.toString(16);
+ if (colorPart < 16) { digits = '0' + digits; }
+ return acc + digits;
+ }, '#', [nR, nG, nB]);
+
+ return colorStr;
+}
+
+export let calcColorMem = R.memoize(calcColor);
diff --git a/ui/imports/startup/both/config.js b/ui/imports/startup/both/config.js
new file mode 100644
index 0000000..ac7c9a2
--- /dev/null
+++ b/ui/imports/startup/both/config.js
@@ -0,0 +1,9 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import './configs/accounts';
diff --git a/ui/imports/startup/both/configs/accounts.js b/ui/imports/startup/both/configs/accounts.js
new file mode 100644
index 0000000..2653abe
--- /dev/null
+++ b/ui/imports/startup/both/configs/accounts.js
@@ -0,0 +1,9 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+
diff --git a/ui/imports/startup/both/index.js b/ui/imports/startup/both/index.js
new file mode 100644
index 0000000..62d0ef5
--- /dev/null
+++ b/ui/imports/startup/both/index.js
@@ -0,0 +1,10 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import '/imports/api/simple-schema.init';
+import './config';
diff --git a/ui/imports/startup/client/index.js b/ui/imports/startup/client/index.js
new file mode 100644
index 0000000..93da904
--- /dev/null
+++ b/ui/imports/startup/client/index.js
@@ -0,0 +1,38 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import './template-helpers.js';
+
+import '/imports/ui/store/store';
+import '/imports/ui/components/landing/landing';
+import '/imports/ui/components/main/main';
+import '/imports/ui/components/loading/loading';
+import '/imports/ui/components/top-navbar-menu/top-navbar-menu';
+import '/imports/ui/components/environment/environment';
+import '/imports/ui/components/environment-wizard/environment-wizard';
+import '/imports/ui/components/scanning-request/scanning-request';
+import '/imports/ui/components/scheduled-scan/scheduled-scan';
+import '/imports/ui/components/project-dashboard/project-dashboard';
+import '/imports/ui/components/region-dashboard/region-dashboard';
+import '/imports/ui/components/zone-dashboard/zone-dashboard';
+import '/imports/ui/components/host-dashboard/host-dashboard';
+import '/imports/ui/components/aggregate-dashboard/aggregate-dashboard';
+import '/imports/ui/components/scans-list/scans-list';
+import '/imports/ui/components/scheduled-scans-list/scheduled-scans-list';
+import '/imports/ui/components/link-types-list/link-types-list';
+import '/imports/ui/components/link-type/link-type';
+import '/imports/ui/components/clique-types-list/clique-types-list';
+import '/imports/ui/components/clique-type/clique-type';
+import '/imports/ui/components/clique-constraints-list/clique-constraints-list';
+import '/imports/ui/components/clique-constraint/clique-constraint';
+import '/imports/ui/components/user-list/user-list';
+import '/imports/ui/components/user/user';
+import '/imports/ui/components/messages-list/messages-list';
+import '/imports/ui/components/message/message';
+import '/imports/ui/components/dashboard/dashboard';
+import '/imports/ui/components/new-scanning/new-scanning';
diff --git a/ui/imports/startup/client/template-helpers.js b/ui/imports/startup/client/template-helpers.js
new file mode 100644
index 0000000..89023b6
--- /dev/null
+++ b/ui/imports/startup/client/template-helpers.js
@@ -0,0 +1,32 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import * as R from 'ramda';
+import * as utils from '/imports/lib/utilities';
+import { Counter } from 'meteor/natestrauser:publish-performant-counts';
+
+Template.registerHelper('asHash', function (params) {
+ return params.hash;
+});
+
+Template.registerHelper('idToStr', utils.idToStr);
+
+Template.registerHelper('rPath', function (source, pathStr) {
+ let path = R.split('.', pathStr);
+ return R.path(path, source);
+});
+
+Template.registerHelper('asArray', function (val) {
+ return [val];
+});
+
+Template.registerHelper('countOf', function (name) {
+ if (name) {
+ return Counter.get(name);
+ }
+});
diff --git a/ui/imports/startup/server/config.js b/ui/imports/startup/server/config.js
new file mode 100644
index 0000000..ac7c9a2
--- /dev/null
+++ b/ui/imports/startup/server/config.js
@@ -0,0 +1,9 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import './configs/accounts';
diff --git a/ui/imports/startup/server/configs/accounts.js b/ui/imports/startup/server/configs/accounts.js
new file mode 100644
index 0000000..f098233
--- /dev/null
+++ b/ui/imports/startup/server/configs/accounts.js
@@ -0,0 +1,16 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+Accounts.validateNewUser((_user) => {
+ let loggedInUser = Meteor.user();
+ if (Roles.userIsInRole(loggedInUser, 'manage-users', Roles.GLOBAL_GROUP)) {
+ return true;
+ }
+
+ throw new Meteor.Error(403, 'NotAuthorized to create new users');
+});
diff --git a/ui/imports/startup/server/index.js b/ui/imports/startup/server/index.js
new file mode 100644
index 0000000..ee22e45
--- /dev/null
+++ b/ui/imports/startup/server/index.js
@@ -0,0 +1,13 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+// This defines all the collections, publications and methods that the application provides
+// as an API to the client.
+import './register-api.js';
+import './seeds.js';
+import './config.js';
diff --git a/ui/imports/startup/server/register-api.js b/ui/imports/startup/server/register-api.js
new file mode 100644
index 0000000..3475c53
--- /dev/null
+++ b/ui/imports/startup/server/register-api.js
@@ -0,0 +1,56 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import '../../api/constants/server/publications';
+
+import '../../api/environments/server/publications.js';
+import '../../api/environments/methods.js';
+
+import '../../api/inventories/server/publications.js';
+import '../../api/inventories/server/methods.js';
+
+import '../../api/scans/server/publications.js';
+import '../../api/scans/methods.js';
+import '../../api/scans/server/methods.js';
+
+import '../../api/scheduled-scans/server/publications.js';
+import '../../api/scheduled-scans/methods.js';
+import '../../api/scheduled-scans/server/methods.js';
+
+import '../../api/messages/server/publications';
+import '../../api/messages/server/methods';
+import '../../api/messages/methods.js';
+
+import '../../api/cliques/server/publications';
+import '../../api/cliques/methods.js';
+
+import '../../api/links/server/publications';
+import '../../api/links/methods.js';
+
+import '../../api/statistics/server/publications';
+import '../../api/statistics/methods.js';
+
+import '../../api/attributes_for_hover_on_data/server/publications';
+import '../../api/attributes_for_hover_on_data/methods.js';
+
+import '../../api/link-types/server/publications';
+import '../../api/link-types/methods.js';
+
+import '../../api/clique-types/server/publications';
+import '../../api/clique-types/methods.js';
+
+import '../../api/clique-constraints/server/publications';
+import '../../api/clique-constraints/methods.js';
+
+import '../../api/accounts/server/publications';
+import '../../api/accounts/methods';
+
+import '../../api/supported_environments/server/publications';
+import '../../api/supported_environments/methods';
+
+import '../../api/migrations/migrations';
diff --git a/ui/imports/startup/server/seeds.js b/ui/imports/startup/server/seeds.js
new file mode 100644
index 0000000..a6132bf
--- /dev/null
+++ b/ui/imports/startup/server/seeds.js
@@ -0,0 +1,10 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+//import './seeds/constants'; //disabled as of US2758.
+import './seeds/users';
diff --git a/ui/imports/startup/server/seeds/constants.js b/ui/imports/startup/server/seeds/constants.js
new file mode 100644
index 0000000..2d59d99
--- /dev/null
+++ b/ui/imports/startup/server/seeds/constants.js
@@ -0,0 +1,68 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import { Constants } from '/imports/api/constants/constants';
+import * as R from 'ramda';
+import { Distributions } from '/imports/api/constants/data/distributions';
+//import { NetworkPlugins } from './data/network-plugins';
+import { LogLevels } from '/imports/api/constants/data/log-levels';
+import { MechanismDrivers } from '/imports/api/constants/data/mechanism-drivers';
+import { ObjectTypesForLinks } from '/imports/api/constants/data/object-types-for-links';
+import { TypeDrivers } from '/imports/api/constants/data/type-drivers';
+import { EnvTypes } from '/imports/api/constants/data/env-types';
+import { Statuses as ScansStatuses } from '/imports/api/constants/data/scans-statuses';
+import { EnvironmentMonitoringTypes } from '/imports/api/constants/data/environment-monitoring-types';
+import { EnvProvisionTypes } from '/imports/api/constants/data/environment-provision-types';
+import { MessageSourceSystems } from '/imports/api/constants/data/message-source-systems';
+
+let constantsDefaults = [{
+ name: 'env_types',
+ values: EnvTypes
+}, {
+ name: 'scans_statuses',
+ values: ScansStatuses
+}, {
+ name: 'environment_monitoring_types',
+ values: EnvironmentMonitoringTypes
+}, {
+ name: 'distributions',
+ values: Distributions
+}, {
+ name: 'log_levels',
+ values: LogLevels
+}, {
+ name: 'mechanism_drivers',
+ values: MechanismDrivers
+}, {
+ name: 'object_types_for_links',
+ values: ObjectTypesForLinks
+}, {
+ name: 'type_drivers',
+ values: TypeDrivers
+}, {
+ name: 'environment_provision_types',
+ values: EnvProvisionTypes
+}, {
+ name: 'message_source_systems',
+ values: MessageSourceSystems
+}];
+
+if (Meteor.server) {
+ R.forEach((def) => {
+ insertConstants(Constants, def.name, def.values);
+ }, constantsDefaults);
+}
+
+function insertConstants(collection, name, values) {
+ if (collection.find({ name: name}).count() === 0) {
+ Constants.insert({
+ name: name,
+ data: values
+ });
+ }
+}
diff --git a/ui/imports/startup/server/seeds/users.js b/ui/imports/startup/server/seeds/users.js
new file mode 100644
index 0000000..34c20c6
--- /dev/null
+++ b/ui/imports/startup/server/seeds/users.js
@@ -0,0 +1,51 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import * as R from 'ramda';
+import { Roles } from 'meteor/alanning:roles';
+
+let users = [
+ {
+ username: 'admin',
+ name: 'admin',
+ email: 'admin@example.com',
+ password: '123456',
+ roles: [
+ { role: 'manage-users', group: Roles.GLOBAL_GROUP },
+ { role: 'manage-link-types', group: Roles.GLOBAL_GROUP },
+ { role: 'manage-clique-types', group: Roles.GLOBAL_GROUP },
+ { role: 'manage-clique-constraints', group: Roles.GLOBAL_GROUP },
+ { role: 'view-env', group: Roles.GLOBAL_GROUP },
+ { role: 'edit-env', group: Roles.GLOBAL_GROUP },
+ ]
+ }
+];
+
+R.forEach((user) => {
+ let id;
+ let userDb = Meteor.users.findOne({ username: user.username });
+ if (R.isNil(userDb)) {
+ console.log('creating user', user);
+ id = Accounts.createUser({
+ username: user.username,
+ email: user.email,
+ password: user.password,
+ profile: { name: user.name }
+ });
+ } else {
+ id = userDb._id;
+ }
+
+ if (user.roles.length > 0) {
+ console.log('adding roles to user', user, user.roles);
+
+ R.forEach((roleItem) => {
+ Roles.addUsersToRoles(id, roleItem.role, roleItem.group);
+ }, user.roles);
+ }
+}, users);
diff --git a/ui/imports/ui/actions/environment-panel.actions.js b/ui/imports/ui/actions/environment-panel.actions.js
new file mode 100644
index 0000000..fb7350c
--- /dev/null
+++ b/ui/imports/ui/actions/environment-panel.actions.js
@@ -0,0 +1,225 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+//import * as R from 'ramda';
+
+export const SET_ENV_NAME = 'SET_ENV_NAME';
+export const UPDATE_ENV_TREE_NODE = 'UPDATE_ENV_TREE_NODE';
+export const ADD_UPDATE_CHILDREN_ENV_TREE_NODE = 'ADD_UPDATE_CHILDREN_ENV_TREE_NODE';
+export const RESET_ENV_TREE_NODE_CHILDREN = 'RESET_ENV_TREE_NODE_CHILDREN';
+export const START_OPEN_ENV_TREE_NODE = 'START_OPEN_ENV_TREE_NODE';
+export const END_OPEN_ENV_TREE_NODE = 'END_OPEN_ENV_TREE_NODE';
+export const START_CLOSE_ENV_TREE_NODE = 'START_CLOSE_ENV_TREE_NODE';
+export const END_CLOSE_ENV_TREE_NODE = 'END_CLOSE_ENV_TREE_NODE';
+export const SET_ENV_CHILD_DETECTED_TREE_NODE = 'SET_ENV_CHILD_DETECTED_TREE_NODE';
+export const SET_ENV_SELECTED_NODE = 'SET_ENV_SELECTED_NODE';
+export const SET_ENV_ENV_ID = 'SET_ENV_ENV_ID';
+export const SET_ENV_SELECTED_NODE_INFO = 'SET_ENV_SELECTED_NODE_INFO';
+export const SET_ENV_AS_LOADED = 'SET_ENV_AS_LOADED';
+export const SET_ENV_AS_NOT_LOADED = 'SET_ENV_AS_NOT_LOADED';
+export const SET_ENV_SELECTED_NODE_AS_ENV = 'SET_ENV_SELECTED_NODE_AS_ENV';
+export const SET_SHOW_DASHBOARD = 'SET_SHOW_DASHBOARD';
+export const SET_SHOW_GRAPH = 'SET_SHOW_GRAPH';
+export const TOGGLE_ENV_SHOW = 'TOGGLE_ENV_SHOW';
+export const SET_ENV_POSITION_REPORT_IS_NEEDED_AS_ON = 'SET_ENV_POSITION_REPORT_IS_NEEDED_AS_ON';
+export const REPORT_ENV_NODE_POSITION_RETRIEVED = 'REPORT_ENV_NODE_POSITION_RETRIEVED';
+export const SET_ENV_SCROLL_TO_NODE_IS_NEEDED_AS_ON = 'SET_ENV_SCROLL_TO_NODE_IS_NEEDED_AS_ON';
+export const REPORT_ENV_SCROLL_TO_NODE_PERFORMED = 'REPORT_ENV_SCROLL_TO_NODE_PERFORMED';
+export const RESET_ENV_NEED_CHILD_DETECTION = 'RESET_ENV_NEED_CHILD_DETECTION';
+
+export function setEnvName(envName) {
+ return {
+ type: SET_ENV_NAME,
+ payload: {
+ envName: envName
+ }
+ };
+}
+
+export function updateEnvTreeNode(nodeInfo) {
+ return {
+ type: UPDATE_ENV_TREE_NODE,
+ payload: {
+ nodeInfo: nodeInfo
+ }
+ };
+}
+
+export function addUpdateChildrenEnvTreeNode(nodePath, childrenInfo) {
+ return {
+ type: ADD_UPDATE_CHILDREN_ENV_TREE_NODE,
+ payload: {
+ nodePath: nodePath,
+ childrenInfo: childrenInfo
+ },
+ };
+}
+
+export function resetEnvTreeNodeChildren(nodePath) {
+ return {
+ type: RESET_ENV_TREE_NODE_CHILDREN,
+ payload: {
+ nodePath: nodePath,
+ }
+ };
+}
+
+export function startOpenEnvTreeNode(nodePath) {
+ return {
+ type: START_OPEN_ENV_TREE_NODE,
+ payload: {
+ nodePath: nodePath,
+ }
+ };
+}
+
+export function endOpenEnvTreeNode(nodePath) {
+ return {
+ type: END_OPEN_ENV_TREE_NODE,
+ payload: {
+ nodePath: nodePath,
+ }
+ };
+}
+
+export function startCloseEnvTreeNode(nodePath) {
+ return {
+ type: START_CLOSE_ENV_TREE_NODE,
+ payload: {
+ nodePath: nodePath,
+ }
+ };
+}
+
+export function endCloseEnvTreeNode(nodePath) {
+ return {
+ type: END_CLOSE_ENV_TREE_NODE,
+ payload: {
+ nodePath: nodePath,
+ }
+ };
+}
+
+export function setEnvChildDetectedTreeNode(nodePath) {
+ return {
+ type: SET_ENV_CHILD_DETECTED_TREE_NODE,
+ payload: {
+ nodePath: nodePath
+ }
+ };
+}
+
+export function setEnvSelectedNode(nodeId, nodeType) {
+ return {
+ type: SET_ENV_SELECTED_NODE,
+ payload: {
+ nodeId: nodeId,
+ nodeType: nodeType
+ }
+ };
+}
+
+export function setEnvSelectedNodeAsEnv() {
+ return {
+ type: SET_ENV_SELECTED_NODE_AS_ENV,
+ };
+}
+
+export function setEnvEnvId(_id) {
+ return {
+ type: SET_ENV_ENV_ID,
+ payload: {
+ _id: _id
+ }
+ };
+}
+
+export function setEnvSelectedNodeInfo(nodeInfo) {
+ return {
+ type: SET_ENV_SELECTED_NODE_INFO,
+ payload: {
+ nodeInfo: nodeInfo
+ }
+ };
+}
+
+export function setEnvAsLoaded() {
+ return {
+ type: SET_ENV_AS_LOADED,
+ };
+}
+
+export function setEnvAsNotLoaded() {
+ return {
+ type: SET_ENV_AS_NOT_LOADED
+ };
+}
+
+export function setShowDashboard() {
+ return {
+ type: SET_SHOW_DASHBOARD
+ };
+}
+
+export function setShowGraph() {
+ return {
+ type: SET_SHOW_GRAPH
+ };
+}
+
+export function toggleEnvShow() {
+ return {
+ type: TOGGLE_ENV_SHOW
+ };
+}
+
+export function setEnvPositionReportIsNeededAsOn(nodePath) {
+ return {
+ type: SET_ENV_POSITION_REPORT_IS_NEEDED_AS_ON,
+ payload: {
+ nodePath: nodePath
+ }
+ };
+}
+
+export function reportEnvNodePositionRetrieved(nodePath, rect) {
+ return {
+ type: REPORT_ENV_NODE_POSITION_RETRIEVED,
+ payload: {
+ nodePath: nodePath,
+ rect: rect
+ }
+ };
+}
+
+export function setEnvScrollToNodeIsNeededAsOn(nodePath) {
+ return {
+ type: SET_ENV_SCROLL_TO_NODE_IS_NEEDED_AS_ON,
+ payload: {
+ nodePath: nodePath
+ }
+ };
+}
+
+export function reportEnvScrollToNodePerformed(nodePath) {
+ return {
+ type: REPORT_ENV_SCROLL_TO_NODE_PERFORMED,
+ payload: {
+ nodePath: nodePath
+ }
+ };
+}
+
+export function resetEnvNeedChildDetection(nodePath) {
+ return {
+ type: RESET_ENV_NEED_CHILD_DETECTION,
+ payload: {
+ nodePath: nodePath
+ }
+ };
+}
diff --git a/ui/imports/ui/actions/graph-tooltip-window.actions.js b/ui/imports/ui/actions/graph-tooltip-window.actions.js
new file mode 100644
index 0000000..08c48b6
--- /dev/null
+++ b/ui/imports/ui/actions/graph-tooltip-window.actions.js
@@ -0,0 +1,30 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+//import * as R from 'ramda';
+
+export const ACTIVATE_GRAPH_TOOLTIP_WINDOW = 'ACTIVATE_GRAPH_TOOLTIP_WINDOW';
+export const CLOSE_GRAPH_TOOLTIP_WINDOW = 'CLOSE_GRAPH_TOOLTIP_WINDOW';
+
+export function activateGraphTooltipWindow(label, attributes, left, top) {
+ return {
+ type: ACTIVATE_GRAPH_TOOLTIP_WINDOW,
+ payload: {
+ label: label,
+ attributes: attributes,
+ left: left,
+ top: top
+ }
+ };
+}
+
+export function closeGraphTooltipWindow() {
+ return {
+ type: CLOSE_GRAPH_TOOLTIP_WINDOW
+ };
+}
diff --git a/ui/imports/ui/actions/main-app.actions.js b/ui/imports/ui/actions/main-app.actions.js
new file mode 100644
index 0000000..1daafa1
--- /dev/null
+++ b/ui/imports/ui/actions/main-app.actions.js
@@ -0,0 +1,21 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+//import * as R from 'ramda';
+
+export const SET_MAIN_APP_SELECTED_ENVIRONMENT = 'SET_MAIN_APP_SELECTED_ENVIRONMENT';
+
+export function setMainAppSelectedEnvironment(_id, name) {
+ return {
+ type: SET_MAIN_APP_SELECTED_ENVIRONMENT,
+ payload: {
+ _id: _id,
+ name: name
+ }
+ };
+}
diff --git a/ui/imports/ui/actions/navigation.js b/ui/imports/ui/actions/navigation.js
new file mode 100644
index 0000000..06b1501
--- /dev/null
+++ b/ui/imports/ui/actions/navigation.js
@@ -0,0 +1,84 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+import * as R from 'ramda';
+
+const SET_CURRENT_NODE = 'SET_CURRENT_NODE';
+const SET_CURRENT_NODE_FROM_TREE_CONTROL = 'SET_CURRENT_NODE_FROM_TREE_CONTROL';
+
+function setCurrentNode(item) {
+ let nodeChain = convertToNodeChain(item.id_path, item.name_path);
+ R.last(nodeChain).item = item;
+
+ return {
+ type: SET_CURRENT_NODE,
+ payload: {
+ nodeChain: nodeChain
+ }
+ };
+}
+
+function setCurrentNodeFromTreeControl (item) {
+ let nodeChain = convertToNodeChain(item.id_path, item.name_path);
+ R.last(nodeChain).item = item;
+
+ return {
+ type: SET_CURRENT_NODE_FROM_TREE_CONTROL,
+ payload: {
+ nodeChain: nodeChain
+ }
+ };
+}
+
+function convertToNodeChain(idPath, namePath) {
+ let convert = R.pipe(R.split(), R.slice(1, Infinity));
+ let paths = convert('/', idPath);
+ let names = convert('/', namePath);
+ let nodesData = R.zip(paths, names);
+ let nodeChain = R.map((nodeData) => {
+ return {
+ id: nodeData[0],
+ name: nodeData[1]
+ };
+ }, nodesData);
+
+ let parent = null;
+
+ for (let i = 0; i < nodeChain.length; i++) {
+ let node = nodeChain[i];
+ node.parent = parent;
+ node.fullIdPath = calcFullIdPath(node);
+ node.fullNamePath = calcFullNamePath(node);
+ parent = node;
+ }
+
+ return nodeChain;
+}
+
+function calcFullIdPath (node) {
+ if (R.isNil(node)) { return null; }
+ if (R.isNil(node.parent)) { return '/' + node.id; }
+
+ let parentFullPath = calcFullIdPath(node.parent);
+ return parentFullPath + '/' + node.id;
+}
+
+function calcFullNamePath (node) {
+ if (R.isNil(node)) { return null; }
+ if (R.isNil(node.parent)) { return '/' + node.name; }
+
+ let parentFullPath = calcFullNamePath(node.parent);
+ return parentFullPath + '/' + node.name;
+}
+
+export {
+ SET_CURRENT_NODE,
+ SET_CURRENT_NODE_FROM_TREE_CONTROL,
+ setCurrentNode,
+ setCurrentNodeFromTreeControl
+};
diff --git a/ui/imports/ui/actions/search-interested-parties.js b/ui/imports/ui/actions/search-interested-parties.js
new file mode 100644
index 0000000..1eb9b78
--- /dev/null
+++ b/ui/imports/ui/actions/search-interested-parties.js
@@ -0,0 +1,93 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+//import * as R from 'ramda';
+
+const ADD_SEARCH_INTERESTED_PARTY = 'ADD_SEARCH_INTERESTED_PARTY';
+const REMOVE_SEARCH_INTERESTED_PARTY = 'REMOVE_SEARCH_INTERESTED_PARTY';
+const SET_SEARCH_TERM = 'SET_SEARCH_TERM';
+const SET_SEARCH_AUTO_COMPLETE_TERM = 'SET_SEARCH_AUTO_COMPLETE_TERM';
+const RESET_SEARCH_AUTO_COMPLETE_FUTURE = 'RESET_SEARCH_AUTO_COMPLETE_FUTURE';
+const SET_SEARCH_AUTO_COMPLETE_FUTURE = 'SET_SEARCH_AUTO_COMPLETE_FUTURE';
+
+const AUTO_COMPLETE_DELAY = 300; // miliseconds.
+
+function addSearchInterestedParty(listener) {
+ return {
+ type: ADD_SEARCH_INTERESTED_PARTY,
+ payload: {
+ listener: listener
+ }
+ };
+}
+
+function removeSearchInterestedParty(listener) {
+ return {
+ type: REMOVE_SEARCH_INTERESTED_PARTY,
+ payload: {
+ listener: listener
+ }
+ };
+}
+
+function setSearchTerm(searchTerm) {
+ return {
+ type: SET_SEARCH_TERM,
+ payload: {
+ searchTerm: searchTerm
+ }
+ };
+}
+
+function setSearchAutoCompleteTerm(searchTerm) {
+ return {
+ type: SET_SEARCH_AUTO_COMPLETE_TERM,
+ payload: {
+ searchTerm: searchTerm
+ }
+ };
+}
+
+function resetSearchAutoCompleteFuture() {
+ return {
+ type: RESET_SEARCH_AUTO_COMPLETE_FUTURE,
+ };
+}
+
+function setSearchAutoCompleteFuture(futureId) {
+ return {
+ type: SET_SEARCH_AUTO_COMPLETE_FUTURE,
+ payload: {
+ futureId: futureId
+ }
+ };
+}
+
+function notifySearchAutoCompleteTermChanged(searchTerm) {
+ return (dispatch) => {
+ let autoCompleteFutureId = setTimeout(() => {
+ dispatch(resetSearchAutoCompleteFuture());
+ dispatch(setSearchAutoCompleteTerm(searchTerm));
+ }, AUTO_COMPLETE_DELAY);
+ dispatch(setSearchAutoCompleteFuture(autoCompleteFutureId));
+ };
+}
+
+export {
+ ADD_SEARCH_INTERESTED_PARTY,
+ REMOVE_SEARCH_INTERESTED_PARTY,
+ SET_SEARCH_TERM,
+ SET_SEARCH_AUTO_COMPLETE_TERM,
+ RESET_SEARCH_AUTO_COMPLETE_FUTURE,
+ SET_SEARCH_AUTO_COMPLETE_FUTURE,
+ addSearchInterestedParty,
+ removeSearchInterestedParty,
+ setSearchTerm,
+ setSearchAutoCompleteTerm,
+ notifySearchAutoCompleteTermChanged
+};
diff --git a/ui/imports/ui/actions/tree-node.actions.js b/ui/imports/ui/actions/tree-node.actions.js
new file mode 100644
index 0000000..0fad8c9
--- /dev/null
+++ b/ui/imports/ui/actions/tree-node.actions.js
@@ -0,0 +1,144 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+//import * as R from 'ramda';
+
+export const UPDATE_TREE_NODE_INFO = 'UPDATE_TREE_NODE_INFO';
+export const ADD_UPDATE_CHILDREN_TREE_NODE = 'ADD_UPDATE_CHILDREN_TREE_NODE';
+export const RESET_TREE_NODE_CHILDREN = 'RESET_TREE_NODE_CHILDREN';
+export const START_OPEN_TREE_NODE = 'START_OPEN_TREE_NODE';
+export const END_OPEN_TREE_NODE = 'END_OPEN_TREE_NODE';
+export const START_CLOSE_TREE_NODE = 'START_CLOSE_TREE_NODE';
+export const END_CLOSE_TREE_NODE = 'END_CLOSE_TREE_NODE';
+export const SET_CHILD_DETECTED_TREE_NODE = 'SET_CHILD_DETECTED_TREE_NODE';
+export const SET_POSITION_REPORT_IS_NEEDED_AS_ON = 'SET_POSITION_REPORT_IS_NEEDED_AS_ON';
+export const REPORT_NODE_POSITION_RETRIEVED = 'REPORT_NODE_POSITION_RETRIEVED';
+export const SET_SCROLL_TO_NODE_IS_NEEDED_AS_ON = 'SET_SCROLL_TO_NODE_IS_NEEDED_AS_ON';
+export const REPORT_SCROLL_TO_NODE_PERFORMED = 'REPORT_SCROLL_TO_NODE_PERFORMED';
+export const RESET_NEED_CHILD_DETECTION = 'RESET_NEED_CHILD_DETECTION';
+
+export function updateTreeNodeInfo(nodeInfo, level) {
+ return {
+ type: UPDATE_TREE_NODE_INFO,
+ payload: {
+ nodeInfo: nodeInfo,
+ level: level
+ }
+ };
+}
+
+export function addUpdateChildrenTreeNode(nodePath, childrenInfo, level) {
+ return {
+ type: ADD_UPDATE_CHILDREN_TREE_NODE,
+ payload: {
+ nodePath: nodePath,
+ childrenInfo: childrenInfo,
+ level: level
+ },
+ };
+}
+
+export function resetTreeNodeChildren(nodePath) {
+ return {
+ type: RESET_TREE_NODE_CHILDREN,
+ payload: {
+ nodePath: nodePath,
+ }
+ };
+}
+
+export function startOpenTreeNode(nodePath) {
+ return {
+ type: START_OPEN_TREE_NODE,
+ payload: {
+ nodePath: nodePath,
+ }
+ };
+}
+
+export function endOpenTreeNode(nodePath) {
+ return {
+ type: END_OPEN_TREE_NODE,
+ payload: {
+ nodePath: nodePath,
+ }
+ };
+}
+
+export function startCloseTreeNode(nodePath) {
+ return {
+ type: START_CLOSE_TREE_NODE,
+ payload: {
+ nodePath: nodePath,
+ }
+ };
+}
+
+export function endCloseTreeNode(nodePath) {
+ return {
+ type: END_CLOSE_TREE_NODE,
+ payload: {
+ nodePath: nodePath,
+ }
+ };
+}
+
+export function setChildDetectedTreeNode(nodePath) {
+ return {
+ type: SET_CHILD_DETECTED_TREE_NODE,
+ payload: {
+ nodePath: nodePath
+ }
+ };
+}
+
+export function setPositionReportIsNeededAsOn(nodePath) {
+ return {
+ type: SET_POSITION_REPORT_IS_NEEDED_AS_ON,
+ payload: {
+ nodePath: nodePath
+ }
+ };
+}
+
+export function reportNodePositionRetrieved(nodePath, rect) {
+ return {
+ type: REPORT_NODE_POSITION_RETRIEVED,
+ payload: {
+ nodePath: nodePath,
+ rect: rect
+ }
+ };
+}
+
+export function setScrollToNodeIsNeededAsOn(nodePath) {
+ return {
+ type: SET_SCROLL_TO_NODE_IS_NEEDED_AS_ON,
+ payload: {
+ nodePath: nodePath
+ }
+ };
+}
+
+export function reportScrollToNodePerformed(nodePath) {
+ return {
+ type: REPORT_SCROLL_TO_NODE_PERFORMED,
+ payload: {
+ nodePath: nodePath
+ }
+ };
+}
+
+export function resetNeedChildDetection(nodePath) {
+ return {
+ type: RESET_NEED_CHILD_DETECTION,
+ payload: {
+ nodePath: nodePath
+ }
+ };
+}
diff --git a/ui/imports/ui/actions/vedge-info-window.actions.js b/ui/imports/ui/actions/vedge-info-window.actions.js
new file mode 100644
index 0000000..0431648
--- /dev/null
+++ b/ui/imports/ui/actions/vedge-info-window.actions.js
@@ -0,0 +1,41 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+//import * as R from 'ramda';
+
+export const ACTIVATE_VEDGE_INFO_WINDOW = 'ACTIVATE_VEDGE_INFO_WINDOW';
+export const CLOSE_VEDGE_INFO_WINDOW = 'CLOSE_VEDGE_INFO_WINDOW';
+
+export function activateVedgeInfoWindow(node, left, top) {
+ // todo: remove. this is for debug
+ /*
+ node = {
+ _id: '0',
+ id: 'devstack-vpp1-VPP',
+ id_path: '',
+ name: 'devstack-vpp1-VPP',
+ name_path: '',
+ environment: 'Devstack-VPP'
+ };
+ */
+
+ return {
+ type: ACTIVATE_VEDGE_INFO_WINDOW,
+ payload: {
+ node: node,
+ left: left,
+ top: top
+ }
+ };
+}
+
+export function closeVedgeInfoWindow() {
+ return {
+ type: CLOSE_VEDGE_INFO_WINDOW
+ };
+}
diff --git a/ui/imports/ui/components/accordion-nav-menu/accordion-nav-menu.html b/ui/imports/ui/components/accordion-nav-menu/accordion-nav-menu.html
new file mode 100644
index 0000000..d4cf5de
--- /dev/null
+++ b/ui/imports/ui/components/accordion-nav-menu/accordion-nav-menu.html
@@ -0,0 +1,43 @@
+<!--
+########################################################################################
+# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others #
+# #
+# All rights reserved. This program and the accompanying materials #
+# are made available under the terms of the Apache License, Version 2.0 #
+# which accompanies this distribution, and is available at #
+# http://www.apache.org/licenses/LICENSE-2.0 #
+########################################################################################
+ -->
+<template name="accordionNavMenu">
+ <nav class="os-accordion-nav-menu">
+ <div id="left-nav-menu" class="left-nav-menu">
+ <div class="menu-header">
+ <p>
+ {{ envName }}
+ </p>
+ <a href="#"><i class="material-icons">menu</i></a>
+ </div>
+ <ul class="sm-menu-items-list">
+ <li>
+ <a class="sm-toggle-graph-button toggleGraph" href="#">
+ <span><i class="material-icons">share</i>Toggle Graph</span>
+ </a>
+ </li>
+ <li>
+ <a class="sm-btn-dashboard">
+ <span><i class="material-icons">home</i>Dashboard</span>
+ </a>
+ </li>
+
+ {{#if mainNode }}
+ <div class="sm-inventory-tree">
+ {{>TreeNode (argsTreeNode mainNode) }}
+ </div>
+ {{/if }}
+
+ <li><a href="#"><i class="fa fa-envelope"> </i> Contact</a></li>
+ </ul>
+ <div class="menu-footer"> Cisco Systems inc. All rights reserved.</div>
+ </div>
+ </nav>
+</template>
diff --git a/ui/imports/ui/components/accordion-nav-menu/accordion-nav-menu.js b/ui/imports/ui/components/accordion-nav-menu/accordion-nav-menu.js
new file mode 100644
index 0000000..b3bafa2
--- /dev/null
+++ b/ui/imports/ui/components/accordion-nav-menu/accordion-nav-menu.js
@@ -0,0 +1,155 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+/*
+ Template Component: accordionNavMenu
+ */
+
+/* eslint indent: "off" */
+
+import * as R from 'ramda';
+import { Template } from 'meteor/templating';
+import { ReactiveDict } from 'meteor/reactive-dict';
+//import { Tracker } from 'meteor/tracker';
+//import { Session } from 'meteor/session';
+//import { InventoryTreeNodeBehavior } from '/imports/ui/lib/inventory-tree-node-behavior';
+import { EnvironmentTreeNodeBehavior } from '/imports/ui/lib/environment-tree-node-behavior';
+//import { Inventory } from '/imports/api/inventories/inventories';
+import { SimpleSchema } from 'meteor/aldeed:simple-schema';
+
+import '/imports/ui/components/tree-node/tree-node';
+import '/imports/ui/components/accordionTreeNode/accordionTreeNode';
+import '/imports/ui/components/d3graph/d3graph';
+
+import { store } from '/imports/ui/store/store';
+import {
+ resetEnvTreeNodeChildren,
+ addUpdateEnvTreeNode,
+ addUpdateChildrenEnvTreeNode,
+ startOpenEnvTreeNode,
+ startCloseEnvTreeNode,
+ endCloseEnvTreeNode,
+ setEnvChildDetectedTreeNode,
+} from '/imports/ui/actions/environment-panel.actions';
+
+import './accordion-nav-menu.html';
+
+Template.accordionNavMenu.onCreated(function () {
+ let instance = this;
+
+ instance.state = new ReactiveDict();
+ instance.state.setDefault ({});
+
+ createAttachedFns(instance);
+
+ instance.autorun(function () {
+ let data = Template.currentData();
+
+ new SimpleSchema({
+ envName: { type: String },
+ mainNode: { type: Object, blackbox: true },
+ onOpeningDone: { type: Function },
+ onNodeSelected: { type: Function },
+ onToggleGraphReq: { type: Function },
+ onResetSelectedNodeReq: { type: Function },
+ onPositionRetrieved: { type: Function },
+ onScrollToNodePerformed: { type: Function },
+ onOpenLinkReq: { type: Function },
+ onResetNeedChildDetection: { type: Function },
+ }).validate(data);
+ });
+
+});
+
+
+Template.accordionNavMenu.rendered = function () {
+};
+
+Template.accordionNavMenu.onDestroyed(function () {
+});
+
+/*
+ * Events
+ */
+
+Template.accordionNavMenu.events({
+ 'click .sm-btn-dashboard': function (_event, _instance) {
+ let data = Template.currentData();
+ data.onResetSelectedNodeReq();
+ },
+
+ 'click .sm-toggle-graph-button': function (_event, _instance) {
+ let data = Template.currentData();
+ data.onToggleGraphReq();
+ }
+});
+
+/*
+ * Helpers
+ */
+
+Template.accordionNavMenu.helpers({
+ argsTreeNode: function (node) {
+ let instance = Template.instance();
+ let data = Template.currentData();
+
+ return {
+ behavior: EnvironmentTreeNodeBehavior,
+ showDetailsLine: false,
+ openState: node.openState,
+ node: node.nodeInfo,
+ children: node.children,
+ childDetected: node.childDetected,
+ needChildDetection: node.needChildDetection,
+ linkDetected: node.linkDetected,
+ level: node.level,
+ positionNeeded: node.positionNeeded,
+ scrollToNodeIsNeeded: node.scrollToNodeIsNeeded,
+ onResetChildren: instance._fns.onResetChildren,
+ onChildRead: instance._fns.onChildRead,
+ onChildrenRead: instance._fns.onChildrenRead,
+ onStartOpenReq: instance._fns.onStartOpenReq,
+ onStartCloseReq: instance._fns.onStartCloseReq,
+ onClosingDone: instance._fns.onClosingDone,
+ onChildDetected: instance._fns.onChildDetected,
+ onOpeningDone: data.onOpeningDone,
+ onNodeSelected: data.onNodeSelected,
+ onPositionRetrieved: data.onPositionRetrieved,
+ onScrollToNodePerformed: data.onScrollToNodePerformed,
+ onOpenLinkReq: data.onOpenLinkReq,
+ onResetNeedChildDetection: data.onResetNeedChildDetection,
+ };
+ }
+}); // end: helpers
+
+function createAttachedFns(instance) {
+
+ instance._fns = {
+ onResetChildren: function (nodePath) {
+ store.dispatch(resetEnvTreeNodeChildren(R.tail(nodePath)));
+ },
+ onChildRead: function (nodePath, childNode) {
+ store.dispatch(addUpdateEnvTreeNode(R.tail(nodePath), childNode));
+ },
+ onChildrenRead: function (nodePath, childrenInfo) {
+ store.dispatch(addUpdateChildrenEnvTreeNode(R.tail(nodePath), childrenInfo));
+ },
+ onStartOpenReq: (nodePath) => {
+ store.dispatch(startOpenEnvTreeNode(R.tail(nodePath)));
+ },
+ onStartCloseReq: (nodePath) => {
+ store.dispatch(startCloseEnvTreeNode(R.tail(nodePath)));
+ },
+ onClosingDone: (nodePath) => {
+ store.dispatch(endCloseEnvTreeNode(R.tail(nodePath)));
+ },
+ onChildDetected: (nodePath) => {
+ store.dispatch(setEnvChildDetectedTreeNode(R.tail(nodePath)));
+ },
+ };
+}
diff --git a/ui/imports/ui/components/accordion-nav-menu/accordion-nav-menu.styl b/ui/imports/ui/components/accordion-nav-menu/accordion-nav-menu.styl
new file mode 100644
index 0000000..b3c9ef1
--- /dev/null
+++ b/ui/imports/ui/components/accordion-nav-menu/accordion-nav-menu.styl
@@ -0,0 +1,11 @@
+.os-accordion-nav-menu
+ .sm-btn-dashboard
+ cursor: pointer;
+
+ .sm-inventory-tree
+ float: left;
+ width: 100%;
+
+ display: flex;
+ flex-flow: column;
+ align-items: stretch;
diff --git a/ui/imports/ui/components/accordion-wiki-menu/accordion-wiki-menu.html b/ui/imports/ui/components/accordion-wiki-menu/accordion-wiki-menu.html
new file mode 100644
index 0000000..921c8cd
--- /dev/null
+++ b/ui/imports/ui/components/accordion-wiki-menu/accordion-wiki-menu.html
@@ -0,0 +1,42 @@
+<!--
+########################################################################################
+# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others #
+# #
+# All rights reserved. This program and the accompanying materials #
+# are made available under the terms of the Apache License, Version 2.0 #
+# which accompanies this distribution, and is available at #
+# http://www.apache.org/licenses/LICENSE-2.0 #
+########################################################################################
+ -->
+<template name="accordionWikiMenu">
+
+ <div class="left-nav-content-wiki">
+ <nav>
+ <div id="left-nav-menu" class="left-nav-menu">
+ <div class="menu-header"> Let's get started! </div>
+ <ul>
+ <li><a href="#wikiFirstStep"><i class="material-icons">class</i> First step</a></li>
+ <li><a href="#wikiAddNewEnv">
+ <i class="material-icons">class</i> Add new environment</a></li>
+ <li><a href="#wikiAccessSwitchEnv">
+ <i class="material-icons">class</i> Access and Switch environment</a></li>
+ <li><a href="#wikiMainDashboard">
+ <i class="material-icons">class</i> Main Dashboard</a></li>
+ <li><a href="#wikiMainMessages"><i class="material-icons">class</i> Main messages</a></li>
+ <li><a href="#wikiWorkWithEnvs">
+ <i class="material-icons">class</i> Work with environments</a></li>
+ <li><a href="#wikiScanningEnv">
+ <i class="material-icons">class</i> Scanning an environment</a></li>
+ <li><a href="#wikiDeletingEnv">
+ <i class="material-icons">class</i> Deleting an environment</a></li>
+ <li><a href="#wikiEditingEnv">
+ <i class="material-icons">class</i> Editing an environment</a></li>
+ <li><a href="#wikiCalipsoSetting"><i class="material-icons">class</i> Calipso setting</a></li>
+ <li><a href="#wikiBrowsingEnv"><i class="material-icons">class</i> Browsing your cloud environment</a></li>
+ </ul>
+ <div class="menu-footer"> Cisco Systems inc. All rights reserved.</div>
+ </div>
+ </nav>
+ </div>
+
+</template>
diff --git a/ui/imports/ui/components/accordion-wiki-menu/accordion-wiki-menu.js b/ui/imports/ui/components/accordion-wiki-menu/accordion-wiki-menu.js
new file mode 100644
index 0000000..68f38d1
--- /dev/null
+++ b/ui/imports/ui/components/accordion-wiki-menu/accordion-wiki-menu.js
@@ -0,0 +1,147 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * Template Component: accordionWikiMenu
+ */
+
+import { Template } from 'meteor/templating';
+//import { ReactiveDict } from 'meteor/reactive-dict';
+
+//import { store } from '/imports/ui/store/store';
+//import { setCurrentNode } from '/imports/ui/actions/navigation';
+
+import './accordion-wiki-menu.html';
+
+Template.accordionWikiMenu.rendered = function () {
+
+ // init wow lib
+ new WOW().init();
+
+ // smooth scrolling function
+ $(function() {
+ $('a[href*="#"]:not([href="#"])').click(function() {
+ if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'') && location.hostname == this.hostname) {
+ var target = $(this.hash);
+ target = target.length ? target : $('[name=' + this.hash.slice(1) +']');
+ if (target.length) {
+ $('html, body').animate({
+ scrollTop: target.offset().top - 90
+ }, 1000);
+ return false;
+ }
+ }
+ });
+ });
+
+ /* accordion menu plugin*/
+ (function($, window, _document, _undefined) {
+ var pluginName = 'accordion';
+ var defaults = {
+ speed: 200,
+ showDelay: 0,
+ hideDelay: 0,
+ singleOpen: true,
+ clickEffect: true,
+ indicator: 'submenu-indicator-minus',
+ subMenu: 'submenu',
+ event: 'click touchstart' // click, touchstart
+ };
+
+ function Plugin(element, options) {
+ this.element = element;
+ this.settings = $.extend({}, defaults, options);
+ this._defaults = defaults;
+ this._name = pluginName;
+ this.init();
+ }
+ $.extend(Plugin.prototype, {
+ init: function() {
+ this.openSubmenu();
+ this.submenuIndicators();
+ if (defaults.clickEffect) {
+ this.addClickEffect();
+ }
+ },
+ openSubmenu: function() {
+ $(this.element).children('ul').find('li').bind(defaults.event, function(e) {
+ e.stopPropagation();
+ e.preventDefault();
+ var $subMenus = $(this).children('.' + defaults.subMenu);
+ var $allSubMenus = $(this).find('.' + defaults.subMenu);
+ if ($subMenus.length > 0) {
+ if ($subMenus.css('display') == 'none') {
+ $subMenus.slideDown(defaults.speed).siblings('a').addClass(defaults.indicator);
+ if (defaults.singleOpen) {
+ $(this).siblings().find('.' + defaults.subMenu).slideUp(defaults.speed)
+ .end().find('a').removeClass(defaults.indicator);
+ }
+ return false;
+ } else {
+ $(this).find('.' + defaults.subMenu).delay(defaults.hideDelay).slideUp(defaults.speed);
+ }
+ if ($allSubMenus.siblings('a').hasClass(defaults.indicator)) {
+ $allSubMenus.siblings('a').removeClass(defaults.indicator);
+ }
+ }
+ window.location.href = $(this).children('a').attr('href');
+ });
+ },
+ submenuIndicators: function() {
+ if ($(this.element).find('.' + defaults.subMenu).length > 0) {
+ $(this.element).find('.' + defaults.subMenu).siblings('a').append('<span class="submenu-indicator">+</span>');
+ }
+ },
+ addClickEffect: function() {
+ var ink, d, x, y;
+ $(this.element).find('a').bind('click touchstart', function(e) {
+ $('.ink').remove();
+ if ($(this).children('.ink').length === 0) {
+ $(this).prepend('<span class="ink"></span>');
+ }
+ ink = $(this).find('.ink');
+ ink.removeClass('animate-ink');
+ if (!ink.height() && !ink.width()) {
+ d = Math.max($(this).outerWidth(), $(this).outerHeight());
+ ink.css({
+ height: d,
+ width: d
+ });
+ }
+ x = e.pageX - $(this).offset().left - ink.width() / 2;
+ y = e.pageY - $(this).offset().top - ink.height() / 2;
+ ink.css({
+ top: y + 'px',
+ left: x + 'px'
+ }).addClass('animate-ink');
+ });
+ }
+ });
+ $.fn[pluginName] = function(options) {
+ this.each(function() {
+ if (!$.data(this, 'plugin_' + pluginName)) {
+ $.data(this, 'plugin_' + pluginName, new Plugin(this, options));
+ }
+ });
+ return this;
+ };
+ })(jQuery, window, document);
+
+ jQuery(document).ready(function($) {
+ $('#left-nav-menu').accordion();
+ $('.colors a').click(function() {
+ if ($(this).attr('class') != 'default') {
+ $('#left-nav-menu').removeClass();
+ $('#left-nav-menu').addClass('menu').addClass($(this).attr('class'));
+ } else {
+ $('#left-nav-menu').removeClass();
+ $('#left-nav-menu').addClass('menu');
+ }
+ });
+ });
+};
diff --git a/ui/imports/ui/components/accordionTreeNode/accordion-tree-node.styl b/ui/imports/ui/components/accordionTreeNode/accordion-tree-node.styl
new file mode 100644
index 0000000..b41d1f9
--- /dev/null
+++ b/ui/imports/ui/components/accordionTreeNode/accordion-tree-node.styl
@@ -0,0 +1,5 @@
+.os-accordion-tree-node
+ .js-item-link
+ display: flex;
+ justify-content: space-between;
+
diff --git a/ui/imports/ui/components/accordionTreeNode/accordionTreeNode.html b/ui/imports/ui/components/accordionTreeNode/accordionTreeNode.html
new file mode 100644
index 0000000..cb1b10e
--- /dev/null
+++ b/ui/imports/ui/components/accordionTreeNode/accordionTreeNode.html
@@ -0,0 +1,54 @@
+<!--
+########################################################################################
+# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others #
+# #
+# All rights reserved. This program and the accompanying materials #
+# are made available under the terms of the Apache License, Version 2.0 #
+# which accompanies this distribution, and is available at #
+# http://www.apache.org/licenses/LICENSE-2.0 #
+########################################################################################
+ -->
+<template name="accordionTreeNode" >
+ <li class="os-accordion-tree-node {{#if node.clique }}genGraphClick{{/if}}"
+ id="{{ node.id }}"
+ title="{{ node.name }}"
+ type="{{ node.type }}"
+ clique="{{ node.clique }}"
+ objId="{{ node._id._str }}"
+ >
+ <a class="js-item-link" href="#">
+ <div class="sm-node-content-part">
+ {{#if hasChildren }}
+ <i class="material-icons">class</i>
+ {{else }}
+ <i class="material-icons">description</i>
+ {{/if }}
+
+ {{ node.object_name }}
+ </div>
+
+ {{#if showNow }}
+
+ <div class="sm-open-close-indicator">
+ {{#if hasChildren }}
+
+ {{#if isOpen }}
+ <i class="fa fa-minus" aria-hidden="true"></i>
+ {{else }}
+ <i class="fa fa-plus" aria-hidden="true"></i>
+ {{/if }}
+
+ {{/if }}
+ </div>
+
+ {{/if }}
+ </a>
+
+ {{#if isNotClose }}
+ {{> accordionTreeNodeChildren (createChildrenArgs node selectedNode) }}
+ {{/if }}
+
+ </li>
+ {{ reactOnShowOpen(showOpen) }}
+ {{ reactOnNewData(node) }}
+</template>
diff --git a/ui/imports/ui/components/accordionTreeNode/accordionTreeNode.js b/ui/imports/ui/components/accordionTreeNode/accordionTreeNode.js
new file mode 100644
index 0000000..837c6a1
--- /dev/null
+++ b/ui/imports/ui/components/accordionTreeNode/accordionTreeNode.js
@@ -0,0 +1,284 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * Template Component: accordionTreeNode
+ */
+
+/* eslint no-undef: off */
+
+import { Template } from 'meteor/templating';
+import { ReactiveDict } from 'meteor/reactive-dict';
+
+import { Inventory } from '/imports/api/inventories/inventories';
+//import { store } from '/client/imports/store';
+//import { setCurrentNode } from '/client/imports/actions/navigation';
+
+//import { d3Graph } from '/imports/lib/d3-graph';
+
+import '/imports/ui/components/accordionTreeNodeChildren/accordionTreeNodeChildren';
+import './accordionTreeNode.html';
+
+var subMenuClass = 'submenu';
+var switchingSpeed = 200;
+
+Template.accordionTreeNode.onCreated(function () {
+ var instance = this;
+ this.state = new ReactiveDict();
+ this.state.setDefault({
+ openState: 'close',
+ needChildrenClosing: false,
+ openedChildId: null,
+ showNow: false,
+ startAsClickedState: 'not_done',
+ data: null,
+ });
+
+ instance.autorun(function () {
+ //var tempData = instance.state.get('data');
+
+ let data = Template.currentData();
+ let node = data.node;
+ instance.subscribe('inventory.first-child',
+ node.id, node.type, node.name, node.environment);
+ });
+
+});
+
+Template.accordionTreeNode.rendered = function () {
+ var instance = this;
+
+ setTimeout(function () {
+ instance.state.set('showNow', true);
+ }, 50);
+
+ instance.autorun(function () {
+ var openState = instance.state.get('openState');
+ switch (openState) {
+ case 'opening':
+ // Blaze arcitecture bug: in render the children are not it rendered.
+ // There for we need to wait until children are rendered to do the animation.
+ instance.state.set('openState', 'open');
+ activateNodeAction(instance);
+ setTimeout(function () {
+ animateOpening(instance.$(instance.firstNode));
+ }, 65);
+ break;
+
+ case 'closing':
+
+ animateClosing(instance.$(instance.firstNode));
+ setTimeout(function () {
+ instance.state.set('openState', 'close');
+ //instance.data.onClose(instance.data.node.id);
+ }, 200);
+ break;
+
+ case 'none':
+ break;
+
+ default:
+ break;
+ }
+ });
+
+};
+
+Template.accordionTreeNode.helpers({
+ reactOnShowOpen: function (showOpen) {
+ let instance = Template.instance();
+ let openState = instance.state.get('openState');
+ let nextOpenState = null;
+
+ if (showOpen === false) {
+ if (openState === 'open' ||
+ openState === 'opening') {
+ nextOpenState = 'closing';
+ }
+ } else if (showOpen === true) {
+ if (openState === 'close' ||
+ openState === 'closing') {
+ nextOpenState = 'opening';
+ }
+ }
+
+ if (nextOpenState) {
+ setTimeout(function () {
+ instance.state.set('openState', nextOpenState);
+ }, 10);
+ }
+ },
+
+ reactOnNewData: function (node) {
+ let instance = Template.instance();
+ instance.state.set('data', { node: node });
+ },
+
+ isNot: function (condition) {
+ return ! condition;
+ },
+
+ isNotClose: function () {
+ var instance = Template.instance();
+ var openState = instance.state.get('openState');
+ return (openState !== 'close');
+ },
+
+ hasClique: function(){
+ var controller = Iron.controller();
+ var envName = controller.state.get('envName');
+
+ if(Inventory.find({
+ parent_id: this.node.id,
+ parent_type: this.node.type,
+ environment: envName,
+ clique:true,
+ show_in_tree:true
+ }).count() > 0){
+
+ return 'true';
+ }
+ else{
+ return 'false';
+ }
+
+ },
+
+ hasChildren: function(){
+ return hasChildren(this);
+ },
+
+ isOpen: function () {
+ var instance = Template.instance();
+ return instance.state.get('openState') === 'open';
+ },
+
+ isOpenOrOpening: function () {
+ var instance = Template.instance();
+ var openState = instance.state.get('openState');
+ return (openState === 'open' || openState === 'opening');
+ },
+
+ createChildrenArgs: function(
+ parentNode,
+ selectedNode
+ ) {
+
+ let instance = Template.instance();
+ return {
+ node: parentNode,
+ selectedNode: selectedNode,
+ onClick(childNode) {
+ instance.data.onClick(childNode);
+ },
+ };
+ },
+
+ isNeedChildrenClosing: function () {
+ var instance = Template.instance();
+ return instance.state.get('needChildrenClosing');
+ },
+
+ closeWhenNeeded: function() {
+ var instance = Template.instance();
+ var openState = instance.state.get('openState');
+
+ if (! singleOpenOption) { return; }
+ if (! instance.data.openedFamilyId) { return; }
+ if (openState !== 'open') { return; }
+ if (instance.data.node.id === instance.data.openedFamilyId) { return; }
+
+ instance.state.set('openState', 'closing');
+ },
+
+ showNow: function () {
+ var instance = Template.instance();
+ return instance.state.get('showNow');
+ },
+});
+
+Template.accordionTreeNode.events({
+ 'click': function(event, instance){
+ event.stopPropagation();
+ event.preventDefault();
+
+ instance.data.onClick(instance.data.node);
+
+ /*
+ * todo : remove code
+ store.dispatch(setCurrentNode(
+ instance.data.node.id_path,
+ instance.data.node.name_path));
+
+ var openState = instance.state.get('openState');
+ var nextState = openState;
+
+ if (hasChildren(instance.data)) {
+ switch (openState) {
+ case 'open':
+ nextState = 'closing';
+ break;
+
+ case 'opening':
+ break;
+
+ case 'close':
+ nextState = 'opening';
+ break;
+
+ case 'closing':
+ break;
+ }
+
+ instance.state.set('openState', nextState);
+
+ }
+
+
+ */
+ },
+});
+
+function activateNodeAction (_instance) {
+
+}
+
+function hasChildren(instance) {
+ var counterName = 'inventory.first-child!counter!id=' + instance.node.id;
+ return Counts.get(counterName) > 0;
+
+ /*
+ var controller = Iron.controller();
+ var envName = controller.state.get('envName');
+
+ return hasChildrenQuery(instance.node, envName);
+ */
+}
+
+/*
+function hasChildrenQuery(node, envName) {
+ return Inventory.find({
+ parent_id: node.id,
+ parent_type: node.type,
+ environment: envName,
+ show_in_tree: true
+ }, {
+ limit: 1
+ }).count() > 0;
+}
+*/
+
+function animateOpening($element) {
+ $subMenu = $element.children('.' + subMenuClass);
+ $subMenu.slideDown(switchingSpeed);
+}
+
+function animateClosing($element) {
+ $subMenu = $element.children('.' + subMenuClass);
+ $subMenu.slideUp(switchingSpeed);
+}
diff --git a/ui/imports/ui/components/accordionTreeNodeChildren/accordionTreeNodeChildren.html b/ui/imports/ui/components/accordionTreeNodeChildren/accordionTreeNodeChildren.html
new file mode 100644
index 0000000..786ecb1
--- /dev/null
+++ b/ui/imports/ui/components/accordionTreeNodeChildren/accordionTreeNodeChildren.html
@@ -0,0 +1,19 @@
+<!--
+########################################################################################
+# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others #
+# #
+# All rights reserved. This program and the accompanying materials #
+# are made available under the terms of the Apache License, Version 2.0 #
+# which accompanies this distribution, and is available at #
+# http://www.apache.org/licenses/LICENSE-2.0 #
+########################################################################################
+ -->
+<template name="accordionTreeNodeChildren" >
+<ul class="submenu">
+ {{#each childItem in children }}
+ {{> accordionTreeNode (createTreeNodeArgs childItem selectedNode) }}
+ {{/each }}
+</ul>
+{{ reactOnNewData node }}
+</template>
+
diff --git a/ui/imports/ui/components/accordionTreeNodeChildren/accordionTreeNodeChildren.js b/ui/imports/ui/components/accordionTreeNodeChildren/accordionTreeNodeChildren.js
new file mode 100644
index 0000000..a74059c
--- /dev/null
+++ b/ui/imports/ui/components/accordionTreeNodeChildren/accordionTreeNodeChildren.js
@@ -0,0 +1,125 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * Template Component: accordionTreeNodeChildren
+ */
+
+/* eslint no-undef: off */
+
+import * as R from 'ramda';
+import { Template } from 'meteor/templating';
+import { ReactiveDict } from 'meteor/reactive-dict';
+import { Inventory } from '/imports/api/inventories/inventories';
+
+import './accordionTreeNodeChildren.html';
+
+Template.accordionTreeNodeChildren.onCreated(function () {
+ var instance = this;
+ this.state = new ReactiveDict();
+ this.state.setDefault({
+ data: null,
+ siblingId: null
+ });
+
+ instance.autorun(function () {
+ let data = Template.currentData();
+ let node = data.node;
+ instance.subscribe('inventory.children',
+ node.id, node.type, node.name, node.environment);
+
+ if (R.equals('host_ref', node.type)) {
+ instance.subscribe('inventory?name&env&type',
+ node.name, node.environment, 'host');
+
+ Inventory.find({
+ name: node.name,
+ environment: node.environment,
+ type: 'host'
+ }).forEach((sibling) => {
+ instance.state.set('siblingId', sibling.id);
+ });
+ }
+ });
+
+});
+
+Template.accordionTreeNodeChildren.helpers({
+ reactOnNewData: function (node) {
+ let instance = Template.instance();
+ instance.state.set('data', { node: node });
+ },
+
+ children: function () {
+ let instance = Template.instance();
+ let siblingId = instance.state.get('siblingId');
+
+ return getChildrenQuery(instance.data.node, siblingId);
+ },
+
+ createTreeNodeArgs: function(
+ node,
+ selectedNode
+ ) {
+
+ var instance = Template.instance();
+
+ let firstChild = null;
+ let restOfChildren = null;
+ let showOpen = false;
+
+ if ((! R.isNil(selectedNode)) &&
+ selectedNode.length > 0
+ ) {
+ firstChild = selectedNode[0];
+ restOfChildren = selectedNode.length > 1 ?
+ R.slice(1, Infinity, selectedNode) : null;
+ showOpen = firstChild.id === node.id ? true : false;
+ }
+
+ return {
+ node: node,
+ showOpen: showOpen,
+ selectedNode: restOfChildren,
+ onClick: instance.data.onClick
+ };
+ },
+
+
+});
+
+Template.accordionTreeNodeChildren.events({
+});
+
+function getChildrenQuery(node, siblingId) {
+ let query =
+ {
+ $or: [
+ {
+ parent_id: node.id,
+ parent_type: node.type,
+ environment: node.environment,
+ show_in_tree: true
+ }
+ ]
+ };
+
+
+ if (R.equals('host_ref', node.type)) {
+ query = R.merge(query, {
+ $or: R.append({
+ parent_id: siblingId,
+ show_in_tree: true
+ }, query.$or)
+ });
+ }
+
+ console.log('getChildrenQuery', R.toString(query));
+
+ return Inventory.find(query);
+}
diff --git a/ui/imports/ui/components/aggregate-dashboard/aggregate-dashboard.html b/ui/imports/ui/components/aggregate-dashboard/aggregate-dashboard.html
new file mode 100644
index 0000000..b1769ca
--- /dev/null
+++ b/ui/imports/ui/components/aggregate-dashboard/aggregate-dashboard.html
@@ -0,0 +1,35 @@
+<!--
+########################################################################################
+# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others #
+# #
+# All rights reserved. This program and the accompanying materials #
+# are made available under the terms of the Apache License, Version 2.0 #
+# which accompanies this distribution, and is available at #
+# http://www.apache.org/licenses/LICENSE-2.0 #
+########################################################################################
+ -->
+<template name="AggregateDashboard">
+ <div class="os-aggregate-dashboard flex-box justify-content-between">
+ <div class="flex-box-3 main-layout-no-nav">
+
+ <div class="flex">
+ <div class="flex-box-1 cards white title">
+ <h4>Aggregate name: {{ aggregate.name }}</h4>
+ </div>
+ </div>
+
+ <div class="sm-info-boxes">
+ {{#each infoBox in infoBoxes }}
+ {{> DataCubic (argsInfoBox infoBox) }}
+ {{/each }}
+ </div>
+
+ <div class="sm-list-info-boxes">
+ {{#each listInfoBox in listInfoBoxes }}
+ {{> ListInfoBox (argsListInfoBox listInfoBox) }}
+ {{/each }}
+ </div>
+
+ </div>
+ </div>
+</template>
diff --git a/ui/imports/ui/components/aggregate-dashboard/aggregate-dashboard.js b/ui/imports/ui/components/aggregate-dashboard/aggregate-dashboard.js
new file mode 100644
index 0000000..5e7278d
--- /dev/null
+++ b/ui/imports/ui/components/aggregate-dashboard/aggregate-dashboard.js
@@ -0,0 +1,212 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * Template Component: AggregateDashboard
+ */
+
+//import { Meteor } from 'meteor/meteor';
+import { Template } from 'meteor/templating';
+import { ReactiveDict } from 'meteor/reactive-dict';
+import { Inventory } from '/imports/api/inventories/inventories';
+import { SimpleSchema } from 'meteor/aldeed:simple-schema';
+import { regexEscape } from '/imports/lib/regex-utils';
+import * as R from 'ramda';
+import { store } from '/imports/ui/store/store';
+import { Icon } from '/imports/lib/icon';
+
+//import '/imports/ui/components/accordionNavMenu/accordionNavMenu';
+import '/imports/ui/components/data-cubic/data-cubic';
+import '/imports/ui/components/list-info-box/list-info-box';
+
+import './aggregate-dashboard.html';
+
+let infoBoxes = [{
+ header: ['components', 'aggregateDashboard', 'infoBoxes', 'instances', 'header'],
+ dataSource: 'instancesCount',
+ icon: { type: 'fa', name: 'desktop' },
+ theme: 'dark'
+}, {
+ header: ['components', 'aggregateDashboard', 'infoBoxes', 'vServices', 'header'],
+ dataSource: 'vServicesCount',
+ icon: { type: 'fa', name: 'object-group' },
+ theme: 'dark'
+}, {
+ header: ['components', 'aggregateDashboard', 'infoBoxes', 'hosts', 'header'],
+ dataSource: 'hostsCount',
+ icon: { type: 'fa', name: 'server' },
+ theme: 'dark'
+}, {
+ header: ['components', 'aggregateDashboard', 'infoBoxes', 'vConnectors', 'header'],
+ dataSource: 'vConnectorsCount',
+ icon: { type: 'fa', name: 'compress' },
+ theme: 'dark'
+}, {
+ header: ['components', 'aggregateDashboard', 'infoBoxes', 'vEdges', 'header'],
+ dataSource: 'vEdgesCount',
+ icon: { type: 'fa', name: 'external-link' },
+ theme: 'dark'
+}];
+
+let listInfoBoxes = [{
+ header: ['components', 'aggregateDashboard', 'listInfoBoxes', 'hosts', 'header'],
+ listName: 'hosts',
+ listItemFormat: { label: 'name', value: 'id_path' },
+ icon: { type: 'material', name: 'developer_board' },
+}];
+
+/*
+ * Lifecycles
+ */
+
+Template.AggregateDashboard.onCreated(function() {
+ var instance = this;
+
+ instance.state = new ReactiveDict();
+ instance.state.setDefault({
+ _id: null,
+ id_path: null,
+ instancesCount: 0,
+ vServicesCount: 0,
+ hostsCount: 0,
+ vConnectors: 0,
+ vEdges: 0,
+ });
+
+ instance.autorun(function () {
+ let data = Template.currentData();
+
+ new SimpleSchema({
+ _id: { type: { _str: { type: String, regEx: SimpleSchema.RegEx.Id } } },
+ onNodeSelected: { type: Function },
+ }).validate(data);
+
+ instance.state.set('_id', data._id);
+ });
+
+ instance.autorun(function () {
+ let _id = instance.state.get('_id');
+
+ instance.subscribe('inventory?_id', _id);
+ Inventory.find({ _id: _id }).forEach((aggr) => {
+ instance.state.set('id_path', aggr.id_path);
+
+ instance.subscribe('inventory?id_path', aggr.id_path);
+ instance.subscribe('inventory?id_path_start&type', aggr.id_path, 'instance');
+ instance.subscribe('inventory?id_path_start&type', aggr.id_path, 'vservice');
+ instance.subscribe('inventory?id_path_start&type', aggr.id_path, 'host');
+ instance.subscribe('inventory?id_path_start&type', aggr.id_path, 'vconnector');
+ instance.subscribe('inventory?id_path_start&type', aggr.id_path, 'vedge');
+
+ let idPathExp = new RegExp(`^${regexEscape(aggr.id_path)}`);
+
+ instance.state.set('instancesCount', Inventory.find({
+ id_path: idPathExp,
+ type: 'instance'
+ }).count());
+
+ instance.state.set('vServicesCount', Inventory.find({
+ id_path: idPathExp,
+ type: 'vservice'
+ }).count());
+
+ instance.state.set('hostsCount', Inventory.find({
+ id_path: idPathExp,
+ type: 'host'
+ }).count());
+
+ instance.state.set('vConnectorsCount', Inventory.find({
+ id_path: idPathExp,
+ type: 'vconnector'
+ }).count());
+
+ instance.state.set('vEdgesCount', Inventory.find({
+ id_path: idPathExp,
+ type: 'vedge'
+ }).count());
+ });
+ });
+});
+
+/*
+Template.AggregateDashboard.rendered = function() {
+};
+*/
+
+/*
+ * Events
+ */
+
+Template.AggregateDashboard.events({
+});
+
+/*
+ * Helpers
+ */
+
+Template.AggregateDashboard.helpers({
+ aggregate: function () {
+ let instance = Template.instance();
+ let aggregate_id_path = instance.state.get('id_path');
+
+ return Inventory.findOne({ id_path: aggregate_id_path });
+ },
+
+ infoBoxes: function () {
+ return infoBoxes;
+ },
+
+ listInfoBoxes: function () {
+ return listInfoBoxes;
+ },
+
+ argsInfoBox: function (infoBox) {
+ let instance = Template.instance();
+
+ return {
+ header: R.path(infoBox.header, store.getState().api.i18n),
+ dataInfo: instance.state.get(infoBox.dataSource).toString(),
+ icon: new Icon(infoBox.icon),
+ theme: infoBox.theme
+ };
+ },
+
+ argsListInfoBox: function (listInfoBox) {
+ let instance = Template.instance();
+ let data = Template.currentData();
+ let aggregate_id_path = instance.state.get('id_path');
+
+ return {
+ header: R.path(listInfoBox.header, store.getState().api.i18n),
+ list: getList(listInfoBox.listName, aggregate_id_path),
+ //dataInfo: instance.state.get(infoBox.dataSource).toString(),
+ icon: new Icon(listInfoBox.icon),
+ //theme: infoBox.theme
+ listItemFormat: listInfoBox.listItemFormat,
+ onItemSelected: function (itemKey) {
+ data.onNodeSelected(new Mongo.ObjectID(itemKey));
+ }
+ };
+ }
+});
+
+
+function getList(listName, parentIdPath) {
+ let idPathExp = new RegExp(`^${regexEscape(parentIdPath)}`);
+
+ switch (listName) {
+ case 'hosts':
+ return Inventory.find({
+ id_path: idPathExp,
+ type: 'host'
+ });
+
+ default:
+ throw 'unknowned list type';
+ }
+}
diff --git a/ui/imports/ui/components/aggregate-dashboard/aggregate-dashboard.styl b/ui/imports/ui/components/aggregate-dashboard/aggregate-dashboard.styl
new file mode 100644
index 0000000..9764778
--- /dev/null
+++ b/ui/imports/ui/components/aggregate-dashboard/aggregate-dashboard.styl
@@ -0,0 +1,10 @@
+.os-aggregate-dashboard
+ .sm-info-boxes
+ display: flex
+ flex-flow: row wrap;
+ justify-content: space-around
+
+ .sm-list-info-boxes
+ display: flex;
+ flex-flow: row wrap
+ justify-content: space-around
diff --git a/ui/imports/ui/components/alarm-icons/alarm-icons.html b/ui/imports/ui/components/alarm-icons/alarm-icons.html
new file mode 100644
index 0000000..d584990
--- /dev/null
+++ b/ui/imports/ui/components/alarm-icons/alarm-icons.html
@@ -0,0 +1,78 @@
+<!--
+########################################################################################
+# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others #
+# #
+# All rights reserved. This program and the accompanying materials #
+# are made available under the terms of the Apache License, Version 2.0 #
+# which accompanies this distribution, and is available at #
+# http://www.apache.org/licenses/LICENSE-2.0 #
+########################################################################################
+ -->
+<template name="alarmIcons">
+ <div class="os-alarm-icons">
+
+ <div class="alarm-icons">
+
+ <div class="dropdown">
+ <div class="material-icons mdl-badge mdl-badge--overlap dropdown-toggle"
+ data-badge="{{countOf 'messages/count?level=info'}}"
+ type="button"
+ id="dropdownMenu1"
+ data-toggle="modal"
+ data-target="#messagesModalGlobal"
+ data-message-level="info"
+ >notifications</div>
+ </div>
+
+ <div class="dropdown">
+ <div class="material-icons mdl-badge mdl-badge--overlap dropdown-toggle"
+ data-badge="{{countOf 'messages/count?level=warning'}}"
+ type="button"
+ id="dropdownMenu1"
+ data-toggle="modal"
+ data-target="#messagesModalGlobal"
+ data-message-level="warning"
+ >warning</div>
+ </div>
+
+ <div class="dropdown">
+ <div class="material-icons mdl-badge mdl-badge--overlap dropdown-toggle"
+ data-badge="{{countOf 'messages/count?level=error'}}"
+ type="button"
+ id="dropdownMenu1"
+ data-toggle="modal"
+ data-target="#messagesModalGlobal"
+ data-message-level="error"
+ >error</div>
+ </div>
+
+ <div class="dropdown">
+ <div class="material-icons dropdown-toggle" type="button" id="dropdownMenu1" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">settings</div>
+ <ul class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownMenu1">
+ <li class="dropdown-header"
+ ><a href="{{pathFor route='scheduled-scans-list' query=''}}" >Scheduled Scans</a></li>
+ <li class="dropdown-header"
+ ><a href="{{pathFor route='scans-list' query=''}}" >Scans</a></li>
+ <li class="dropdown-header"
+ ><a href="{{pathFor route='link-types-list' query=''}}" >Link Types</a></li>
+ <li class="dropdown-header"
+ ><a href="{{pathFor route='clique-types-list' query=''}}" >Clique Types</a></li>
+ <li class="dropdown-header"
+ ><a href="{{pathFor route='clique-constraints-list' query=''}}" >Clique Constraints</a></li>
+
+ <li class="dropdown-header">
+ <a href="{{pathFor route='messages-list' query=''}}" >Messages</a>
+ </li>
+
+ {{#if isAdmin }}
+ <li class="dropdown-header">
+ <a href="{{pathFor route='user-list' query=''}}">Users</a>
+ </li>
+ {{/if }}
+ </ul>
+ </div>
+
+ </div>
+
+ </div>
+</template>
diff --git a/ui/imports/ui/components/alarm-icons/alarm-icons.js b/ui/imports/ui/components/alarm-icons/alarm-icons.js
new file mode 100644
index 0000000..5c7af31
--- /dev/null
+++ b/ui/imports/ui/components/alarm-icons/alarm-icons.js
@@ -0,0 +1,53 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * Template Component: alarmIcons
+ */
+
+import '/imports/ui/components/breadcrumb/breadcrumb';
+import { Messages } from '/imports/api/messages/messages';
+import { Roles } from 'meteor/alanning:roles';
+
+import './alarm-icons.html';
+
+/*
+ * Lifecycle
+ */
+
+Template.alarmIcons.onCreated(function () {
+ let instance = this;
+
+ instance.autorun(function () {
+ instance.subscribe('messages/count?level', 'info');
+ instance.subscribe('messages/count?level', 'warning');
+ instance.subscribe('messages/count?level', 'error');
+ });
+});
+
+/*
+ * Helpers
+ */
+
+Template.alarmIcons.helpers({
+ isAdmin: function () {
+ return Roles.userIsInRole(Meteor.userId(), 'manage-users', Roles.GLOBAL_GROUP);
+ },
+
+ infosCount: function(){
+ return Messages.find({level:'info'}).count();
+ },
+
+ warningsCount: function(){
+ return Messages.find({level:'warning'}).count();
+ },
+
+ errorsCount: function(){
+ return Messages.find({level:'error'}).count();
+ },
+});
diff --git a/ui/imports/ui/components/alarm-icons/alarm-icons.styl b/ui/imports/ui/components/alarm-icons/alarm-icons.styl
new file mode 100644
index 0000000..20bf947
--- /dev/null
+++ b/ui/imports/ui/components/alarm-icons/alarm-icons.styl
@@ -0,0 +1 @@
+// alarm icon styles
diff --git a/ui/imports/ui/components/auto-search-result-line/auto-search-result-line.html b/ui/imports/ui/components/auto-search-result-line/auto-search-result-line.html
new file mode 100644
index 0000000..247e1ea
--- /dev/null
+++ b/ui/imports/ui/components/auto-search-result-line/auto-search-result-line.html
@@ -0,0 +1,16 @@
+<!--
+########################################################################################
+# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others #
+# #
+# All rights reserved. This program and the accompanying materials #
+# are made available under the terms of the Apache License, Version 2.0 #
+# which accompanies this distribution, and is available at #
+# http://www.apache.org/licenses/LICENSE-2.0 #
+########################################################################################
+ -->
+<template name="AutoSearchResultLine">
+ <li class="os-auto-search-result-line">
+ <span class="sm-header-subline"><span class="sm-object-name">{{ objectName }}</span> - <span class="sm-object-type">{{ objectType }}</span></span>
+ <span class="sm-detail-subline">{{ namePath }}</span>
+ </li>
+</template>
diff --git a/ui/imports/ui/components/auto-search-result-line/auto-search-result-line.js b/ui/imports/ui/components/auto-search-result-line/auto-search-result-line.js
new file mode 100644
index 0000000..23272de
--- /dev/null
+++ b/ui/imports/ui/components/auto-search-result-line/auto-search-result-line.js
@@ -0,0 +1,51 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * Template Component: AutoSearchResultLine
+ */
+
+//import { Meteor } from 'meteor/meteor';
+import { Template } from 'meteor/templating';
+//import { ReactiveDict } from 'meteor/reactive-dict';
+
+import './auto-search-result-line.html';
+
+/*
+ * Lifecycles
+ */
+
+Template.AutoSearchResultLine.onCreated(function() {
+});
+
+/*
+Template.AutoSearchResultLine.rendered = function() {
+};
+*/
+
+/*
+ * Events
+ */
+
+Template.AutoSearchResultLine.events({
+ 'click': function(event, instance) {
+ event.stopPropagation();
+ event.preventDefault();
+
+ instance.data.onClick(instance.data.namePath);
+ }
+});
+
+/*
+ * Helpers
+ */
+
+Template.AutoSearchResultLine.helpers({
+});
+
+
diff --git a/ui/imports/ui/components/auto-search-result-line/auto-search-result-line.styl b/ui/imports/ui/components/auto-search-result-line/auto-search-result-line.styl
new file mode 100644
index 0000000..df6caa5
--- /dev/null
+++ b/ui/imports/ui/components/auto-search-result-line/auto-search-result-line.styl
@@ -0,0 +1,23 @@
+.os-auto-search-result-line
+ cursor: pointer;
+ line-height: 1.1;
+ font-size: 1.1em;
+ padding: 5px 15px;
+ margin: 0 auto;
+ color: brand-blue
+ border-bottom: 1px solid #e8e8e8;
+
+ &:hover
+ background-color: #f2f2f2;
+
+ .sm-header-subline
+ display: block;
+ margin-bottom: 5px;
+
+ .sm-detail-subline
+ display: block;
+ white-space: nowrap;
+ color: spark-grey
+
+.os-auto-search-result-line:last-child
+ border-bottom: none;
diff --git a/ui/imports/ui/components/breadcrumb/breadcrumb.html b/ui/imports/ui/components/breadcrumb/breadcrumb.html
new file mode 100644
index 0000000..0967b7d
--- /dev/null
+++ b/ui/imports/ui/components/breadcrumb/breadcrumb.html
@@ -0,0 +1,17 @@
+<!--
+########################################################################################
+# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others #
+# #
+# All rights reserved. This program and the accompanying materials #
+# are made available under the terms of the Apache License, Version 2.0 #
+# which accompanies this distribution, and is available at #
+# http://www.apache.org/licenses/LICENSE-2.0 #
+########################################################################################
+ -->
+<template name="breadcrumb" >
+ <ol class="os-breadcrumb breadcrumb">
+ {{#each node in nodesList }}
+ {{> breadcrumbNode (argsNode node) }}
+ {{/each }}
+ </ol>
+</template>
diff --git a/ui/imports/ui/components/breadcrumb/breadcrumb.js b/ui/imports/ui/components/breadcrumb/breadcrumb.js
new file mode 100644
index 0000000..642797f
--- /dev/null
+++ b/ui/imports/ui/components/breadcrumb/breadcrumb.js
@@ -0,0 +1,83 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * Template Component: breadcrumb
+ */
+
+import { Template } from 'meteor/templating';
+import { ReactiveDict } from 'meteor/reactive-dict';
+import { SimpleSchema } from 'meteor/aldeed:simple-schema';
+import * as R from 'ramda';
+//import { Inventory } from '/imports/api/inventories/inventories';
+
+import '../breadcrumbNode/breadcrumbNode';
+import './breadcrumb.html';
+
+Template.breadcrumb.onCreated(function () {
+ let instance = this;
+ instance.state = new ReactiveDict();
+ instance.state.setDefault({
+ nodeId: null,
+ nodesList: [],
+ });
+
+ instance.autorun(function () {
+ let data = Template.currentData();
+ new SimpleSchema({
+ nodeId: { type: { _str: { type: String, regEx: SimpleSchema.RegEx.Id } } },
+ onNodeSelected: { type: Function },
+ }).validate(data);
+
+ instance.state.set('nodeId', data.nodeId);
+ });
+
+ instance.autorun(function () {
+ let nodeId = instance.state.get('nodeId');
+
+ if (R.isNil(nodeId)) {
+ return;
+ }
+
+ Meteor.apply('expandNodePath', [ nodeId ], { wait: false }, function (err, res) {
+ if (err) {
+ console.error(err);
+ return;
+ }
+
+ if (R.isNil(res)) {
+ instance.state.set('nodesList', []);
+ return;
+ }
+
+ instance.state.set('nodesList', res);
+ });
+ });
+});
+
+Template.breadcrumb.onDestroyed(function () {
+});
+
+Template.breadcrumb.helpers({
+ nodesList: function () {
+ let instance = Template.instance();
+ return instance.state.get('nodesList');
+ },
+
+ argsNode: function (node) {
+ //let instance = Template.instance();
+ let data = Template.currentData();
+
+ return {
+ node: node,
+ onClick: function () {
+ data.onNodeSelected(node);
+ }
+ };
+ },
+}); // end: helpers
diff --git a/ui/imports/ui/components/breadcrumb/breadcrumb.styl b/ui/imports/ui/components/breadcrumb/breadcrumb.styl
new file mode 100644
index 0000000..f0417de
--- /dev/null
+++ b/ui/imports/ui/components/breadcrumb/breadcrumb.styl
@@ -0,0 +1,3 @@
+.os-breadcrumb
+ background-color: brand-blue;
+ margin-bottom: 0px;
diff --git a/ui/imports/ui/components/breadcrumbNode/breadcrumbNode.html b/ui/imports/ui/components/breadcrumbNode/breadcrumbNode.html
new file mode 100644
index 0000000..041d2fa
--- /dev/null
+++ b/ui/imports/ui/components/breadcrumbNode/breadcrumbNode.html
@@ -0,0 +1,15 @@
+<!--
+########################################################################################
+# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others #
+# #
+# All rights reserved. This program and the accompanying materials #
+# are made available under the terms of the Apache License, Version 2.0 #
+# which accompanies this distribution, and is available at #
+# http://www.apache.org/licenses/LICENSE-2.0 #
+########################################################################################
+ -->
+<template name="breadcrumbNode">
+<li class="os-breadcrumb-node">
+ <a>{{ node.object_name }}</a>
+</li>
+</template>
diff --git a/ui/imports/ui/components/breadcrumbNode/breadcrumbNode.js b/ui/imports/ui/components/breadcrumbNode/breadcrumbNode.js
new file mode 100644
index 0000000..801df43
--- /dev/null
+++ b/ui/imports/ui/components/breadcrumbNode/breadcrumbNode.js
@@ -0,0 +1,43 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * Template Component: breadcrumbNode
+ */
+
+import { Template } from 'meteor/templating';
+import { ReactiveDict } from 'meteor/reactive-dict';
+import { SimpleSchema } from 'meteor/aldeed:simple-schema';
+
+import './breadcrumbNode.html';
+
+Template.breadcrumbNode.onCreated(function () {
+ let instance = this;
+ instance.state = new ReactiveDict();
+
+ instance.autorun(function () {
+ let data = Template.currentData();
+ new SimpleSchema({
+ node: { type: Object, blackbox: true },
+ onClick: { type: Function },
+ }).validate(data);
+ });
+
+});
+
+Template.breadcrumbNode.helpers({
+});
+
+Template.breadcrumbNode.events({
+ 'click': function(event, instance) {
+ event.stopPropagation();
+ event.preventDefault();
+
+ instance.data.onClick();
+ }
+});
diff --git a/ui/imports/ui/components/breadcrumbNode/breadcrumbNode.styl b/ui/imports/ui/components/breadcrumbNode/breadcrumbNode.styl
new file mode 100644
index 0000000..e2915d8
--- /dev/null
+++ b/ui/imports/ui/components/breadcrumbNode/breadcrumbNode.styl
@@ -0,0 +1,4 @@
+.os-breadcrumb-node
+ a
+ color: white;
+ cursor pointer
diff --git a/ui/imports/ui/components/clique-constraint/clique-constraint.html b/ui/imports/ui/components/clique-constraint/clique-constraint.html
new file mode 100644
index 0000000..0583872
--- /dev/null
+++ b/ui/imports/ui/components/clique-constraint/clique-constraint.html
@@ -0,0 +1,96 @@
+<!--
+########################################################################################
+# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others #
+# #
+# All rights reserved. This program and the accompanying materials #
+# are made available under the terms of the Apache License, Version 2.0 #
+# which accompanies this distribution, and is available at #
+# http://www.apache.org/licenses/LICENSE-2.0 #
+########################################################################################
+ -->
+<template name="CliqueConstraint">
+ <div class="os-clique-constraint cards white">
+ {{#if notificationsExists}}
+ <div class="sm-notification-panel alert alert-danger">
+ {{#each note in notifications }}
+ <div>{{ note }}</div>
+ {{/each }}
+ </div>
+ {{/if}}
+
+ <h3>{{ getState 'pageHeader' }}</h3>
+
+ <div class="sm-form-container">
+ <form role="form" class="sm-item-form form-horizontal">
+
+ <div class="sm-field-group-id cl-field-group">
+ <label class="cl-field-label">Id</label>
+ <input name="id"
+ disabled="disabled"
+ value="{{ getModelField '_id' }}"
+ class="sm-input-id cl-input" type="text" placeholder="Id" />
+ <div class="cl-field-id">Id</div>
+ </div>
+
+ <!--div class="sm-field-group-env cl-field-group">
+ <label class="cl-field-label">Environment</label>
+ <select name="env" class="sm-input-env cl-input"
+ {{ getAttrDisabled }} >
+ {{#each env in envsList }}
+ <option value="{{ env.name }}"
+ {{ getAttrSelected env.name (getModelField 'environment') }}
+ >{{ env.name }}</option>
+ {{/each }}
+ </select>
+ <div class="cl-field-desc">Environment</div>
+ </div-->
+
+ <div class="sm-field-group-focal-point-type cl-field-group">
+ <label class="cl-field-label">Focal Point Type</label>
+ <select name="focalPointType" class="sm-input-focal-point-type cl-input"
+ {{ getAttrDisabled }} >
+ {{#each objectType in objectTypesList }}
+ <option value="{{ objectType.value }}"
+ {{ getAttrSelected objectType.value (getModelField 'focal_point_type') }}
+ >{{ objectType.label }}</option>
+ {{/each }}
+ </select>
+ <div class="cl-field-desc">Focal Point Type</div>
+ </div>
+
+ <div class="sm-field-group-constraints cl-field-group">
+ <label class="cl-field-label">Constraints</label>
+ <select name="constraints"
+ class="sm-input-constraints cl-input"
+ multiple
+ size="3"
+ {{ getAttrDisabled }} >
+ {{#each objectType in objectTypesList }}
+ <option value="{{ constraint.value }}"
+ {{ getAttrSelectedMultiple objectType.value (getModelField 'constraints') }}
+ >{{ objectType.label }}</option>
+ {{/each }}
+ </select>
+ <div class="cl-field-desc">Constraints</div>
+ </div>
+
+ {{#if isUpdateableAction }}
+ <button type="submit"
+ class="js-submit-button mdl-button mdl-js-button mdl-button--raised
+ mdl-js-ripple-effect mdl-button--colored"
+ >{{ actionLabel }}</button>
+ {{/if }}
+
+ </form>
+
+ {{#if (getState 'isMessage') }}
+ <div class="js-message-panel alert {{#if (getState 'isError')}}alert-danger{{/if}}
+ {{#if (getState 'isSuccess')}}alert-success{{/if}}"
+ role="alert">
+ {{ getState 'message' }}
+ </div>
+ {{/if }}
+
+ </div>
+ </div>
+</template>
diff --git a/ui/imports/ui/components/clique-constraint/clique-constraint.js b/ui/imports/ui/components/clique-constraint/clique-constraint.js
new file mode 100644
index 0000000..75623eb
--- /dev/null
+++ b/ui/imports/ui/components/clique-constraint/clique-constraint.js
@@ -0,0 +1,329 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * Template Component: CliqueConstraint
+ */
+
+//import { Meteor } from 'meteor/meteor';
+import * as R from 'ramda';
+import { Template } from 'meteor/templating';
+import { ReactiveDict } from 'meteor/reactive-dict';
+import { SimpleSchema } from 'meteor/aldeed:simple-schema';
+import { CliqueConstraints } from '/imports/api/clique-constraints/clique-constraints';
+//import { Environments } from '/imports/api/environments/environments';
+import { Constants } from '/imports/api/constants/constants';
+import { insert, remove, update } from '/imports/api/clique-constraints/methods';
+import { parseReqId } from '/imports/lib/utilities';
+
+import './clique-constraint.html';
+
+/*
+ * Lifecycles
+ */
+
+Template.CliqueConstraint.onCreated(function() {
+ let instance = this;
+ instance.state = new ReactiveDict();
+ instance.state.setDefault({
+ id: null,
+ //env: null,
+ action: 'insert',
+ isError: false,
+ isSuccess: false,
+ isMessage: false,
+ message: null,
+ disabled: false,
+ notifications: {},
+ model: {},
+ pageHeader: 'Clique Constraint'
+ });
+
+ instance.autorun(function () {
+ let controller = Iron.controller();
+ let params = controller.getParams();
+ let query = params.query;
+
+ new SimpleSchema({
+ action: { type: String, allowedValues: ['insert', 'view', 'remove', 'update'] },
+ id: { type: String, optional: true }
+ }).validate(query);
+
+ switch (query.action) {
+ case 'insert':
+ initInsertView(instance, query);
+ break;
+
+ case 'view':
+ initViewView(instance, query);
+ break;
+
+ case 'update':
+ initUpdateView(instance, query);
+ break;
+
+ case 'remove':
+ initRemoveView(instance, query);
+ break;
+
+ default:
+ throw 'unimplemented action';
+ }
+ });
+});
+
+/*
+Template.CliqueConstraint.rendered = function() {
+};
+*/
+
+/*
+ * Events
+ */
+
+Template.CliqueConstraint.events({
+ 'submit .sm-item-form': function(event, instance) {
+ event.preventDefault();
+
+ let _id = instance.state.get('id');
+ //let env = instance.$('.sm-input-env')[0].value;
+ let focalPointType = instance.$('.sm-input-focal-point-type')[0].value;
+ let constraints = R.map(R.prop('value'),
+ instance.$('.sm-input-constraints')[0].selectedOptions);
+
+ submitItem(instance,
+ _id,
+ //env,
+ focalPointType,
+ constraints
+ );
+ }
+});
+
+/*
+ * Helpers
+ */
+
+Template.CliqueConstraint.helpers({
+ isUpdateableAction() {
+ let instance = Template.instance();
+ let action = instance.state.get('action');
+
+ return R.contains(action, ['insert', 'update', 'remove']);
+ },
+
+ getState: function (key) {
+ let instance = Template.instance();
+ return instance.state.get(key);
+ },
+
+ objectTypesList: function () {
+ return R.ifElse(R.isNil, R.always([]), R.prop('data')
+ )(Constants.findOne({ name: 'object_types_for_links' }));
+ },
+
+ /*
+ envsList: function () {
+ return Environments.find({});
+ },
+ */
+
+ getAttrDisabled: function () {
+ let instance = Template.instance();
+ let result = {};
+ let action = instance.state.get('action');
+
+ if (R.contains(action, ['view', 'remove'])) {
+ result = R.assoc('disabled', true, result);
+ }
+
+ return result;
+ },
+
+ getModel: function () {
+ let instance = Template.instance();
+ return instance.state.get('model');
+ },
+
+ getModelField: function (fieldName) {
+ let instance = Template.instance();
+ return R.path([fieldName], instance.state.get('model'));
+ },
+
+ getAttrSelected: function (optionValue, modelValue) {
+ let result = {};
+
+ if (optionValue === modelValue) {
+ result = R.assoc('selected', 'selected', result);
+ }
+
+ return result;
+ },
+
+ getAttrSelectedMultiple: function (optionValue, modelValues) {
+ let result = {};
+
+ if (R.isNil(modelValues)) { return result; }
+
+ if (R.contains(optionValue, modelValues)) {
+ result = R.assoc('selected', 'selected', result);
+ }
+
+ return result;
+ },
+
+ actionLabel: function () {
+ let instance = Template.instance();
+ let action = instance.state.get('action');
+ return calcActionLabel(action);
+ }
+});
+
+
+function initInsertView(instance, query) {
+ instance.state.set('action', query.action);
+ instance.state.set('model', CliqueConstraints.schema.clean({
+ }));
+
+ subscribeToOptionsData(instance);
+ //instance.subscribe('link_types?env', query.env);
+}
+
+function initViewView(instance, query) {
+ let reqId = parseReqId(query.id);
+
+ instance.state.set('action', query.action);
+ instance.state.set('id', reqId);
+
+ subscribeToOptionsData(instance);
+
+ instance.subscribe('clique_constraints?_id', reqId.id);
+
+ CliqueConstraints.find({ _id: reqId.id }).forEach((model) => {
+ instance.state.set('model', model);
+ });
+}
+
+function initUpdateView(instance, query) {
+ let reqId = parseReqId(query.id);
+
+ instance.state.set('action', query.action);
+ instance.state.set('id', reqId);
+
+ subscribeToOptionsData(instance);
+ instance.subscribe('clique_constraints?_id', reqId.id);
+
+ CliqueConstraints.find({ _id: reqId.id }).forEach((model) => {
+ instance.state.set('model', model);
+ });
+}
+
+function initRemoveView(instance, query) {
+ initViewView(instance, query);
+}
+
+function subscribeToOptionsData(instance) {
+// instance.subscribe('environments_config');
+ instance.subscribe('link_types');
+ instance.subscribe('constants');
+}
+
+function submitItem(
+ instance,
+ id,
+ focal_point_type,
+ constraints
+ ) {
+
+ let action = instance.state.get('action');
+
+ instance.state.set('isError', false);
+ instance.state.set('isSuccess', false);
+ instance.state.set('isMessage', false);
+ instance.state.set('message', null);
+
+ switch (action) {
+ case 'insert':
+ insert.call({
+ focal_point_type: focal_point_type,
+ constraints: constraints,
+ }, processActionResult.bind(null, instance));
+ break;
+
+ case 'update':
+ update.call({
+ _id: id.id,
+ focal_point_type: focal_point_type,
+ constraints: constraints,
+ }, processActionResult.bind(null, instance));
+ break;
+
+ case 'remove':
+ remove.call({
+ _id: id.id
+ }, processActionResult.bind(null, instance));
+ break;
+
+ default:
+ // todo
+ break;
+ }
+}
+
+function processActionResult(instance, error) {
+ let action = instance.state.get('action');
+
+ if (error) {
+ instance.state.set('isError', true);
+ instance.state.set('isSuccess', false);
+ instance.state.set('isMessage', true);
+
+ if (typeof error === 'string') {
+ instance.state.set('message', error);
+ } else {
+ instance.state.set('message', error.message);
+ }
+
+ return;
+ }
+
+ instance.state.set('isError', false);
+ instance.state.set('isSuccess', true);
+ instance.state.set('isMessage', true);
+
+ switch (action) {
+ case 'insert':
+ instance.state.set('message', 'Record had been added successfully');
+ instance.state.set('disabled', true);
+ break;
+
+ case 'remove':
+ instance.state.set('message', 'Record had been removed successfully');
+ instance.state.set('disabled', true);
+ break;
+
+ case 'update':
+ instance.state.set('message', 'Record had been updated successfully');
+ break;
+ }
+
+ Router.go('/clique-constraints-list');
+}
+
+function calcActionLabel(action) {
+ switch (action) {
+ case 'insert':
+ return 'Add';
+ case 'remove':
+ return 'Remove';
+ case 'update':
+ return 'Update';
+ default:
+ return 'Submit';
+ }
+}
diff --git a/ui/imports/ui/components/clique-constraint/clique-constraint.styl b/ui/imports/ui/components/clique-constraint/clique-constraint.styl
new file mode 100644
index 0000000..72d2348
--- /dev/null
+++ b/ui/imports/ui/components/clique-constraint/clique-constraint.styl
@@ -0,0 +1,35 @@
+.os-clique-constraint
+ margin: 20px;
+
+ .cl-field-group
+ display: flex;
+ flex-flow: row nowrap;
+ align-items: center;
+ padding: 5px 0;
+
+ .cl-field-label
+ width: 120px;
+ margin: 0 5px;
+
+ .cl-input
+ display: block;
+ width: 100%;
+ min-height: 34px;
+ padding: 6px 12px;
+ font-size: 14px;
+ line-height: 1.42857143;
+ color: #555;
+ background-color: #fff;
+ background-image: none;
+ border: 1px solid #ccc;
+ border-radius: 4px;
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+ width: 400px;
+ margin: 0 5px;
+
+ .cl-field-desc
+ margin: 0 5px;
+
+ .js-message-panel
+ margin-top: 20px;
+
diff --git a/ui/imports/ui/components/clique-constraints-list/clique-constraints-list.html b/ui/imports/ui/components/clique-constraints-list/clique-constraints-list.html
new file mode 100644
index 0000000..111c31b
--- /dev/null
+++ b/ui/imports/ui/components/clique-constraints-list/clique-constraints-list.html
@@ -0,0 +1,52 @@
+<!--
+########################################################################################
+# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others #
+# #
+# All rights reserved. This program and the accompanying materials #
+# are made available under the terms of the Apache License, Version 2.0 #
+# which accompanies this distribution, and is available at #
+# http://www.apache.org/licenses/LICENSE-2.0 #
+########################################################################################
+ -->
+<template name="CliqueConstraintsList">
+<div class="os-clique-constraints-list cards white">
+ <h3>Clique Constraints</h3>
+ <a class="sm-add-new-link"
+ href="{{pathFor route='clique-constraint' query=(asHash action='insert') }}">
+ <i class="cl-action-icon fa fa-plus" area-hidden="true"></i> Create new clique constraint
+ </a>
+ <table class="sm-clique-constraints-table table">
+ <thead>
+ <tr>
+ <th>Focal Point Type</th>
+ <th>Constraints</th>
+ <th>Actions</th>
+ </tr> </thead>
+ <tbody>
+ {{#each cliqueConstraint in cliqueConstraints }}
+ <tr>
+ <td>{{ cliqueConstraint.focal_point_type }}</td>
+ <td>{{ cliqueConstraint.constraints }}</td>
+ <td>
+ <div class="sm-action-bar">
+ <a href="{{pathFor route='clique-constraint'
+ query=(asHash id=(idToStr cliqueConstraint._id) action='view') }}"
+ ><i class="cl-action-icon fa fa-eye" area-hidden="true"></i></a>
+
+ {{#if isAuthManageCliqueConstraints }}
+ <a href="{{pathFor route='clique-constraint'
+ query=(asHash id=(idToStr cliqueConstraint._id) action='update') }}"
+ ><i class="cl-action-icon fa fa-pencil" area-hidden="true"></i></a>
+
+ <a href="{{pathFor route='clique-constraint'
+ query=(asHash id=(idToStr cliqueConstraint._id) action='remove') }}"
+ ><i class="cl-action-icon fa fa-trash-o" area-hidden="true"></i></a>
+ {{/if }}
+ </div>
+ </td>
+ </tr>
+ {{/each }}
+ </tbody>
+ </table>
+</div>
+</template>
diff --git a/ui/imports/ui/components/clique-constraints-list/clique-constraints-list.js b/ui/imports/ui/components/clique-constraints-list/clique-constraints-list.js
new file mode 100644
index 0000000..79c31e4
--- /dev/null
+++ b/ui/imports/ui/components/clique-constraints-list/clique-constraints-list.js
@@ -0,0 +1,77 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * Template Component: CliqueConstraintsList
+ */
+
+//import { Meteor } from 'meteor/meteor';
+import { Template } from 'meteor/templating';
+import { ReactiveDict } from 'meteor/reactive-dict';
+import { SimpleSchema } from 'meteor/aldeed:simple-schema';
+import { CliqueConstraints } from '/imports/api/clique-constraints/clique-constraints';
+import { Roles } from 'meteor/alanning:roles';
+
+import './clique-constraints-list.html';
+
+/*
+ * Lifecycles
+ */
+
+Template.CliqueConstraintsList.onCreated(function() {
+ var instance = this;
+
+ instance.state = new ReactiveDict();
+ instance.state.setDefault({
+ });
+
+ instance.autorun(function () {
+ //let data = Template.currentData();
+
+ var controller = Iron.controller();
+ var params = controller.getParams();
+ var query = params.query;
+
+ new SimpleSchema({
+ }).validate(query);
+
+ instance.subscribe('clique_constraints');
+ });
+});
+
+/*
+Template.CliqueConstraintsList.rendered = function() {
+};
+*/
+
+/*
+ * Events
+ */
+
+Template.CliqueConstraintsList.events({
+});
+
+/*
+ * Helpers
+ */
+
+Template.CliqueConstraintsList.helpers({
+ cliqueConstraints: function () {
+ //let instance = Template.instance();
+
+ //var env = instance.state.get('env');
+ //return Scans.find({ environment: env });
+ return CliqueConstraints.find({});
+ },
+
+ isAuthManageCliqueConstraints: function () {
+ return Roles.userIsInRole(Meteor.userId(), 'manage-clique-constraints', Roles.GLOBAL_GROUP);
+ },
+}); /// end: helpers
+
+
diff --git a/ui/imports/ui/components/clique-constraints-list/clique-constraints-list.styl b/ui/imports/ui/components/clique-constraints-list/clique-constraints-list.styl
new file mode 100644
index 0000000..9c3072c
--- /dev/null
+++ b/ui/imports/ui/components/clique-constraints-list/clique-constraints-list.styl
@@ -0,0 +1,22 @@
+.os-clique-constraints-list
+ margin: 20px;
+
+ .cl-action-icon,
+ .card.fa.cl-action-icon
+ font-size: 16px !important;
+
+ .sm-clique-constraints-table
+ th
+ color: spark-blue
+
+ .sm-action-bar
+ display: flex;
+
+ a
+ margin: 0px 5px;
+
+ .cl-action-icon
+ color: gray
+
+ .sm-add-new-link
+ color: spark-blue
diff --git a/ui/imports/ui/components/clique-type/clique-type.html b/ui/imports/ui/components/clique-type/clique-type.html
new file mode 100644
index 0000000..318fb38
--- /dev/null
+++ b/ui/imports/ui/components/clique-type/clique-type.html
@@ -0,0 +1,100 @@
+<!--
+########################################################################################
+# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others #
+# #
+# All rights reserved. This program and the accompanying materials #
+# are made available under the terms of the Apache License, Version 2.0 #
+# which accompanies this distribution, and is available at #
+# http://www.apache.org/licenses/LICENSE-2.0 #
+########################################################################################
+ -->
+<template name="CliqueType">
+ <div class="os-clique-type cards white">
+ {{#if notificationsExists}}
+ <div class="sm-notification-panel alert alert-danger">
+ {{#each note in notifications }}
+ <div>{{ note }}</div>
+ {{/each }}
+ </div>
+ {{/if}}
+
+ <h3>{{ getState 'pageHeader' }}</h3>
+
+ <div class="sm-form-container">
+ <form role="form" class="sm-item-form form-horizontal">
+
+ <div class="sm-field-group-id cl-field-group">
+ <label class="cl-field-label">Id</label>
+ <input name="id"
+ disabled="disabled"
+ value="{{ getModelField '_id' }}"
+ class="sm-input-id cl-input" type="text" placeholder="Id" />
+ <div class="cl-field-id">Id</div>
+ </div>
+
+ <div class="sm-field-group-env cl-field-group">
+ <label class="cl-field-label">Environment</label>
+ <select name="env" class="sm-input-env cl-input"
+ {{ getAttrDisabled }} >
+ <option value="" selected disabled hidden></option>
+ {{#each env in envsList }}
+ <option value="{{ env.name }}"
+ {{ getAttrSelected env.name (getModelField 'environment') }}
+ >{{ env.name }}</option>
+ {{/each }}
+ </select>
+ <div class="cl-field-desc">Environment</div>
+ </div>
+
+ <div class="sm-field-group-focal-point-type cl-field-group">
+ <label class="cl-field-label">Focal Point Type</label>
+ <select name="focalPointType" class="sm-input-focal-point-type cl-input"
+ {{ getAttrDisabled }} >
+ {{#each objectType in objectTypesList }}
+ <option value="{{ objectType.value }}"
+ {{ getAttrSelected objectType.value (getModelField 'focal_point_type') }}
+ >{{ objectType.label }}</option>
+ {{/each }}
+ </select>
+ <div class="cl-field-desc">Focal Point Type</div>
+ </div>
+
+ <div class="sm-field-group-link-types cl-field-group">
+ <label class="cl-field-label">Link Types</label>
+ {{#if (getModelField 'link_types') }}
+ {{>SelectableOrderedInput (argsLinkTypesInput linkTypesList (getModelField 'link_types')) }}
+ {{/if }}
+ <div class="cl-field-desc">Link Types</div>
+ </div>
+
+ <div class="sm-field-group-name cl-field-group">
+ <label class="cl-field-label">Name</label>
+ <input name="name"
+ {{ getAttrDisabled }}
+ value="{{ getModelField 'name' }}"
+ class="sm-input-name cl-input"
+ type="text"
+ placeholder="Name" />
+ <div class="cl-field-desc">Name</div>
+ </div>
+
+ {{#if isUpdateableAction }}
+ <button type="submit"
+ class="js-submit-button mdl-button mdl-js-button mdl-button--raised
+ mdl-js-ripple-effect mdl-button--colored"
+ >{{ actionLabel }}</button>
+ {{/if }}
+
+ </form>
+
+ {{#if (getState 'isMessage') }}
+ <div class="js-message-panel alert {{#if (getState 'isError')}}alert-danger{{/if}}
+ {{#if (getState 'isSuccess')}}alert-success{{/if}}"
+ role="alert">
+ {{ getState 'message' }}
+ </div>
+ {{/if }}
+
+ </div>
+ </div>
+</template>
diff --git a/ui/imports/ui/components/clique-type/clique-type.js b/ui/imports/ui/components/clique-type/clique-type.js
new file mode 100644
index 0000000..31efc88
--- /dev/null
+++ b/ui/imports/ui/components/clique-type/clique-type.js
@@ -0,0 +1,371 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * Template Component: CliqueType
+ */
+
+//import { Meteor } from 'meteor/meteor';
+import * as R from 'ramda';
+import { Template } from 'meteor/templating';
+import { ReactiveDict } from 'meteor/reactive-dict';
+import { SimpleSchema } from 'meteor/aldeed:simple-schema';
+//import { Constants } from '/imports/api/constants/constants';
+import { CliqueTypes } from '/imports/api/clique-types/clique-types';
+import { Environments } from '/imports/api/environments/environments';
+import { Constants } from '/imports/api/constants/constants';
+import { LinkTypes } from '/imports/api/link-types/link-types';
+import { insert, update, remove } from '/imports/api/clique-types/methods';
+import { parseReqId } from '/imports/lib/utilities';
+
+import '/imports/ui/components/selectable-ordered-input/selectable-ordered-input';
+
+import './clique-type.html';
+
+/*
+ * Lifecycles
+ */
+
+Template.CliqueType.onCreated(function() {
+ let instance = this;
+ instance.state = new ReactiveDict();
+ instance.state.setDefault({
+ id: null,
+ //env: null,
+ action: 'insert',
+ isError: false,
+ isSuccess: false,
+ isMessage: false,
+ message: null,
+ disabled: false,
+ notifications: {},
+ model: {},
+ pageHeader: 'Clique Type'
+ });
+
+ instance.autorun(function () {
+ let controller = Iron.controller();
+ let params = controller.getParams();
+ let query = params.query;
+
+ new SimpleSchema({
+ action: { type: String, allowedValues: ['insert', 'view', 'update', 'remove'] },
+ env: { type: String, optional: true },
+ id: { type: String, optional: true }
+ }).validate(query);
+
+ switch (query.action) {
+ case 'insert':
+ initInsertView(instance, query);
+ break;
+
+ case 'view':
+ initViewView(instance, query);
+ break;
+
+ case 'update':
+ initUpdateView(instance, query);
+ break;
+
+ case 'remove':
+ initRemoveView(instance, query);
+ break;
+
+ default:
+ throw 'unimplemented action';
+ }
+ });
+});
+
+/*
+Template.CliqueType.rendered = function() {
+};
+*/
+
+/*
+ * Events
+ */
+
+Template.CliqueType.events({
+ 'submit .sm-item-form': function(event, instance) {
+ event.preventDefault();
+
+ let _id = instance.state.get('id');
+ let env = instance.$('.sm-input-env')[0].value;
+ let focalPointType = instance.$('.sm-input-focal-point-type')[0].value;
+ let linkTypes = R.path(['link_types'], instance.state.get('model'));
+ let name = instance.$('.sm-input-name')[0].value;
+
+ submitItem(instance,
+ _id,
+ env,
+ focalPointType,
+ linkTypes,
+ name
+ );
+ }
+});
+
+/*
+ * Helpers
+ */
+
+Template.CliqueType.helpers({
+ isUpdateableAction() {
+ let instance = Template.instance();
+ let action = instance.state.get('action');
+
+ return R.contains(action, ['insert', 'update', 'remove']);
+ },
+
+ getState: function (key) {
+ let instance = Template.instance();
+ return instance.state.get(key);
+ },
+
+ objectTypesList: function () {
+ return R.ifElse(R.isNil, R.always([]), R.prop('data')
+ )(Constants.findOne({ name: 'object_types_for_links' }));
+ },
+
+ linkTypesList: function () {
+ return LinkTypes.find({});
+ },
+
+ envsList: function () {
+ return Environments.find({});
+ },
+
+ getAttrDisabled: function () {
+ let instance = Template.instance();
+ let result = {};
+ let action = instance.state.get('action');
+
+ if (R.contains(action, ['view', 'remove'])) {
+ result = R.assoc('disabled', true, result);
+ }
+
+ return result;
+ },
+
+ getModel: function () {
+ let instance = Template.instance();
+ return instance.state.get('model');
+ },
+
+ getModelField: function (fieldName) {
+ let instance = Template.instance();
+ return R.path([fieldName], instance.state.get('model'));
+ },
+
+ getAttrSelected: function (optionValue, modelValue) {
+ let result = {};
+
+ if (optionValue === modelValue) {
+ result = R.assoc('selected', 'selected', result);
+ }
+
+ return result;
+ },
+
+ getAttrSelectedMultiple: function (optionValue, modelValues) {
+ let result = {};
+
+ if (R.isNil(modelValues)) { return result; }
+
+ if (R.contains(optionValue, modelValues)) {
+ result = R.assoc('selected', 'selected', result);
+ }
+
+ return result;
+ },
+
+ actionLabel: function () {
+ let instance = Template.instance();
+ let action = instance.state.get('action');
+ return calcActionLabel(action);
+ },
+
+ argsLinkTypesInput: function (linkTypesList, chosenLinkTypes) {
+ let instance = Template.instance();
+
+ let options = R.map((linkType) => {
+ return { value: linkType.type, label: linkType.type };
+ }, linkTypesList);
+
+ let product = R.map((linkTypeVal) => {
+ return { value: linkTypeVal, label: linkTypeVal };
+ }, chosenLinkTypes);
+
+ return {
+ choices: options,
+ product: product,
+ onProductChange: function (product) {
+ let model = instance.state.get('model');
+ let link_types = R.map(R.prop('value'), product);
+ model = R.assoc('link_types', link_types, model);
+ instance.state.set('model', model);
+ },
+ };
+ },
+}); // end: helpers
+
+function initInsertView(instance, query) {
+ instance.state.set('action', query.action);
+ instance.state.set('env', query.env);
+ instance.state.set('model', CliqueTypes.schema.clean({
+ environment: instance.state.get('env')
+ }));
+
+ subscribeToOptionsData(instance);
+ instance.subscribe('constants');
+ //instance.subscribe('link_types?env', query.env);
+}
+
+function initViewView(instance, query) {
+ let reqId = parseReqId(query.id);
+
+ instance.state.set('action', query.action);
+ instance.state.set('env', query.env);
+ instance.state.set('id', reqId);
+
+ subscribeToOptionsData(instance);
+ instance.subscribe('constants');
+ instance.subscribe('clique_types?_id', reqId.id);
+
+ CliqueTypes.find({ _id: reqId.id }).forEach((model) => {
+ instance.state.set('model', model);
+ });
+
+}
+
+function initUpdateView(instance, query) {
+ let reqId = parseReqId(query.id);
+
+ instance.state.set('action', query.action);
+ instance.state.set('env', query.env);
+ instance.state.set('id', reqId);
+
+ subscribeToOptionsData(instance);
+ instance.subscribe('constants');
+ instance.subscribe('clique_types?_id', reqId.id);
+
+ CliqueTypes.find({ _id: reqId.id }).forEach((model) => {
+ instance.state.set('model', model);
+ });
+}
+
+function initRemoveView(instance, query) {
+ initViewView(instance, query);
+}
+
+function subscribeToOptionsData(instance) {
+ instance.subscribe('environments_config');
+ instance.subscribe('link_types');
+}
+
+function submitItem(
+ instance,
+ id,
+ env,
+ focal_point_type,
+ link_types,
+ name
+ ) {
+
+ let action = instance.state.get('action');
+
+ instance.state.set('isError', false);
+ instance.state.set('isSuccess', false);
+ instance.state.set('isMessage', false);
+ instance.state.set('message', null);
+
+ switch (action) {
+ case 'insert':
+ insert.call({
+ environment: env,
+ focal_point_type: focal_point_type,
+ link_types: link_types,
+ name: name
+ }, processActionResult.bind(null, instance));
+ break;
+
+ case 'update':
+ update.call({
+ _id: id.id,
+ environment: env,
+ focal_point_type: focal_point_type,
+ link_types: link_types,
+ name: name
+ }, processActionResult.bind(null, instance));
+ break;
+
+ case 'remove':
+ remove.call({
+ _id: id.id
+ }, processActionResult.bind(null, instance));
+ break;
+
+ default:
+ // todo
+ break;
+ }
+}
+
+function processActionResult(instance, error) {
+ let action = instance.state.get('action');
+
+ if (error) {
+ instance.state.set('isError', true);
+ instance.state.set('isSuccess', false);
+ instance.state.set('isMessage', true);
+
+ if (typeof error === 'string') {
+ instance.state.set('message', error);
+ } else {
+ instance.state.set('message', error.message);
+ }
+
+ return;
+ }
+
+ instance.state.set('isError', false);
+ instance.state.set('isSuccess', true);
+ instance.state.set('isMessage', true);
+
+ switch (action) {
+ case 'insert':
+ instance.state.set('message', 'Record had been added successfully');
+ instance.state.set('disabled', true);
+ break;
+
+ case 'remove':
+ instance.state.set('message', 'Record had been removed successfully');
+ instance.state.set('disabled', true);
+ break;
+
+ case 'update':
+ instance.state.set('message', 'Record had been updated successfully');
+ break;
+ }
+
+ Router.go('/clique-types-list');
+}
+
+function calcActionLabel(action) {
+ switch (action) {
+ case 'insert':
+ return 'Add';
+ case 'remove':
+ return 'Remove';
+ case 'update':
+ return 'Update';
+ default:
+ return 'Submit';
+ }
+}
diff --git a/ui/imports/ui/components/clique-type/clique-type.styl b/ui/imports/ui/components/clique-type/clique-type.styl
new file mode 100644
index 0000000..11c42df
--- /dev/null
+++ b/ui/imports/ui/components/clique-type/clique-type.styl
@@ -0,0 +1,54 @@
+.os-clique-type
+ margin: 20px;
+
+ .cl-field-group
+ display: flex;
+ flex-flow: row nowrap;
+ align-items: center;
+ padding: 5px 0;
+
+ .cl-field-label
+ width: 120px;
+ margin: 0 5px;
+
+ >.cl-input
+ display: block;
+ width: 100%;
+ min-height: 34px;
+ padding: 6px 12px;
+ font-size: 14px;
+ line-height: 1.42857143;
+ color: #555;
+ background-color: #fff;
+ background-image: none;
+ border: 1px solid #ccc;
+ border-radius: 4px;
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+ width: 400px;
+ margin: 0 5px;
+
+ .cl-field-desc
+ margin: 0 5px;
+
+ .js-message-panel
+ margin-top: 20px;
+
+ .sm-field-group-link-types
+ .os-selectable-ordered-input
+ width: 400px;
+
+ .cl-input
+ display: block;
+ width: 100%;
+ min-height: 34px;
+ padding: 6px 12px;
+ font-size: 14px;
+ line-height: 1.42857143;
+ color: #555;
+ background-color: #fff;
+ background-image: none;
+ border: 1px solid #ccc;
+ border-radius: 4px;
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+ //width: 400px;
+ margin: 0 5px;
diff --git a/ui/imports/ui/components/clique-types-list/clique-types-list.html b/ui/imports/ui/components/clique-types-list/clique-types-list.html
new file mode 100644
index 0000000..e4badf9
--- /dev/null
+++ b/ui/imports/ui/components/clique-types-list/clique-types-list.html
@@ -0,0 +1,56 @@
+<!--
+########################################################################################
+# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others #
+# #
+# All rights reserved. This program and the accompanying materials #
+# are made available under the terms of the Apache License, Version 2.0 #
+# which accompanies this distribution, and is available at #
+# http://www.apache.org/licenses/LICENSE-2.0 #
+########################################################################################
+ -->
+<template name="CliqueTypesList">
+<div class="os-clique-types-list cards white">
+ <h3>Clique Types</h3>
+ <a class="sm-add-new-link"
+ href="{{pathFor route='clique-type' query=(asHash action='insert') }}">
+ <i class="cl-action-icon fa fa-plus" area-hidden="true"></i> Create new clique type
+ </a>
+ <table class="sm-clique-types-table table">
+ <thead>
+ <tr>
+ <th>name</th>
+ <th>Environment</th>
+ <th>Focal Point Type</th>
+ <th>Link Types</th>
+ <th>Actions</th>
+ </tr> </thead>
+ <tbody>
+ {{#each cliqueType in cliqueTypes }}
+ <tr>
+ <td>{{ cliqueType.name }}</td>
+ <td>{{ cliqueType.environment }}</td>
+ <td>{{ cliqueType.focal_point_type }}</td>
+ <td>{{ cliqueType.link_types }}</td>
+ <td>
+ <div class="sm-action-bar">
+ <a href="{{pathFor route='clique-type'
+ query=(asHash id=(idToStr cliqueType._id) action='view') }}"
+ ><i class="cl-action-icon fa fa-eye" area-hidden="true"></i></a>
+
+ {{#if isAuthManageCliqueTypes }}
+ <a href="{{pathFor route='clique-type'
+ query=(asHash id=(idToStr cliqueType._id) action='update') }}"
+ ><i class="cl-action-icon fa fa-pencil" area-hidden="true"></i></a>
+
+ <a href="{{pathFor route='clique-type'
+ query=(asHash id=(idToStr cliqueType._id) action='remove') }}"
+ ><i class="cl-action-icon fa fa-trash-o" area-hidden="true"></i></a>
+ {{/if }}
+ </div>
+ </td>
+ </tr>
+ {{/each }}
+ </tbody>
+ </table>
+</div>
+</template>
diff --git a/ui/imports/ui/components/clique-types-list/clique-types-list.js b/ui/imports/ui/components/clique-types-list/clique-types-list.js
new file mode 100644
index 0000000..7f3f149
--- /dev/null
+++ b/ui/imports/ui/components/clique-types-list/clique-types-list.js
@@ -0,0 +1,82 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * Template Component: CliqueTypesList
+ */
+
+//import { Meteor } from 'meteor/meteor';
+import { Template } from 'meteor/templating';
+import { ReactiveDict } from 'meteor/reactive-dict';
+import { SimpleSchema } from 'meteor/aldeed:simple-schema';
+import { CliqueTypes } from '/imports/api/clique-types/clique-types';
+import { Roles } from 'meteor/alanning:roles';
+
+import './clique-types-list.html';
+
+/*
+ * Lifecycles
+ */
+
+Template.CliqueTypesList.onCreated(function() {
+ var instance = this;
+
+ instance.state = new ReactiveDict();
+ instance.state.setDefault({
+ env: null
+ });
+
+ instance.autorun(function () {
+ //let data = Template.currentData();
+
+ var controller = Iron.controller();
+ var params = controller.getParams();
+ var query = params.query;
+
+ new SimpleSchema({
+ env: { type: String, optional: true },
+ }).validate(query);
+
+ let env = query.env;
+ instance.state.set('env', env);
+
+ instance.subscribe('clique_types?env*', env);
+ });
+});
+
+/*
+Template.CliqueTypesList.rendered = function() {
+};
+*/
+
+/*
+ * Events
+ */
+
+Template.CliqueTypesList.events({
+});
+
+/*
+ * Helpers
+ */
+
+Template.CliqueTypesList.helpers({
+ cliqueTypes: function () {
+ //let instance = Template.instance();
+
+ //var env = instance.state.get('env');
+ //return Scans.find({ environment: env });
+ return CliqueTypes.find({});
+ },
+
+ isAuthManageCliqueTypes: function () {
+ return Roles.userIsInRole(Meteor.userId(), 'manage-clique-types', Roles.GLOBAL_GROUP);
+ },
+});
+
+
diff --git a/ui/imports/ui/components/clique-types-list/clique-types-list.styl b/ui/imports/ui/components/clique-types-list/clique-types-list.styl
new file mode 100644
index 0000000..d4e08a2
--- /dev/null
+++ b/ui/imports/ui/components/clique-types-list/clique-types-list.styl
@@ -0,0 +1,22 @@
+.os-clique-types-list
+ margin: 20px;
+
+ .cl-action-icon,
+ .card.fa.cl-action-icon
+ font-size: 16px !important;
+
+ .sm-clique-types-table
+ th
+ color: spark-blue
+
+ .sm-action-bar
+ display: flex;
+
+ a
+ margin: 0px 5px;
+
+ .cl-action-icon
+ color: gray
+
+ .sm-add-new-link
+ color: spark-blue
diff --git a/ui/imports/ui/components/d3graph/d3graph.html b/ui/imports/ui/components/d3graph/d3graph.html
new file mode 100644
index 0000000..52d84b5
--- /dev/null
+++ b/ui/imports/ui/components/d3graph/d3graph.html
@@ -0,0 +1,13 @@
+<!--
+########################################################################################
+# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others #
+# #
+# All rights reserved. This program and the accompanying materials #
+# are made available under the terms of the Apache License, Version 2.0 #
+# which accompanies this distribution, and is available at #
+# http://www.apache.org/licenses/LICENSE-2.0 #
+########################################################################################
+ -->
+<template name="d3graph">
+ <div id="dgraphid"></div>
+</template>
diff --git a/ui/imports/ui/components/d3graph/d3graph.js b/ui/imports/ui/components/d3graph/d3graph.js
new file mode 100644
index 0000000..41177ed
--- /dev/null
+++ b/ui/imports/ui/components/d3graph/d3graph.js
@@ -0,0 +1,126 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * Template Component: d3graph
+ */
+
+//import { Meteor } from 'meteor/meteor';
+import * as R from 'ramda';
+import { Template } from 'meteor/templating';
+import { ReactiveDict } from 'meteor/reactive-dict';
+import { SimpleSchema } from 'meteor/aldeed:simple-schema';
+import { Inventory } from '/imports/api/inventories/inventories';
+import { Cliques } from '/imports/api/cliques/cliques.js';
+import { Links } from '/imports/api/links/links.js';
+
+import { d3Graph } from '/imports/lib/d3-graph';
+
+import './d3graph.html';
+
+/*
+ * Lifecycles
+ */
+
+Template.d3graph.onCreated(function() {
+ let instance = this;
+
+ instance.state = new ReactiveDict();
+ instance.state.setDefault({
+ id_path: null,
+ ready: false
+ });
+
+ instance.autorun(function () {
+ let data = Template.currentData();
+
+ new SimpleSchema({
+ id_path: { type: String },
+ }).validate(data);
+
+ instance.state.set('ready', false);
+ let id_path = data.id_path;
+
+ instance.subscribe('inventory?id_path', id_path);
+ instance.subscribe('attributes_for_hover_on_data');
+
+ Inventory.find({ id_path: id_path }).forEach((inventory) => {
+ instance.state.set('_id', inventory._id);
+
+ if (inventory.clique) {
+
+ if (inventory.id === 'aggregate-WebEx-RTP-SSD-Aggregate-node-24') {
+ let objId = 'node-24';
+ instance.subscribe('inventory?type+host', 'instance', objId);
+
+ } else {
+ let objId = inventory._id._str;
+ instance.subscribe('cliques?focal_point', objId);
+
+ Cliques.find({
+ focal_point: new Mongo.ObjectID(objId)
+ })
+ .forEach(
+ function (cliqueItem) {
+ instance.subscribe('links?_id-in', cliqueItem.links);
+
+ Links.find({ _id: {$in: cliqueItem.links} })
+ .forEach(function(linkItem) {
+ let idsList = [ linkItem['source'], linkItem['target'] ];
+ instance.subscribe('inventory?_id-in', idsList);
+
+ Inventory.find({ _id: { $in: idsList } })
+ .forEach(function (invItem) {
+ instance.subscribe('attributes_for_hover_on_data?type', invItem.type);
+ });
+ });
+
+ instance.state.set('ready', true);
+ });
+ }
+ }
+ });
+ });
+});
+
+Template.d3graph.rendered = function () {
+ let instance = Template.instance();
+ let element = instance.$('#dgraphid')[0];
+ d3Graph.createGraphData(element.clientWidth, element.clientHeight);
+
+ Tracker.autorun(function () {
+ var nodeId = instance.state.get('_id');
+ var ready = instance.state.get('ready');
+
+ if (! ready) { return; }
+ if(R.isNil(nodeId)) { return; }
+
+ setTimeout(() => {
+ let graphData = d3Graph.getGraphDataByClique(nodeId._str);
+ setTimeout(() => {
+ d3Graph.updateNetworkGraph(graphData);
+ }, 100);
+ }, 500);
+ });
+};
+
+/*
+ * Events
+ */
+
+Template.d3graph.events({
+});
+
+/*
+ * Helpers
+ */
+
+Template.d3graph.helpers({
+});
+
+
diff --git a/ui/imports/ui/components/d3graph/d3graph.styl b/ui/imports/ui/components/d3graph/d3graph.styl
new file mode 100644
index 0000000..27908be
--- /dev/null
+++ b/ui/imports/ui/components/d3graph/d3graph.styl
@@ -0,0 +1,12 @@
+#dgraphid
+ width: 100%;
+ height: 100%;
+
+ svg.os-d3-graph {
+ /*background-color: antiquewhite;*/
+ /*background-color: rgb(161, 183, 206);*/
+ background-color:#FDFEFF;
+ /*height:100vh; */
+ /* padding-top: 100px; */
+
+ }
diff --git a/ui/imports/ui/components/dashboard/dashboard.html b/ui/imports/ui/components/dashboard/dashboard.html
new file mode 100644
index 0000000..5781374
--- /dev/null
+++ b/ui/imports/ui/components/dashboard/dashboard.html
@@ -0,0 +1,157 @@
+<!--
+########################################################################################
+# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others #
+# #
+# All rights reserved. This program and the accompanying materials #
+# are made available under the terms of the Apache License, Version 2.0 #
+# which accompanies this distribution, and is available at #
+# http://www.apache.org/licenses/LICENSE-2.0 #
+########################################################################################
+ -->
+<template name="Dashboard">
+
+ <div class="flex-box">
+
+ <div class="flex-box-1 site-sidenav-collapse">
+ <i class="material-icons">menu</i>
+ </div>
+
+ <div class="flex-box-3 main-layout-no-nav">
+
+ <!-- this flex box separate environment cards from alerts -->
+ <div class="flex-box ">
+
+ <!-- this flex box for environment cards -->
+ <div class="flex-box-3 flex-box flex-col">
+ {{#each envItem in envList}}
+ <div class="cards-flex-col-h500 white flex-box-1 ">
+ <div class="flex-box justify-content-between">
+ <div class="flex-box-1">
+ <i class="material-icons">view_carousel</i>
+ </div>
+ <div class="flex-box-3">
+ <h3>Enviroment name: {{ envItem.name }}</h3>
+ <table class="table table-striped">
+ <tbody>
+ <tr>
+ <th>Distribution</th>
+ <td> {{ envItem.distribution }} </td>
+ </tr>
+ <tr>
+ <th>Number of regions: {{ regoinsCount (envItem.name) }}</th>
+ <td>
+ <div class="dropdown">
+ <button class="btn btn-default dropdown-toggle" type="button" id="dropdownMenu1" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
+ Select region from dropdown
+ <span class="caret"></span>
+ </button>
+ <ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
+ {{#each regionItem in regoins (envItem.name) }}
+ <li>
+ <a href="{{pathFor route='environment'
+ data=(asHash _id=(idToStr envItem._id))
+ query=(asHash
+ selectedNodeId=(idToStr regionItem._id ) ) }}"
+ >{{ regionItem.object_name }}</a>
+ </li>
+ {{/each}}
+ </ul>
+ </div>
+ </td>
+ </tr>
+ <tr>
+ <th>Number of projects: {{ projectsCount (envItem.name) }}</th>
+ <td>
+ <div class="dropdown">
+ <button class="btn btn-default dropdown-toggle" type="button" id="dropdownMenu1" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
+ Select project from dropdown
+ <span class="caret"></span>
+ </button>
+ <ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
+ {{#each projectItem in projects (envItem.name) }}
+ <li>
+ <a href="{{pathFor route='environment'
+ data=(asHash _id=(idToStr envItem._id))
+ query=(asHash selectedNodeId=(idToStr projectItem._id)) }}"
+ >{{ projectItem.object_name }}</a>
+ </li>
+ {{/each}}
+ </ul>
+ </div>
+
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h5>Enviroment summary:</h5>
+ <div class="flex-box justify-content-around">
+ <div class="cards-w300-h60 white blue-text flex-box-1">
+ <div class="flex-box ">
+ <div class="flex-box-1">
+ <i class="fa fa-desktop"></i>
+ </div>
+ <div class="flex-box-3">
+ <p>Number of instances {{ instancesCount (envItem.name)}}</p>
+ </div>
+ </div>
+ </div>
+ <div class="cards-w300-h60 white blue-text flex-box-1">
+ <div class="flex-box ">
+ <div class="flex-box-1">
+ <i class="fa fa-object-group"></i>
+ </div>
+ <div class="flex-box-3">
+ <p>Number of vServices {{ vservicesCount (envItem.name) }}</p>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="flex-box justify-content-around">
+ <div class="cards-w300-h60 white blue-text flex-box-1">
+ <div class="flex-box ">
+ <div class="flex-box-1">
+ <i class="fa fa-compress"></i>
+ </div>
+ <div class="flex-box-3">
+ <p>Number of vConnectors {{ vconnectorsCount (envItem.name) }}</p>
+ </div>
+ </div>
+ </div>
+ <div class="cards-w300-h60 white blue-text flex-box-1">
+ <div class="flex-box ">
+ <div class="flex-box-1">
+ <i class="fa fa-server" aria-hidden="true"></i>
+ </div>
+ <div class="flex-box-3">
+ <p>Number of hosts {{ hostsCount (envItem.name) }}</p>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ {{/each}} <!-- envItem in envList -->
+
+ </div> <!-- flex box for environment cards -->
+
+ <!-- this flex box for alerts cards -->
+ <div class="flex-box-1 flex-box flex-col ">
+
+ <div class="sm-messages-section">
+ {{#each messagesInfoBox in (getListMessagesInfoBox) }}
+ <div class="sm-message-box">
+ {{> MessagesInfoBox (argsMessagesInfoBox messagesInfoBox
+ (messageCount messagesInfoBox.level)) }}
+ </div>
+ {{/each }}
+ </div>
+
+ </div> <!-- flex box for alerts cards -->
+
+ </div>
+
+ </div> <!-- main-layout -->
+ </div>
+
+</template>
diff --git a/ui/imports/ui/components/dashboard/dashboard.js b/ui/imports/ui/components/dashboard/dashboard.js
new file mode 100644
index 0000000..1a72a2d
--- /dev/null
+++ b/ui/imports/ui/components/dashboard/dashboard.js
@@ -0,0 +1,222 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * Template Component: Dashboard
+ */
+
+//import * as R from 'ramda';
+import * as _ from 'lodash';
+import { Environments } from '/imports/api/environments/environments';
+import { //Messages,
+ calcIconForMessageLevel, lastMessageTimestamp, calcColorClassForMessagesInfoBox
+} from '/imports/api/messages/messages';
+import { Template } from 'meteor/templating';
+import { Inventory } from '/imports/api/inventories/inventories';
+import { Counts } from 'meteor/tmeasday:publish-counts';
+import { Counter } from 'meteor/natestrauser:publish-performant-counts';
+//import { Messages } from '/imports/api/messages/messages';
+import { store } from '/imports/ui/store/store';
+import { setMainAppSelectedEnvironment } from '/imports/ui/actions/main-app.actions';
+
+import '/imports/ui/components/messages-info-box/messages-info-box';
+
+import './dashboard.html';
+
+/*
+ * Lifecycle methods
+ */
+
+Template.Dashboard.onCreated(function () {
+ var instance = this;
+
+ instance.autorun(function () {
+ instance.subscribe('environments_config');
+
+ instance.subscribe('messages/count?level', 'info');
+ instance.subscribe('messages/count?level', 'warning');
+ instance.subscribe('messages/count?level', 'error');
+
+ Environments.find({}).forEach(function (envItem) {
+ instance.subscribe('inventory?env+type', envItem.name, 'instance');
+ instance.subscribe('inventory?env+type', envItem.name, 'vservice');
+ instance.subscribe('inventory?env+type', envItem.name, 'host');
+ instance.subscribe('inventory?env+type', envItem.name, 'vconnector');
+ instance.subscribe('inventory?env+type', envItem.name, 'project');
+ instance.subscribe('inventory?env+type', envItem.name, 'region');
+ });
+
+ store.dispatch(setMainAppSelectedEnvironment(null));
+ });
+});
+
+Template.Dashboard.rendered = function(){
+
+ /*
+ $.getScript('https://www.gstatic.com/charts/loader.js', function() {
+ google.charts.load('current', {'packages':['gauge', 'line']});
+ google.charts.setOnLoadCallback(drawLine);
+
+
+ function drawLine() {
+ var data = new google.visualization.DataTable();
+ data.addColumn('number', 'Traffic Webex');
+ data.addColumn('number', 'Traffic metapod');
+ data.addColumn('number', 'Some other Traffic');
+ data.addColumn('number', 'Some other Traffic');
+
+ data.addRows([
+ [1, 37.8, 80.8, 41.8],
+ [2, 30.9, 69.5, 32.4],
+ [3, 25.4, 57, 25.7],
+ [4, 11.7, 18.8, 32.5],
+ [5, 11.9, 25.6, 10.4],
+ [6, 68.8, 13.6, 27.7],
+ [7, 7.6, 42.3, 9.6],
+ [8, 12.3, 29.2, 10.6],
+ [9, 16.9, 42.9, 14.8]
+ ]);
+
+ var options = {
+ chart: {
+ title: 'Network traffic throughput',
+ subtitle: 'in Mbps'
+ }
+ };
+
+ var chart = new google.charts.Line(document.getElementById('curve_chart'));
+
+ chart.draw(data, options);
+ }
+ });
+
+ */
+};
+/*
+ * Helpers
+ */
+
+Template.Dashboard.helpers({
+
+ envList:function(){
+ //return Environments.find({type:'environment'});
+ return Environments.find({});
+ },
+
+ instancesCount: function (envName){
+ //return Inventory.find({environment: envName, type:'instance'}).count();
+ return Counts.get('inventory?env+type!counter?env=' +
+ envName + '&type=' + 'instance');
+ },
+
+ vservicesCount: function (envName) {
+ //return Inventory.find({environment: envName, type:'vservice'}).count();
+ return Counts.get('inventory?env+type!counter?env=' +
+ envName + '&type=' + 'vservice');
+ },
+
+ hostsCount: function (envName) {
+ //return Inventory.find({environment: envName, type:'host'}).count();
+ return Counts.get('inventory?env+type!counter?env=' +
+ envName + '&type=' + 'host');
+ },
+
+ vconnectorsCount: function(envName){
+ //return Inventory.find({environment: envName, type:'vconnector'}).count();
+ return Counts.get('inventory?env+type!counter?env=' +
+ envName + '&type=' + 'vconnector');
+ },
+
+ projectsCount: function (envName){
+ //return Inventory.find({environment: envName, type:'project'}).count();
+ return Counts.get('inventory?env+type!counter?env=' +
+ envName + '&type=' + 'project');
+ },
+
+ regoinsCount: function (envName){
+ //return Inventory.find({environment: envName, type:'region'}).count();
+ return Counts.get('inventory?env+type!counter?env=' +
+ envName + '&type=' + 'region');
+ },
+
+ regoins: function (envName) {
+ return Inventory.find({environment: envName, type:'region'});
+ },
+
+ projects: function (envName){
+ return Inventory.find({environment: envName, type:'project'});
+ },
+
+ notificationsCount: function(){
+ //return Messages.find({level:'notify'}).count();
+ return Counts.get('messages?level!counter?' +
+ 'level=' + 'notify');
+ },
+
+ warningsCount: function(){
+ //return Messages.find({level:'warn'}).count();
+ return Counts.get('messages?level!counter?' +
+ 'level=' + 'warn');
+ },
+
+ errorsCount: function(){
+ //return Messages.find({level:'error'}).count();
+ return Counts.get('messages?level!counter?' +
+ 'level=' + 'error');
+ },
+/*
+ notificationsTimestamp: function(){
+ var msgTimestamp = Messages.findOne({state:'added'},{fields: {'timestamp': 1} });
+ return msgTimestamp.timestamp;
+ },
+ warnings: function(){
+ return Messages.findOne({state:'warn'});
+ },
+ errors: function(){
+ return Messages.findOne({state:'down'});
+ },
+*/
+
+ getListMessagesInfoBox: function () {
+ return [
+ {
+ level: 'info'
+ },
+ {
+ level: 'warning'
+ },
+ {
+ level: 'error'
+ },
+ ];
+ },
+
+ messageCount: function (level) {
+ return Counter.get(`messages/count?level=${level}`);
+ },
+
+ argsMessagesInfoBox: function(boxDef, messageCount) {
+ //let instance = Template.instance();
+ let title = _.capitalize(boxDef.level);
+
+ return {
+ title: title,
+ count: messageCount,
+ lastScanTimestamp: lastMessageTimestamp(boxDef.level),
+ icon: calcIconForMessageLevel(boxDef.level),
+ colorClass: calcColorClassForMessagesInfoBox(boxDef.level),
+ onMoreDetailsReq: function () {
+ $('#messagesModalGlobal').modal('show', {
+ dataset: {
+ messageLevel: boxDef.level,
+ }
+ });
+ }
+ };
+ },
+}); // end: helpers
diff --git a/ui/imports/ui/components/dashboard/dashboard.styl b/ui/imports/ui/components/dashboard/dashboard.styl
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/ui/imports/ui/components/dashboard/dashboard.styl
diff --git a/ui/imports/ui/components/data-cubic/data-cubic.html b/ui/imports/ui/components/data-cubic/data-cubic.html
new file mode 100644
index 0000000..1849b78
--- /dev/null
+++ b/ui/imports/ui/components/data-cubic/data-cubic.html
@@ -0,0 +1,23 @@
+<!--
+########################################################################################
+# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others #
+# #
+# All rights reserved. This program and the accompanying materials #
+# are made available under the terms of the Apache License, Version 2.0 #
+# which accompanies this distribution, and is available at #
+# http://www.apache.org/licenses/LICENSE-2.0 #
+########################################################################################
+ -->
+<template name="DataCubic">
+ <div class="os-data-cubic cards-w250 cl-theme-{{ getTheme }}">
+ <div class="sm-container">
+ <div class="sm-icon-part" >
+ {{> Icon type=icon.type name=icon.name }}
+ </div>
+ <div class="sm-info-part">
+ <p>{{ header }}</p>
+ <span class="sm-data-info">{{ dataInfo }}</span>
+ </div>
+ </div>
+ </div>
+</template>
diff --git a/ui/imports/ui/components/data-cubic/data-cubic.js b/ui/imports/ui/components/data-cubic/data-cubic.js
new file mode 100644
index 0000000..ac0860a
--- /dev/null
+++ b/ui/imports/ui/components/data-cubic/data-cubic.js
@@ -0,0 +1,71 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * Template Component: DataCubic
+ */
+
+//import { Meteor } from 'meteor/meteor';
+import { Template } from 'meteor/templating';
+import { ReactiveDict } from 'meteor/reactive-dict';
+import * as R from 'ramda';
+import { SimpleSchema } from 'meteor/aldeed:simple-schema';
+import { Icon } from '/imports/lib/icon';
+
+import './data-cubic.html';
+
+/*
+ * Lifecycles
+ */
+
+Template.DataCubic.onCreated(function() {
+ var instance = this;
+
+ instance.state = new ReactiveDict();
+ instance.state.setDefault({
+ theme: null
+ });
+
+ this.autorun(() => {
+ new SimpleSchema({
+ header: { type: String },
+ dataInfo: { type: String },
+ icon: { type: Icon },
+ theme: { type: String, optional: true }
+ }).validate(Template.currentData());
+
+ let theme = Template.currentData().theme;
+ theme = R.isNil(theme) ? 'light' : theme;
+ instance.state.set('theme', theme);
+ });
+});
+
+/*
+Template.DataCubic.rendered = function() {
+};
+*/
+
+/*
+ * Events
+ */
+
+Template.DataCubic.events({
+});
+
+/*
+ * Helpers
+ */
+
+Template.DataCubic.helpers({
+ getTheme: function () {
+ let instance = Template.instance();
+ return instance.state.get('theme');
+ }
+});
+
+
diff --git a/ui/imports/ui/components/data-cubic/data-cubic.styl b/ui/imports/ui/components/data-cubic/data-cubic.styl
new file mode 100644
index 0000000..4bdb9b3
--- /dev/null
+++ b/ui/imports/ui/components/data-cubic/data-cubic.styl
@@ -0,0 +1,27 @@
+.os-data-cubic
+ display: flex;
+ flex-flow: row nowrap;
+ justify-content: center;
+
+ .sm-container
+ display: flex;
+ flex-flow: row nowrap;
+
+ .sm-icon-part
+ flex: 1
+
+ .sm-info-part
+ flex: 2
+
+ display: flex;
+ flex-flow: column nowrap
+
+ .sm-data-info
+ font-size: 12px;
+
+.os-data-cubic.cl-theme-dark
+ background-color: dk-gray1
+
+.os-data-cubic.cl-theme-light
+ color: spark-blue
+ background-color: #fff
diff --git a/ui/imports/ui/components/detailed-node-info-box/detailed-node-info-box.html b/ui/imports/ui/components/detailed-node-info-box/detailed-node-info-box.html
new file mode 100644
index 0000000..f8c7221
--- /dev/null
+++ b/ui/imports/ui/components/detailed-node-info-box/detailed-node-info-box.html
@@ -0,0 +1,53 @@
+<!--
+########################################################################################
+# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others #
+# #
+# All rights reserved. This program and the accompanying materials #
+# are made available under the terms of the Apache License, Version 2.0 #
+# which accompanies this distribution, and is available at #
+# http://www.apache.org/licenses/LICENSE-2.0 #
+########################################################################################
+ -->
+<template name="DetailedNodeInfoBox">
+<div class="os-detailed-node-info-box cards-450 white">
+ <div class="sm-icon-segment">
+ </div>
+ <div class="sm-info-segment">
+ <div class="sm-info-title">{{ node.type }} - {{#if node.objectName }}{{ node.objectName }}{{else}} {{ node.name }}{{/if}}</div>
+ <div class="sm-info-bits">
+ <div class="cl-info-bit">
+ <div class="cl-label">Environment</div>
+ <div class="cl-data"><div class="">{{ node.environment }}</div></div>
+ </div>
+ <div class="cl-info-bit">
+ <div class="cl-label">Name</div>
+ <div class="cl-data"><div class="">{{ node.name }}</div></div>
+ </div>
+ <div class="cl-info-bit">
+ <div class="cl-label">Parent Id</div>
+ <div class="cl-data"><div class="">{{ node.parent_id }}</div></div>
+ </div>
+ <div class="cl-info-bit">
+ <div class="cl-label">Host</div>
+ <div class="cl-data"><div class="">{{ node.host }}</div></div>
+ </div>
+ <div class="cl-info-bit">
+ <div class="cl-label">Last Scanned</div>
+ <div class="cl-data"><div class="">{{ node.last_scanned }}</div></div>
+ </div>
+ <div class="cl-info-bit">
+ <div class="cl-label">Configurations</div>
+ <div class="cl-data"><div class="">{{ node.configurations }}</div></div>
+ </div>
+ <div class="cl-info-bit">
+ <div class="cl-label">Agent Type</div>
+ <div class="cl-data"><div class="">{{ node.agent_type }}</div></div>
+ </div>
+ <div class="cl-info-bit">
+ <div class="cl-label">Admin State Up</div>
+ <div class="cl-data"><div class="">{{ node.admin_state_up }}</div></div>
+ </div>
+ </div>
+ </div>
+</div>
+</template>
diff --git a/ui/imports/ui/components/detailed-node-info-box/detailed-node-info-box.js b/ui/imports/ui/components/detailed-node-info-box/detailed-node-info-box.js
new file mode 100644
index 0000000..1f0917f
--- /dev/null
+++ b/ui/imports/ui/components/detailed-node-info-box/detailed-node-info-box.js
@@ -0,0 +1,57 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * Template Component: DetailedNodeInfoBox
+ */
+
+//import { Meteor } from 'meteor/meteor';
+import { Template } from 'meteor/templating';
+import { SimpleSchema } from 'meteor/aldeed:simple-schema';
+import { ReactiveDict } from 'meteor/reactive-dict';
+
+import './detailed-node-info-box.html';
+
+/*
+ * Lifecycles
+ */
+
+Template.DetailedNodeInfoBox.onCreated(function() {
+ var instance = this;
+ instance.state = new ReactiveDict();
+ instance.state.setDefault({
+ });
+
+ instance.autorun(function () {
+ let data = Template.currentData();
+ new SimpleSchema({
+ node: { type: Object, blackbox: true },
+ }).validate(data);
+ });
+});
+
+/*
+Template.DetailedNodeInfoBox.rendered = function() {
+};
+*/
+
+/*
+ * Events
+ */
+
+Template.DetailedNodeInfoBox.events({
+});
+
+/*
+ * Helpers
+ */
+
+Template.DetailedNodeInfoBox.helpers({
+});
+
+
diff --git a/ui/imports/ui/components/detailed-node-info-box/detailed-node-info-box.styl b/ui/imports/ui/components/detailed-node-info-box/detailed-node-info-box.styl
new file mode 100644
index 0000000..d51b393
--- /dev/null
+++ b/ui/imports/ui/components/detailed-node-info-box/detailed-node-info-box.styl
@@ -0,0 +1,33 @@
+.os-detailed-node-info-box
+ display: flex;
+ flex-flow: row nowrap;
+
+ .sm-icon-segment
+ flex: 0 1 70px;
+
+ .sm-info-segment
+ flex: 1;
+ display: flex;
+ flex-flow: column nowrap;
+
+ .sm-info-title
+ color: #0a9ad7;
+ font-size: 2em;
+ border-bottom: 3px solid #0a9ad7;
+ line-height: 1.5em;
+
+ .sm-info-bits
+ padding: 5px 0px;
+
+ display: flex;
+ flex-flow: column nowrap;
+
+ .cl-info-bit
+ display: flex;
+ flex-flow: row nowrap;
+
+ .cl-label
+ flex: 0 0 110px;
+ color: black;
+ font-weight: bold;
+
diff --git a/ui/imports/ui/components/env-aci-info/env-aci-info.html b/ui/imports/ui/components/env-aci-info/env-aci-info.html
new file mode 100644
index 0000000..51b7afa
--- /dev/null
+++ b/ui/imports/ui/components/env-aci-info/env-aci-info.html
@@ -0,0 +1,88 @@
+<!--
+########################################################################################
+# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others #
+# #
+# All rights reserved. This program and the accompanying materials #
+# are made available under the terms of the Apache License, Version 2.0 #
+# which accompanies this distribution, and is available at #
+# http://www.apache.org/licenses/LICENSE-2.0 #
+########################################################################################
+ -->
+<template name="EnvAciInfo">
+<div class="form-horizontal">
+ <div class="form-group">
+ <label for=""
+ class="col-sm-2 control-label"
+ >Host</label>
+
+ <div class="col-sm-3">
+ {{> InputModel(createInputArgs
+ value=model.host
+ key="host"
+ type="text"
+ disabled=disabled
+ placeholder="Host")
+ }}
+ </div>
+
+ <div class="col-sm-4">
+ <p>Some help info</p>
+ </div>
+ </div>
+
+ <div class="form-group">
+ <label for=""
+ class="col-sm-2 control-label"
+ >User</label>
+
+ <div class="col-sm-3">
+ {{> InputModel(createInputArgs
+ value=model.user
+ key="user"
+ type="text"
+ disabled=disabled
+ placeholder="User")
+ }}
+ </div>
+
+ <div class="col-sm-4">
+ <p>Some help info</p>
+ </div>
+ </div>
+
+ <div class="form-group">
+ <label for=""
+ class="col-sm-2 control-label"
+ >Password</label>
+
+ <div class="col-sm-3">
+ {{> InputModel(createInputArgs
+ value=model.pwd
+ key="pwd"
+ type="password"
+ disabled=disabled
+ placeholder="Password")
+ }}
+ </div>
+
+ <div class="col-sm-4">
+ <p>Some help info</p>
+ </div>
+ </div>
+
+ <div class="form-group">
+ <div class="col-sm-offset-2 col-sm-10">
+ <button type="button"
+ class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect mdl-button--colored toast"
+ >Test connection</button>
+ </div>
+
+ <div class="col-sm-offset-2 col-sm-10 btn-mgt-5">
+ <button type="button"
+ class="sm-next-button mdl-button mdl-js-button
+ btnNext"
+ >Next</button>
+ </div>
+ </div>
+</div>
+</template>
diff --git a/ui/imports/ui/components/env-aci-info/env-aci-info.js b/ui/imports/ui/components/env-aci-info/env-aci-info.js
new file mode 100644
index 0000000..7d93687
--- /dev/null
+++ b/ui/imports/ui/components/env-aci-info/env-aci-info.js
@@ -0,0 +1,61 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * Template Component: EnvAcinfo
+ */
+
+//import { Meteor } from 'meteor/meteor';
+import { Template } from 'meteor/templating';
+//import { ReactiveDict } from 'meteor/reactive-dict';
+import * as R from 'ramda';
+
+import { createInputArgs } from '/imports/ui/lib/input-model';
+
+import './env-aci-info.html';
+
+/*
+ * Lifecycles
+ */
+
+Template.EnvAciInfo.onCreated(function() {
+});
+
+/*
+Template.EnvAciInfo.rendered = function() {
+};
+*/
+
+/*
+ * Events
+ */
+
+Template.EnvAciInfo.events({
+ 'click .sm-next-button': function () {
+ let instance = Template.instance();
+ instance.data.onNextRequested();
+ }
+});
+
+/*
+ * Helpers
+ */
+
+Template.EnvAciInfo.helpers({
+ createInputArgs: createInputArgs,
+
+ markIfDisabled: function () {
+ let instance = Template.instance();
+ let attrs = {};
+ if (instance.data.disabled) {
+ attrs = R.assoc('disabled', true, attrs);
+ }
+
+ return attrs;
+ }
+});
diff --git a/ui/imports/ui/components/env-amqp-credentials-info/env-amqp-credentials-info.html b/ui/imports/ui/components/env-amqp-credentials-info/env-amqp-credentials-info.html
new file mode 100644
index 0000000..b6187e6
--- /dev/null
+++ b/ui/imports/ui/components/env-amqp-credentials-info/env-amqp-credentials-info.html
@@ -0,0 +1,108 @@
+<!--
+########################################################################################
+# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others #
+# #
+# All rights reserved. This program and the accompanying materials #
+# are made available under the terms of the Apache License, Version 2.0 #
+# which accompanies this distribution, and is available at #
+# http://www.apache.org/licenses/LICENSE-2.0 #
+########################################################################################
+ -->
+<template name="EnvAmqpCredentialsInfo">
+<div class="form-horizontal">
+ <!-- Host -->
+ <div class="form-group">
+
+ <label for="dbHost" class="col-sm-2
+ control-label"
+ >Host</label>
+
+ <div class="col-sm-3">
+ {{> InputModel(createInputArgs
+ value=model.host
+ key="host"
+ type="text"
+ disabled=disabled
+ placeholder="Host")
+ }}
+ </div>
+
+ <div class="col-sm-4">
+ <p>This is AMQP host</p>
+ </div>
+ </div>
+
+ <div class="form-group">
+ <label for="dbPort"
+ class="col-sm-2 control-label"
+ >port</label>
+
+ <div class="col-sm-3">
+ {{> InputModel(createInputArgs
+ value=model.port
+ key="port"
+ type="text"
+ disabled=disabled
+ placeholder="Port")
+ }}
+ </div>
+
+ <div class="col-sm-4">
+ <p>This is AMQP port</p>
+ </div>
+ </div>
+
+ <div class="form-group">
+ <label for="dbUsername"
+ class="col-sm-2 control-label"
+ >Username</label>
+
+ <div class="col-sm-3">
+ {{> InputModel(createInputArgs
+ value=model.user
+ key="user"
+ type="text"
+ disabled=disabled
+ placeholder="Username")
+ }}
+ </div>
+
+ <div class="col-sm-4">
+ <p>This is AMQP user name</p>
+ </div>
+ </div>
+
+ <div class="form-group">
+ <label for="dbPassword"
+ class="col-sm-2 control-label"
+ >Password</label>
+
+ <div class="col-sm-3">
+ {{> InputModel(createInputArgs
+ value=model.password
+ key="password"
+ type="password"
+ disabled=disabled
+ placeholder="Password")
+ }}
+ </div>
+
+ <div class="col-sm-4">
+ <p>This is AMQP password</p>
+ </div>
+ </div>
+
+ <div class="form-group">
+ <div class="col-sm-offset-2 col-sm-2">
+ <button type="button"
+ class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect mdl-button--colored toast"
+ >Test connection</button>
+ </div>
+
+ <div class="col-sm-offset-2 col-sm-10 btn-mgt-5">
+ <button type="button" class="mdl-button mdl-js-button btnNext sm-next-button">Next</button>
+ </div>
+
+ </div>
+</div>
+</template>
diff --git a/ui/imports/ui/components/env-amqp-credentials-info/env-amqp-credentials-info.js b/ui/imports/ui/components/env-amqp-credentials-info/env-amqp-credentials-info.js
new file mode 100644
index 0000000..3e1522a
--- /dev/null
+++ b/ui/imports/ui/components/env-amqp-credentials-info/env-amqp-credentials-info.js
@@ -0,0 +1,52 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * Template Component: EnvAmqpCredentialsInfo
+ */
+
+//import { Meteor } from 'meteor/meteor';
+import { Template } from 'meteor/templating';
+//import { ReactiveDict } from 'meteor/reactive-dict';
+
+import { createInputArgs } from '/imports/ui/lib/input-model';
+
+import './env-amqp-credentials-info.html';
+
+/*
+ * Lifecycles
+ */
+
+Template.EnvAmqpCredentialsInfo.onCreated(function() {
+});
+
+/*
+Template.EnvAmqpCredentialsInfo.rendered = function() {
+};
+*/
+
+/*
+ * Events
+ */
+
+Template.EnvAmqpCredentialsInfo.events({
+ 'click .sm-next-button': function () {
+ let instance = Template.instance();
+ instance.data.onNextRequested();
+ }
+});
+
+/*
+ * Helpers
+ */
+
+Template.EnvAmqpCredentialsInfo.helpers({
+ createInputArgs: createInputArgs
+});
+
+
diff --git a/ui/imports/ui/components/env-delete-modal/env-delete-modal.html b/ui/imports/ui/components/env-delete-modal/env-delete-modal.html
new file mode 100644
index 0000000..40ac5d9
--- /dev/null
+++ b/ui/imports/ui/components/env-delete-modal/env-delete-modal.html
@@ -0,0 +1,48 @@
+<!--
+########################################################################################
+# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others #
+# #
+# All rights reserved. This program and the accompanying materials #
+# are made available under the terms of the Apache License, Version 2.0 #
+# which accompanies this distribution, and is available at #
+# http://www.apache.org/licenses/LICENSE-2.0 #
+########################################################################################
+ -->
+<template name="EnvDeleteModal">
+<div class="modal fade sm-event-modals"
+ id="env-delete-modal"
+ tabindex="-1"
+ role="dialog"
+ aria-labelledby="Delete environment">
+
+ <div class="modal-dialog"
+ role="document">
+
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button"
+ class="close"
+ data-dismiss="modal"
+ aria-label="Close"
+ ><span aria-hidden="true">&times;</span>
+ </button>
+ <i class="fa fa-trash-o" aria-hidden="true"></i>
+ </div>
+
+ <div class="modal-body">
+ <h5 class="modal-title"
+ id="myModalLabel"
+ >Delete environment confirmation</h5>
+ <p>Are you sure you want to delete this environment?</p>
+ </div>
+ <div class="modal-footer">
+ <button type="button"
+ class="btn btn-default pull-left"
+ data-dismiss="modal">Cancel</button>
+ <button type="button"
+ class="sm-button-delete btn btn-primary">Delete</button>
+ </div>
+ </div>
+ </div>
+</div>
+</template>
diff --git a/ui/imports/ui/components/env-delete-modal/env-delete-modal.js b/ui/imports/ui/components/env-delete-modal/env-delete-modal.js
new file mode 100644
index 0000000..7784c4d
--- /dev/null
+++ b/ui/imports/ui/components/env-delete-modal/env-delete-modal.js
@@ -0,0 +1,55 @@
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others /
+// /
+// All rights reserved. This program and the accompanying materials /
+// are made available under the terms of the Apache License, Version 2.0 /
+// which accompanies this distribution, and is available at /
+// http://www.apache.org/licenses/LICENSE-2.0 /
+/////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * Template Component: EnvDeleteModal
+ */
+
+//import { Meteor } from 'meteor/meteor';
+import { Template } from 'meteor/templating';
+//import { ReactiveDict } from 'meteor/reactive-dict';
+import { SimpleSchema } from 'meteor/aldeed:simple-schema';
+
+import './env-delete-modal.html';
+
+/*
+ * Lifecycles
+ */
+
+Template.EnvDeleteModal.onCreated(function() {
+ this.autorun(() => {
+ new SimpleSchema({
+ onDeleteReq: { type: Function },
+ }).validate(Template.currentData());
+ });
+});
+
+/*
+Template.EnvDeleteModal.rendered = function() {
+};
+*/
+
+/*
+ * Events
+ */
+
+Template.EnvDeleteModal.events({
+ 'click .sm-button-delete': function (_event, _instance) {
+ let onDeleteReq = Template.currentData().onDeleteReq;
+ onDeleteReq();
+ }
+});
+
+/*
+ * Helpers
+ */
+
+Template.EnvDeleteModal.helpers({
+});
+
+
diff --git a/ui/imports/ui/components/env-delete-modal/env-delete-modal.styl b/ui/imports/ui/components/env-delete-modal/env-delete-modal.styl
new file mode 100644
index 0000000..5fde285
--- /dev/null
+++ b/ui/imports/ui/components/env-delete-modal/env-delete-modal.styl
@@ -0,0 +1,2 @@
+/* Set the component style here */
+// "EnvDeleteModal"
diff --git a/ui/imports/ui/components/env-form/env-form.html b/ui/imports/ui/components/env-form/env-form.html
new file mode 100644
index 0000000..a0fd3bd
--- /dev/null
+++ b/ui/imports/ui/components/env-form/env-form.html
@@ -0,0 +1,37 @@
+<!--
+########################################################################################
+# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others #
+# #
+# All rights reserved. This program and the accompanying materials #
+# are made available under the terms of the Apache License, Version 2.0 #
+# which accompanies this distribution, and is available at #
+# http://www.apache.org/licenses/LICENSE-2.0 #
+########################################################################################
+ -->
+<template name="envForm">
+
+<!-- li class dropdown -->
+<a id="dLabel" data-target="#" href="#" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
+ {{ selectedEnvName }}
+ <span class="caret"></span>
+</a>
+
+<ul class="os-env-form-dropdown-menu dropdown-menu"
+ aria-labelledby="dLabel"
+ style="color: black; padding: 10px;" >
+
+ <li><a href="{{pathFor route='wizard'}}" class="droplist">Add new environment</a></li>
+ <li class="divider"></li>
+ <li style="border-bottom: 3px solid #2196f3;">Existing environments:</li>
+ {{#each envItem in envList}}
+ <li><a class="sm-env-item envList droplist"
+ data-env-name="{{ envItem.name }}"
+ data-env-id="{{ idToStr envItem._id }}"
+ >{{envItem.name}}</a></li>
+