Skip to content

Commit

Permalink
Merge pull request ceph#16360 from dillaman/wip-rbd-mirror-service-da…
Browse files Browse the repository at this point in the history
…emon

mgr/dashboard: rbd mirroring status page

Reviewed-by: John Spray <[email protected]>
  • Loading branch information
John Spray authored Jul 24, 2017
2 parents 478ba05 + a7dd68e commit 34f4125
Show file tree
Hide file tree
Showing 39 changed files with 1,769 additions and 266 deletions.
1 change: 1 addition & 0 deletions src/include/rados/librados.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ extern "C" {
#define LIBRADOS_VERSION_CODE LIBRADOS_VERSION(LIBRADOS_VER_MAJOR, LIBRADOS_VER_MINOR, LIBRADOS_VER_EXTRA)

#define LIBRADOS_SUPPORTS_WATCH 1
#define LIBRADOS_SUPPORTS_SERVICES 1

/* RADOS lock flags
* They are also defined in cls_lock_types.h. Keep them in sync!
Expand Down
3 changes: 2 additions & 1 deletion src/librbd/image/CreateRequest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,8 @@ void CreateRequest<I>::send() {
return;
}

if (!validate_layout(m_cct, m_size, m_layout)) {
if (((m_features & RBD_FEATURE_OBJECT_MAP) != 0) &&
(!validate_layout(m_cct, m_size, m_layout))) {
complete(-EINVAL);
return;
}
Expand Down
115 changes: 108 additions & 7 deletions src/pybind/mgr/dashboard/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
href="/static/AdminLTE-2.3.7/dist/css/AdminLTE.min.css">
<link rel="stylesheet"
href="/static/AdminLTE-2.3.7/dist/css/skins/skin-blue.min.css">
<link rel="stylesheet"
href="/static/AdminLTE-2.3.7/plugins/datatables/jquery.dataTables.css">

<script src="/static/AdminLTE-2.3.7/plugins/jQuery/jquery-2.2.3.min.js"></script>
<script src="/static/AdminLTE-2.3.7/plugins/sparkline/jquery.sparkline.min.js"></script>
Expand All @@ -27,6 +29,7 @@

<script src="/static/AdminLTE-2.3.7/bootstrap/js/bootstrap.min.js"></script>
<script src="/static/AdminLTE-2.3.7/dist/js/app.min.js"></script>
<script src="/static/AdminLTE-2.3.7/plugins/datatables/jquery.dataTables.min.js"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.17.1/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.4.0/Chart.min.js"></script>
Expand Down Expand Up @@ -112,12 +115,39 @@
return format_number(n, 1024, ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB']);
};

rivets.formatters.block_health_color = function(rbd_mirroring) {
if (rbd_mirroring.errors > 0) {
return "color: #ff0000";
} else if (rbd_mirroring.warnings > 0) {
return "color: #ffc200";
}
return "";
};

rivets.formatters.short_version = function(version) {
// Expect "ceph version 1.2.3-g9asdasd (as98d7a0s8d7)"
var result = /ceph version\s+([^ ]+)\s+\(.+\)/.exec(version);
if (result) {
// Return the "1.2.3-g9asdasd" part
return result[1];
} else {
// Unexpected format, pass it through
return version;
}
return
};

/* This is useful if you need to display some alternative text
* when a collection is empty using rv-hide */
rivets.formatters.length = function(val) {
return val.length;
}

rivets.formatters.hide_count_box = function(value) {
value = +value
return (isNaN(value) || value == 0)
};

rivets.bind($("#health"), toplevel_data);
rivets.bind($("section.sidebar"), toplevel_data);
setTimeout(refresh, refresh_interval);
Expand Down Expand Up @@ -203,6 +233,55 @@
font-weight: bold;
}

.dataTables_wrapper .dataTables_filter {
color: #ddd
}

.dataTables_wrapper .dataTables_filter input {
color: #222d32
}

.dataTables_wrapper tbody td {
background-color: #424d52;
color: #ddd;
}

.dataTables_wrapper .dataTables_paginate .ellipsis {
color: #ddd !important
}

.dataTables_wrapper .dataTables_paginate .paginate_button {
color: #ddd !important
}

.dataTables_wrapper .dataTables_paginate .paginate_button.disabled {
color: #424d52 !important
}

.nav-tabs-custom {
background-color: #222d32;
color: #ddd;
}
.nav-tabs-custom>.nav-tabs>li{
margin-right: 0;
}
.nav-tabs-custom>.nav-tabs>li>a{
border-bottom-color: transparent;
color: #aaa;
}
.nav-tabs-custom>.nav-tabs>li.active>a{
background-color: #424d52;
color: #eee;
}
.nav-tabs-custom>.nav-tabs>li.active:hover>a{
background-color: #66777f;
color: #eee;
}
.nav-tabs-custom>.tab-content {
background-color: #424d52;
color: #eee;
}

</style>

</head>
Expand Down Expand Up @@ -256,11 +335,13 @@
<!-- Sidebar Menu -->
<ul class="sidebar-menu">
<!-- Optionally, you can add icons to the links -->
<li><a href="/health">
<li class="{%if path_info=='/' or path_info.startswith('/health')%}active{%endif%}">
<a href="/health">
<i class="fa fa-heartbeat" rv-style="health_status | health_color"></i>
<span>Cluster health</span></a>
</li>
<li class="treeview active">
PATH: {{ path_info }}
<li class="treeview{%if path_info.startswith(('/server', '/osd'))%} active{%endif%}">
<a href="#"><i class="fa fa-server"></i> <span>Cluster</span>
<span class="pull-right-container">
<i class="fa fa-angle-left pull-right"></i>
Expand All @@ -275,20 +356,40 @@
</li>
</ul>
</li>
<li class="treeview active">
<a href="#"><i class="fa fa-hdd-o"></i> <span>Block</span>
<li class="treeview{%if path_info.startswith('/rbd')%} active{%endif%}">
<a href="#">
<i class="fa fa-hdd-o" rv-style="rbd_mirroring | block_health_color"></i> <span>Block</span>
<span class="pull-right-container">
<i class="fa fa-angle-left pull-right"></i>
</span>
</a>
<ul class="treeview-menu menu-open">
<li rv-each-pool="rbd_pools">
<a rv-href="pool.url">{pool.name}</a>
<li>
<a href="/rbd_mirroring">
<i class="fa fa-exchange"></i> Mirroring
<span class="pull-right-container">
<small rv-hide="rbd_mirroring.warnings | hide_count_box" class="label pull-right bg-yellow">{rbd_mirroring.warnings}</small>
<small rv-hide="rbd_mirroring.errors | hide_count_box" class="label pull-right bg-red">{rbd_mirroring.errors}</small>
</span>
</a>
</li>
<li class="treeview{%if path_info.startswith('/rbd_pool')%} active menu-open{%endif%}">
<a href="#">
<i class="fa fa-dot-circle-o"></i> <span>Pools</span>
<span class="pull-right-container">
<i class="fa fa-angle-left pull-right"></i>
</span>
</a>
<ul class="treeview-menu">
<li rv-each-pool="rbd_pools">
<a rv-href="pool.url"><i class="fa fa-circle-o"></i> {pool.name}</a>
</li>
</ul>
</li>
<li class="ceph-none-found" rv-hide="rbd_pools | length">None found</li>
</ul>
</li>
<li class="treeview active">
<li class="treeview{%if path_info.startswith(('/filesystem/', '/clients/'))%} active{%endif%}">
<a href="#"><i class="fa fa-folder"></i> <span>Filesystems</span>
<span class="pull-right-container">
<i class="fa fa-angle-left pull-right"></i>
Expand Down
58 changes: 50 additions & 8 deletions src/pybind/mgr/dashboard/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ def global_instance():
PgSummary, Health, MonStatus

import rados
import rbd_mirroring
from rbd_ls import RbdLs, RbdPoolLs
from cephfs_clients import CephFSClients


log = logging.getLogger("dashboard")


Expand Down Expand Up @@ -83,6 +83,9 @@ def __init__(self, *args, **kwargs):
# pools
self.rbd_pool_ls = RbdPoolLs(self)

# Stateful instance of RbdMirroring, hold cached results.
self.rbd_mirroring = rbd_mirroring.Controller(self)

# Stateful instances of CephFSClients, hold cached results. Key to
# dict is FSCID
self.cephfs_clients = {}
Expand Down Expand Up @@ -433,11 +436,16 @@ def _toplevel_data(self):
rbd_pools = sorted([
{
"name": name,
"url": "/rbd/{0}/".format(name)
"url": "/rbd_pool/{0}/".format(name)
}
for name in data
], key=lambda k: k['name'])

status, rbd_mirroring = global_instance().rbd_mirroring.toplevel.get()
if rbd_mirroring is None:
log.warning("Failed to get RBD mirroring summary")
rbd_mirroring = {}

fsmap = global_instance().get_sync_object(FsMap)
filesystems = [
{
Expand All @@ -450,6 +458,7 @@ def _toplevel_data(self):

return {
'rbd_pools': rbd_pools,
'rbd_mirroring': rbd_mirroring,
'health_status': self._health_data()['status'],
'filesystems': filesystems
}
Expand All @@ -467,6 +476,7 @@ def filesystem(self, fs_id):

return template.render(
ceph_version=global_instance().version,
path_info=cherrypy.request.path_info,
toplevel_data=json.dumps(toplevel_data, indent=2),
content_data=json.dumps(content_data, indent=2)
)
Expand Down Expand Up @@ -534,6 +544,7 @@ def clients(self, fscid_str):
template = env.get_template("clients.html")
return template.render(
ceph_version=global_instance().version,
path_info=cherrypy.request.path_info,
toplevel_data=json.dumps(self._toplevel_data(), indent=2),
content_data=json.dumps(content_data, indent=2)
)
Expand All @@ -543,7 +554,7 @@ def clients(self, fscid_str):
def clients_data(self, fs_id):
return self._clients(int(fs_id))

def _rbd(self, pool_name):
def _rbd_pool(self, pool_name):
rbd_ls = global_instance().rbd_ls.get(pool_name, None)
if rbd_ls is None:
rbd_ls = RbdLs(global_instance(), pool_name)
Expand All @@ -564,33 +575,61 @@ def wait_and_load():
return value

@cherrypy.expose
def rbd(self, pool_name):
template = env.get_template("rbd.html")
def rbd_pool(self, pool_name):
template = env.get_template("rbd_pool.html")

toplevel_data = self._toplevel_data()

images = self._rbd(pool_name)
images = self._rbd_pool(pool_name)
content_data = {
"images": images,
"pool_name": pool_name
}

return template.render(
ceph_version=global_instance().version,
path_info=cherrypy.request.path_info,
toplevel_data=json.dumps(toplevel_data, indent=2),
content_data=json.dumps(content_data, indent=2)
)

@cherrypy.expose
@cherrypy.tools.json_out()
def rbd_pool_data(self, pool_name):
return self._rbd_pool(pool_name)

def _rbd_mirroring(self):
status, data = global_instance().rbd_mirroring.content_data.get()
if data is None:
log.warning("Failed to get RBD mirroring status")
return {}
return data

@cherrypy.expose
def rbd_mirroring(self):
template = env.get_template("rbd_mirroring.html")

toplevel_data = self._toplevel_data()
content_data = self._rbd_mirroring()

return template.render(
ceph_version=global_instance().version,
path_info=cherrypy.request.path_info,
toplevel_data=json.dumps(toplevel_data, indent=2),
content_data=json.dumps(content_data, indent=2)
)

@cherrypy.expose
@cherrypy.tools.json_out()
def rbd_data(self, pool_name):
return self._rbd(pool_name)
def rbd_mirroring_data(self):
return self._rbd_mirroring()

@cherrypy.expose
def health(self):
template = env.get_template("health.html")
return template.render(
ceph_version=global_instance().version,
path_info=cherrypy.request.path_info,
toplevel_data=json.dumps(self._toplevel_data(), indent=2),
content_data=json.dumps(self._health(), indent=2)
)
Expand All @@ -600,6 +639,7 @@ def servers(self):
template = env.get_template("servers.html")
return template.render(
ceph_version=global_instance().version,
path_info=cherrypy.request.path_info,
toplevel_data=json.dumps(self._toplevel_data(), indent=2),
content_data=json.dumps(self._servers(), indent=2)
)
Expand Down Expand Up @@ -813,6 +853,7 @@ def perf(self, osd_id):

return template.render(
ceph_version=global_instance().version,
path_info='/osd' + cherrypy.request.path_info,
toplevel_data=json.dumps(toplevel_data, indent=2),
content_data=json.dumps(self._osd(osd_id), indent=2)
)
Expand Down Expand Up @@ -906,6 +947,7 @@ def index(self):

return template.render(
ceph_version=global_instance().version,
path_info='/osd' + cherrypy.request.path_info,
toplevel_data=json.dumps(toplevel_data, indent=2),
content_data=json.dumps(content_data, indent=2)
)
Expand Down
Loading

0 comments on commit 34f4125

Please sign in to comment.