Mongo Scan extended
Posted: Thu Oct 15, 2020 10:55 am
Code: Select all
// Copyright (c) 2013-2019 BMC Software Inc. All rights reserved.
tpl 1.14 module tengen.MongoDB_Extended;
metadata
origin := "TKU";
tkn_name := "MongoDB - Database Detail Pattern";
tree_path := 'Structured Data Management Software', 'Nonrelational Database Management Systems', 'MongoDB', 'Extended discovery';
end metadata;
from System import Future 1.0;
from DiscoveryFunctions import DiscoveryFunctions 1.7;
pattern Server_DBDetails 4.1
'''
Pattern for discovering and modelling the MongoDB databases
This pattern triggers off one of the three MongoDB Server Software Instances.
It then proceeds to list the databases by excuting a command or listing
the contents of the directory pointed to by db_path and then
models each as a DatabaseDetail node.
Supported Platforms:
Windows
UNIX
Change History:
2014-02-18 - Publisher name changed from 10gen to MongoDB (QM001816727)
2014-06-18 - Removed duplicated 'MongoDB' wording from DatabaseDetail's type attribute. Key is unchanged (TKU-2289)
2016-05-18 - Added WiredTiger storage extension to find databases in filesystem.
2016-07-05 - Added command based discovery of databases (DRDC1-6285)
2016-07-05 - Removed searching for .wt database files in favour of the
db.adminCommand('listDatabases') command (DRDC1-6285)
2017-06-05 - Update for new Database kind
2017-11-01 - Now utilises WiredTiger storage extension to find databases from specific .wt files (DRDC1-10134)
2017-11-14 - Database nodes now attached to Replication Cluster node if host part of a cluster (DRDC1-9909)
2019-02-12 - Updated pattern to use Future.runCommand and
DiscoveryFunctions.escapePath functions
2019-03-13 - Updated UNIX command to list database files (DRDC1-12419)
'''
metadata
categories := "Nonrelational Database Management Systems";
products := "MongoDB";
publishers := "MongoDB";
prior_publishers := "10gen";
urls := "http://www.mongodb.com/";
end metadata;
overview
tags TKU, TKU_2019_03_01, tengen, MongoDB, Server;
end overview;
constants
database_detail_type := "MongoDB Database";
end constants;
triggers
on si := SoftwareInstance created, confirmed where
db_path matches regex "^(/|\w:)"
and
type in ["MongoDB Server", "MongoDB Config Server",
"MongoDB Shard Server"];
end triggers;
body
host := model.host(si);
if host.os_class = "Windows" then
sep := '\\';
else
sep := '/';
end if;
database_names := [];
database_sizes := table();
detail_node_list := [];
port := "";
serialnumber := none;
sn := none;
ran_cmdSN := "";
cmdSN := "";
if si.listening_ports then
port := number.toText(si.listening_ports[0]);
end if;
// Obtain list of databases via command execution
if si.install_root then
append := '''--eval "printjson(db.adminCommand('listDatabases'))"''';
appendSN := '''--eval "printjson(db.sumit.find(\{name:\"serialnumber\"}))"''';
if port then
append := "-port %port% %append%";
appendSN := "-port %port% %appendSN%";
log.info("appendSN'%appendSN%' looks like this");
end if;
if host.os_class = "Windows" then
cmd := '''"%si.install_root%\\bin\\mongo.exe" admin %append%''';
// *command_info_start
// command_windows := '"<install_root>\bin\mongo.exe" admin -port <port> --eval "printjson(db.adminCommand('listDatabases'))"'
// command_windows := '"<install_root>\bin\mongo.exe" admin --eval "printjson(db.adminCommand('listDatabases'))"'
// reason := "Obtain list of databases"
// when := "Only if installation root known"
// *command_info_end
ran_cmd := Future.runCommand(host, cmd, '%si.install_root%\\bin\\mongo.exe');
ran_cmdSN := Future.runCommand(host, cmd, '%si.install_root%\\bin\\mongo.exe');
log.info("Windows Host");
else
cmd := "%si.install_root%/bin/mongo admin %append%";
cmdSN := "%si.install_root%/bin/mongo admin %appendSN%";
// *command_info_start
// command_unix := '<install_root>/bin/mongo admin -port <port> --eval "printjson(db.adminCommand('listDatabases'))"'
// command_unix := '<install_root>/bin/mongo admin --eval "printjson(db.adminCommand('listDatabases'))"'
// reason := "Obtain list of databases"
// when := "Only if installation root known"
// *command_info_end
ran_cmd := Future.runCommand(host, cmd, '%si.install_root%/bin/mongo');
ran_cmdSN := Future.runCommand(host, cmdSN, '%si.install_root%/bin/mongo');
log.info("Line:113 ran_cmd = '%ran_cmd%' and ran_cmdSN = '%ran_cmdSN%'");
end if;
if (not ran_cmd or not ran_cmd.result
or not ran_cmd.result has subword "name"
or ran_cmd.result has subword "unauthorized")
and host.os_class = "UNIX" then
// Results might be:
// MongoDB shell version: 2.4.13
// connecting to: 127.0.0.1:27017/admin
// { "ok" : 0, "errmsg" : "unauthorized" }
// *command_info_start
// command_unix := 'PRIV_RUNCMD <install_root>/bin/mongo admin -port <port> --eval "printjson(db.adminCommand('listDatabases'))"'
// command_unix := 'PRIV_RUNCMD <install_root>/bin/mongo admin --eval "printjson(db.adminCommand('listDatabases'))"'
// reason := "Obtain list of databases"
// privileges := true
// when := "Only if previous non-privileged command execution failed"
// *command_info_end
ran_cmd := Future.runCommand(host, 'PRIV_RUNCMD %cmd%', '%si.install_root%/bin/mongo');
ran_cmdSN := Future.runCommand(host, 'PRIV_RUNCMD %cmdSN%', '%si.install_root%/bin/mongo');
log.info("Line:133 %ran_cmdSN%");
end if;
if ran_cmd and ran_cmd.result then
databases := text.split(ran_cmd.result, '}');
for database in databases do
database_name := regex.extract(database,
regex '"name"\s*:\s*"(\S+)"',
raw '\1');
if database_name then
list.append(database_names, database_name);
size_bytes := regex.extract(database,
regex '"sizeOnDisk"\s*:\s*(\d+)',
raw '\1');
if size_bytes then
database_sizes[database_name] := text.toNumber(size_bytes);
end if;
end if;
end for;
end if;
end if;
// dhudsky ->
sn_value := none;
if ran_cmdSN and ran_cmdSN.result then
serialnumber := text.split(ran_cmdSN.result, '}');
sn_value := regex.extract(ran_cmdSN.result,
regex '"name"\s*:\s*"(\S+)"',
raw '\1');
log.info("Got SerialNumber: %sn_value%");
end if;
log.info("Line:154 Got this result '%ran_cmdSN%'");
// dhudsky <--
// Obtain list of databases via directory listing
if not database_names then
esc_db_path := DiscoveryFunctions.escapePath(host, si.db_path);
// Search for .ns files in the database directory. It is not possible to perform a
// similar search for .wt files due to such files having strange names like
// 'collection-45-3947809740892176921.wt' or 'index-15-3947809740892176921.wt'
if host.os_class = "Windows" then
if si.__directory_per_db then
cmd := 'dir /s %esc_db_path%\\*.ns';
else
cmd := 'dir %esc_db_path%\\*.ns';
end if;
// *command_info_start
// command_windows := 'dir /s "<db_path>\*.ns"'
// command_windows := 'dir "<db_path>\*.ns"'
// when := "Only if unable to obtain databases from previous command";
// reason := "Obtain database names from directory listing"
// *command_info_end
dir_cmd := discovery.runCommand(host, cmd);
if dir_cmd and dir_cmd.result then
// The following regex would return a list such as ['eol','hrd']
database_names := regex.extractAll(dir_cmd.result,
regex "\d\s([\w\s-]+)\.ns");
end if;
else
if si.__directory_per_db then
cmd := 'ls -R %esc_db_path% | grep "\.ns"';
else
cmd := 'ls %esc_db_path% | grep "\.ns"';
end if;
// *command_info_start
// command_unix := 'ls -R "<db_path>" | grep ".ns"'
// command_unix := 'ls "<db_path>" | grep ".ns"'
// when := "Only if unable to obtain databases from previous command";
// reason := "Obtain database names from directory listing"
// *command_info_end
dir_cmd := discovery.runCommand(host, cmd);
if dir_cmd and dir_cmd.result then
// The following regex would return a list such as ['eol','hrd']
database_names := regex.extractAll(dir_cmd.result,
regex "([\w-]+)\.ns");
end if;
end if;
end if;
// If unable to extract database names from the names of .ns files
// attempt to extract them from the content of the _mdb_catalog.wt file
if not database_names then
// *filepath_info_start
// filepath_unix := "<db_path>/_mdb_catalog.wt"
// filepath_windows := "<db_path>\\_mdb_catalog.wt"
// reason := "Extract database names from content"
// when := "Only if database names not already obtained"
// *filepath_info_end
wt_file := discovery.fileGet(host, '%si.db_path%%sep%_mdb_catalog.wt');
if wt_file and wt_file.content then
database_names := regex.extractAll(wt_file.content, regex 'ns\x00\x18\x00\x00\x00([\w\-_]+)\.\w+');
end if;
end if;
// If unable to extract database names from the content of _mdb_catalog.wt file
// attempt to extract them from the content of the WiredTiger.wt file
if not database_names then
// *filepath_info_start
// filepath_unix := "<db_path>/WiredTiger.wt"
// filepath_windows := "<db_path>\\WiredTiger.wt"
// reason := "Extract database names from content"
// when := "Only if database names not already obtained"
// *filepath_info_end
wt_file := discovery.fileGet(host, '%si.db_path%%sep%WiredTiger.wt');
if wt_file and wt_file.content then
database_names := regex.extractAll(wt_file.content, regex '"ns"\s+\:\s+"([\w\-_]+)\.\w+"');
end if;
end if;
// Remove duplicated database names
if database_names then
unique_database_names := [];
for database_name in database_names do
if database_name not in unique_database_names then
list.append(unique_database_names, database_name);
end if;
end for;
database_names := unique_database_names;
end if;
// Although 'MongoDB' as vendor has already been omitted from the DatabaseDetail's type attribute,
// it is being retained in the key to avoid unnecessarily altering the same.
key_database_detail_type := "MongoDB %database_detail_type%";
// Ascertain if the host is part of a Master/Slave or Replica Set Replication Cluster
msrc_list := search(in si traverse ContainedSoftware:SoftwareContainment:SoftwareContainer:SoftwareCluster
where type = 'MongoDB Server Replication Cluster');
if size(msrc_list) = 1 then
hosting_node := msrc_list[0];
else
hosting_node := si;
end if;
for database_name in database_names do
database_detail_key := "%database_name%/%key_database_detail_type%/%hosting_node.key%";
db_detail := model.Database(name := database_name,
instance := database_name,
logical_database_type := database_detail_type,
key := database_detail_key,
type := database_detail_type);
log.info("Database modeled for database %database_name%");
if database_name in database_sizes then
db_detail.size_bytes := database_sizes[database_name];
end if;
list.append(detail_node_list, db_detail);
model.setRemovalGroup(db_detail, "MongoDB_db_detail_nodes");
end for;
/// dhudsky -- >
qresult := none;
for i in ran_cmdSN do
log.info("Line: before if ran_cmdSN '%i%'");
end for;
if not ran_cmdSN = "None" then
qresult := ran_cmdSN.serialnumber;
log.info("RESULTS: '%qresult%'");
db_detail.serial := '%qresult%';
model.addDisplayAttribute (db_detail,"serial");
end if;
log.info("cmd = '%cmd%'");
log.info("database_name = '%database_name%'");
log.info("database_names = '%database_names%'");
log.info("append = '%append%'");
log.info("appendSN = '%appendSN%'");
log.info("cmd = '%cmd%'");
log.info("cmdSN = '%cmdSN%'");
log.info("database_names = '%database_names%'");
log.info("databases = '%databases%'");
log.info("db_detail.size_bytes = '%db_detail.size_bytes%'");
log.info("dir_cmd = '%dir_cmd%'");
log.info("ran_cmd = '%ran_cmd%'");
log.info("ran_cmdSN = '%ran_cmdSN%'");
log.info("append = '%append%'");
log.info("appendSN = '%appendSN%'");
log.info("database_detail_key = '%database_detail_key%'");
log.info("database_names = '%database_names%'");
log.info("db_detail = '%db_detail%'");
log.info("db_detail.serial = '%db_detail.serial%'");
log.info("esc_db_path = '%esc_db_path%'");
log.info("hosting_node = '%hosting_node%'");
log.info("port = '%port%'");
log.info("qresult = '%qresult%'");
log.info("sep = '%sep%'");
log.info("unique_database_names = '%unique_database_names%'");
log.info("wt_file = '%wt_file%'");
log.info("qresult = '%qresult%'");
log.info("database_detail_type = '%database_detail_type%'");
log.info("database_names = '%database_names%'");
log.info("database_sizes = '%database_sizes%'");
log.info("detail_node_list = '%detail_node_list%'");
log.info("host = '%host%'");
log.info("key_database_detail_type = '%key_database_detail_type%'");
log.info("msrc_list = '%msrc_list%'");
log.info("on si = '%si%'");
log.info("port = '%port%'");
/// < -- dhudsky
// Creating relationship with the SI/Cluster to all DB Detail nodes together
// This automatically deletes the relationships with details which are absent
containment_rels := model.uniquerel.Detail(ElementWithDetail := hosting_node,
Detail := detail_node_list,
type := database_detail_type);
end body;
end pattern;