-
Notifications
You must be signed in to change notification settings - Fork 2
Feat/filter control blocks #20
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Conversation
* Feature/includeparameter (#14) * Add skeleton for inclusion list parameter Extend interface/skeleton for include_list * Input POinter Attempt to add input pointer parameter for control blocks * Definitions outside of if statements * Attempt to map the intended inclusion list * Try Virtual Function to keep Control Block in Explorer * No longer have get return * Attempt to add the meat of the function -Add methods and members to control blocks to get name of control block, specific control blocks within it, and all control blocks within it -add functions in control block explorer to parse inclusion "map" into actionable data name fix * Update psa.cpp Update psa.cpp * Update cvt.cpp * Update asvt.cpp * algorithm cleanup * better cleanup * Update control_block.cpp * Update main.cpp * Finish addressing merge issues and commit hooks -Minor code updates that were lost in merge commit -Bring new code up to standard for cppcheck -Format new code with clang-format * Update _cbxp.c * Streamline Control Block Explorer Class Update control_block_explorer.hpp * Massive refactor -Shave 2 step process down to one -Change serialized json inclusion map to use a vector of strings still split by "dot" operators * Error Handling Logic * BIG UPDATE PR COMMENTS -Switch pre-processing to one hash map function -use try/catch with custom errors rather than passing return codes everywhere -style and name changes -Enforce more rigid parm structure on entry -Fix some behavioral bugs and oversights in inclusion preprocessing -General streamlining and refactoring of functions, methods, classes, etc. * Another Big Refactor -PR comments (mostly style, but streamlining of error code as well) -Reworked base and derived classes to allow for includables to be defined to the base class and include_map to be defined to the base and derived classes * Update ascb.cpp * Update control_block.hpp * . * .. * ... * PR Comments -ASCB pointer deref in ASVT -Minor name changes -Remove double wildcard error -Add control_block_name_ private member and add initialization to constructor -move include_map_ to protected and remove private using statement * Update asvt.cpp * Update asvt.cpp * PR Comments Mostly renaming things streamlining some unnecessary text, parms and strings * Update control_block.cpp * Update main.cpp * Last round of PR comments string compare with == remove vestiges of old mechanisms for control block management name changes minor tweaks * Update cvt.cpp * Update cvt.cpp * Update cvt.cpp * Final comments Update control_block_explorer.cpp * comments * Last Comments * include changes * Last round of comments * debug * Unit testing (#17) * initial commit 1 * cleaned code before include test cases * wrote test cases, need to check with team now * added space after every function * added .value * shell script done * made changes proposed by leonard 1 * PR changes requested by team * added tests to check for ascb and asvt entries whether it be a string or dict * added tests to check for ascb and asvt entries whether it be a string or dict one more place * made minor tweaks * added updates provided by leonard * grouped failure test cases together * grouped error test cases together * removed extra lines * style changes * Feat/oss housekeeping2 (#18) * Set explicit C/C++ standard and cleanup README. Signed-off-by: Leonard Carcaramo <[email protected]> * Update contribution guidelines and functional tests. Signed-off-by: Leonard Carcaramo <[email protected]> * Cleanup contribution guidelines and debug debug mode. Signed-off-by: Leonard Carcaramo <[email protected]> * Cleanup. Signed-off-by: Leonard Carcaramo <[email protected]> * Cleanup. Signed-off-by: Leonard Carcaramo <[email protected]> * Fix sdist packaging and pyproject.toml metadata. Signed-off-by: Leonard Carcaramo <[email protected]> --------- Signed-off-by: Leonard Carcaramo <[email protected]> * Fix _C.pyi and removed unused import from cbxp.py. Signed-off-by: Leonard Carcaramo <[email protected]> --------- Signed-off-by: Leonard Carcaramo <[email protected]> Co-authored-by: Elijah Swift <[email protected]> Co-authored-by: Varun Chennamadhava <[email protected]>
|
DCO signoff seems to be missing on all commits. Maybe consider squashing all of these commits into one commit that has a DCO signoff? |
| void processAsteriskInclude(); | ||
| void processExplicitInclude(std::string& include); | ||
|
|
||
| protected: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should restrict filters to only "repeated" control blocks. Maybe we can have a boolean class attribute to keep track of this?
const bool repeated_;There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we? What if we have a script design to regularly run and throw flags if a certain value is found in a singular control block like the PSA? I see a much STRONGER benefit to using them in repeated control blocks, but I'm not convinced that they should be DISALLOWED from others.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the use case for singleton control blocks is completely separate from the concept of filtering, which is meant to filter items in an array or list of items. If somebody wants to check the value of something in a singleton control block they can just do a comparison.
if psa["psapsa"] == "PSA ":
// do somethingThe point of filtering is saving the user from having to manually construct their own logic for looping through and filtering repeated control block data rather than checking if a value in a single control block meets some condition.
| } else if (cbxp_result->result_json_length == null_length && | ||
| filter_key != "") { | ||
| std::cerr << "No control block was found that matched the provided filter" | ||
| << std::endl; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should no matches result in an error or should we simply just return an empty JSON string and then the user can decide what to do with it from there?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I thought that returning an empty JSON with no context might somehow happen inadvertently in some other edge or error case. This is why I had it return an empty JSON, but have the CLI and Python interfaces treat it like an "error" anyway, rather than flagging this as an error in the main code. I am open to changes here, though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Based on how things work in jq, I am leaning towards, not giving an error, but instead just returning null or None in Python.
$ ./dist/cbxp -i cvt psa | jq '.junk.junk'
nullSigned-off-by: Elijah Swift <[email protected]>
0d354a7 to
954478d
Compare
Signed-off-by: Elijah Swift <[email protected]>
Signed-off-by: Elijah Swift <[email protected]>
Signed-off-by: Elijah Swift <[email protected]>
| run_with_expected_exit_code 255 ./dist/cbxp -d -d psa | ||
| run_with_expected_exit_code 255 ./dist/cbxp -f psa | ||
| run_with_expected_exit_code 255 ./dist/cbxp -f psapsa=psa | ||
| run_with_expected_exit_code 55 ./dist/cbxp --debug -d psa |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change 55 to 255.
| const cbxp_result_t* cbxp(const char* control_block_name, | ||
| const char* includes_string, bool debug) { | ||
| cbxp_result_t* cbxp(const char* control_block_name, const char* includes_string, | ||
| const char* filter_string, bool debug) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change to filters_string since we can have multiple like with includes_string.
| std::vector<std::string> ControlBlockExplorer::createIncludeList( | ||
| const std::string& includes_string) { | ||
| if (includes_string == "") { | ||
| std::vector<std::string> ControlBlockExplorer::createList( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change to createOptionsList() to better indicate that this is used to process an options list (i.e., filters or include patterns). We should also change variable names and debug messages accordingly to make this more clear.
| #include "logger.hpp" | ||
|
|
||
| namespace CBXP { | ||
| void ControlBlock::createFilterMap(const std::vector<std::string>& filters) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we have one "options map" that contains both the filters and include patterns?
We could maybe have a cbxp_options_t struct in the control_block.hpp header.
typedef struct {
std::vector<std::string> include_patterns;
std::vector<std::string> filters;
} cbxp_options_t;Then we can build a class attribute options_map_ with the following structure:
std::unordered_map<std::string, cbxp_options_t> options_map_;Then the logic of ControlBlock::createFilterMap() and ControlBlock::createIncludeMap() can be mostly left the same with the only exception being that they populate the same options_map_ structure. We can then have one protected ControlBlock::createOptionsMap() function that calls ControlBlock::createIncludeMap() and ControlBlock::createFilterMap() to build the options_map_ structure. Those two functions can then be made private.
Now when we need to loop through these options, we can do something like this. A benefit of this is that we no longer be at risk of running into a scenario where "assb" for example is present in the include map, but somehow not present in the filters map, and overall I think this would enable better encapsulation as well.
for (const auto& [include, cbxp_options] : options_map_) {
if (include == "assb") {
ascb_json["ascbassb"] = CBXP::ASSB(cbxp_options)
.get(p_ascb->ascbassb);
if (ascb_json["ascbassb"].is_null()) {
return {};
}
}|
|
||
| bool ControlBlock::testValue(const nlohmann::json& json_value, | ||
| const std::string& filter_value, | ||
| const std::string& operation) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There currently seems to be a bug with the string comparison logic here. If I try to use a filters like the following, I always get an empty list back:
./dist/cbxp -i assb -f 'assb.assbjbni=JOB ' ascb
./dist/cbxp -i assb -f assb.assbjbni=JOB ascb
./dist/cbxp -f 'assbjbni=JOB ' assbThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I figured it out. The problem is that the match function converts everything to lowercase. I think we should avoid doing that because we may end up running into data that is case sensitive at some point.
| try { | ||
| try { | ||
| int value_int = json_value.get<int>(); | ||
| int filter_int = std::stoi(filter_value); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How long can an integer value be? My guess is that we can have integers longer than 32-bits. Given this, we should probably treat everything as int64_t for both the try an except. I don't know if we should try to handle figure out whether we are working with a signed or unsigned value, but maybe for now treating everything as uint64_t makes sense?
| filter_value = filter.substr(del_pos + del.length()); | ||
| filter_key = filter.substr(0, del_pos); | ||
| current_filters_[filter_key].push_back(filter_value); | ||
| current_filters_[filter_key].push_back(del); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it make sense to create a cbxp_filter_t struct here to split the operation and value?
typedef struct {
std::string operation;
std::string value;
} cbxp_filter_t;| Logger::getInstance().debug("Done"); | ||
| } | ||
|
|
||
| void ControlBlock::processFilterValue(const std::string& filter) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Call this ControlBlock:addCurrentFilter() for better clarity since this function adds items to the current_filters_ map?
| cbdata = cbxp( | ||
| "psa", | ||
| control_block_filter=[ | ||
| "cvt.asvt.ascb.assb.assbjbns=*master*,cvt.asvt.ascb.ascbasid>0", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't we be requiring each filter to be its own string and not allow filters to contain commas like include patterns?
| def test_cbxp_can_use_ulong_filter_with_greater_than(self): | ||
| cbdata = cbxp( | ||
| "cvt", | ||
| control_block_filter=["cvtasmvt>87FFFFFF"], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we are doing a hex comparison, we should require the value to be prepended with 0x to indicate that the value is a hex value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think a better way of describing these tests would be tests for hex comparisons, no?
| reinterpret_cast<struct ascb* __ptr32>(*p_ascb_addr); | ||
| assbs.push_back( | ||
| ASSB::get(reinterpret_cast<void* __ptr32>(p_ascb->ascbassb))); | ||
| nlohmann::json new_assb = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rename to next_assb and use same naming convention for other repeated control blocks.
| void processAsteriskInclude(); | ||
| void processExplicitInclude(std::string& include); | ||
| void processFilterValue(const std::string& filter); | ||
| bool testValue(const nlohmann::json& json_value, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rename this to compare()?
💡 Issue Reference
Issue: #5
💻 What does this address?
Control block filtering is a valuable enhancement. In basic cases it allows for simple validation that a control block containing the right data is collected, but in more complex cases, it allows users to grab information on just a singular relevant control block or collection of them in a large table.
📟 Implementation Details
The core control block class now has a new unordered map for filtration that is built on object creation. The filter is passed from block to block along with inclusion patterns and validated along the way. At the "end" of the line, the control block's value is validated against that of the filter. In the event of a match, the json data is returned, otherwise an empty json comes back. Large lists of control blocks like ASCB's and ASSB's were edited to filter out these null values, allowing for a true filter.
📋 Is there a test case?
I added the following tests in both the python and the shell test suites (the names in python are fare more descriptive)
test_cbxp_raises_cbxp_error_if_filter_uses_non_included_control_block
test_cbxp_raises_cbxp_error_if_filter_uses_unknown_key
test_cbxp_raises_cbxp_error_if_filter_dict_wrong_size (python only)
test_cbxp_raises_cbxp_error_if_filter_value_not_string (python only)
test_cbxp_raises_cbxp_error_if_no_filter_match
test_cbxp_can_use_basic_filter
test_cbxp_can_use_include_filter_with_generic_include
test_cbxp_can_use_include_filter_with_discrete_include