Skip to content

Commit

Permalink
Merge pull request #1 from RomanNyschuk/master
Browse files Browse the repository at this point in the history
Add relationship mapping.
  • Loading branch information
Marco-Device42 committed Jun 14, 2019
2 parents e1cc4b3 + 82e69fa commit 4370543
Show file tree
Hide file tree
Showing 3 changed files with 281 additions and 26 deletions.
183 changes: 173 additions & 10 deletions d42_sd_sync.py
Expand Up @@ -90,17 +90,23 @@ def get_map_value_from_device42(source, map_info, b_add=False, asset_type_id=Non
if "@target-foregin-key" in map_info:
value = freshservice.get_id_by_name(map_info["@target-foregin"], d42_value)
if b_add and value is None and "@not-null" in map_info and map_info[
"@not-null"] and "@required" in map_info and map_info["@required"]:
name = d42_value
id = freshservice.insert_and_get_id_by_name(map_info["@target-foregin"], name, asset_type_id)
d42_value = id
"@not-null"]: # and "@required" in map_info and map_info["@required"]
if d42_value is not None:
name = d42_value
if map_info["@target-foregin"] == "vendors":
new_id = freshservice.insert_and_get_id_by_name(map_info["@target-foregin"], name, None)
else:
new_id = freshservice.insert_and_get_id_by_name(map_info["@target-foregin"], name, asset_type_id)
d42_value = new_id
else:
d42_value = None
else:
d42_value = value

return d42_value


def update_objects_from_server(sources, _target, mapping, doql=False):
def update_objects_from_server(sources, _target, mapping):
global freshservice

logger.info("Getting all existing devices in FS.")
Expand Down Expand Up @@ -142,9 +148,12 @@ def update_objects_from_server(sources, _target, mapping, doql=False):
is_valid = True
if value is not None and "@min-length" in map_info and len(value) < map_info["@min-length"]:
is_valid = False
if value == "" and "@set-space" in map_info and map_info["@set-space"]:
is_valid = True
value = " " * map_info["@min-length"]
if value is None and "@not-null" in map_info and map_info["@not-null"]:
is_valid = False
if not is_valid and "@required" in map_info and map_info["@required"]:
if not is_valid and "@target-foregin-key" in map_info:
value = get_map_value_from_device42(source, map_info, True, data["asset_type_id"])
if value is not None:
is_valid = True
Expand Down Expand Up @@ -203,6 +212,149 @@ def delete_objects_from_server(sources, _target, mapping):
logger.exception("Error (%s) deleting device %s" % (type(e), existing_object["name"]))


def create_relationships_from_affinity_group(sources, _target, mapping):
global freshservice

logger.info("Getting all existing devices in FS.")
existing_objects = freshservice.request("api/v2/assets" + "?include=type_fields", "GET", _target["@model"])

logger.info("finished getting all existing devices in FS.")

logger.info("Getting relationship type in FS.")
relationship_type = freshservice.get_relationship_type_by_content(mapping["@forward-relationship"],
mapping["@backward-relationship"])
logger.info("finished getting relationship type in FS.")
if relationship_type is None:
logger.info("There is not relationship type in FS. (%s - %s)" % (
mapping["@forward-relationship"], mapping["@backward-relationship"]))
return

for source in sources:
try:
logger.info("Processing %s - %s." % (source[mapping["@key"]], source[mapping["@target-key"]]))
primary_asset = find_object_by_name(existing_objects, source[mapping["@key"]])
secondary_asset = find_object_by_name(existing_objects, source[mapping["@target-key"]])

if primary_asset is None:
logger.info("There is no dependent asset(%s) in FS." % source[mapping["@key"]])
continue

if secondary_asset is None:
logger.info("There is no dependency asset(%s) in FS." % source[mapping["@target-key"]])
continue

relationships = freshservice.get_relationships_by_id(primary_asset["display_id"])
exist = False
for relationship in relationships:
if relationship["relationship_type_id"] == relationship_type["id"]:
if relationship["config_item"]["display_id"] == secondary_asset["display_id"]:
exist = True
break
if exist:
logger.info("There is already relationship in FS.")
continue

data = dict()
data["type"] = "config_items"
data["type_id"] = [secondary_asset["display_id"]]
data["relationship_type_id"] = relationship_type["id"]
data["relationship_type"] = "forward_relationship"
logger.info("adding relationship %s" % source[mapping["@key"]])
new_relationship_id = freshservice.insert_relationship(primary_asset["display_id"], data)
logger.info("added new relationship %d" % new_relationship_id)
except Exception as e:
logger.exception("Error (%s) creating relationship %s" % (type(e), source[mapping["@key"]]))


def delete_relationships_from_affinity_group(sources, _target, mapping):
global freshservice
logger.info("Getting all existing devices in FS.")
existing_objects = freshservice.request("api/v2/assets" + "?include=type_fields", "GET", _target["@model"])

logger.info("finished getting all existing devices in FS.")

logger.info("Getting relationship type in FS.")
relationship_type = freshservice.get_relationship_type_by_content(mapping["@forward-relationship"],
mapping["@backward-relationship"])
logger.info("finished getting relationship type in FS.")
if relationship_type is None:
logger.info("There is not relationship type in FS. (%s - %s)" % (
mapping["@forward-relationship"], mapping["@backward-relationship"]))
return

for source in sources:
try:
logger.info("Processing %s - %s." % (source[mapping["@key"]], source[mapping["@target-key"]]))
primary_asset = find_object_by_name(existing_objects, source[mapping["@key"]])
secondary_asset = find_object_by_name(existing_objects, source[mapping["@target-key"]])

if primary_asset is None:
logger.info("There is no dependent asset(%s) in FS." % source[mapping["@key"]])
continue

if secondary_asset is None:
logger.info("There is no dependency asset(%s) in FS." % source[mapping["@target-key"]])
continue

relationships = freshservice.get_relationships_by_id(primary_asset["display_id"])
remove_relationship = None
for relationship in relationships:
if relationship["relationship_type_id"] == relationship_type["id"]:
if relationship["config_item"]["display_id"] == secondary_asset["display_id"]:
remove_relationship = relationship
break
if remove_relationship is None:
logger.info("There is not relationship in FS.")
continue

freshservice.detach_relationship(primary_asset["display_id"], remove_relationship["id"])
logger.info("detached relationship %d" % remove_relationship["id"])
except Exception as e:
logger.exception("Error (%s) creating relationship %s" % (type(e), source[mapping["@key"]]))


def create_relationships_from_business_app(sources, _target, mapping):
create_relationships_from_affinity_group(sources, _target, mapping)


def delete_relationships_from_business_app(sources, _target, mapping):
global freshservice
logger.info("Getting all existing devices in FS.")
existing_objects = freshservice.request("api/v2/assets" + "?include=type_fields", "GET", _target["@model"])

logger.info("finished getting all existing devices in FS.")

logger.info("Getting relationship type in FS.")
relationship_type = freshservice.get_relationship_type_by_content(mapping["@forward-relationship"],
mapping["@backward-relationship"])
logger.info("finished getting relationship type in FS.")
if relationship_type is None:
logger.info("There is not relationship type in FS. (%s - %s)" % (
mapping["@forward-relationship"], mapping["@backward-relationship"]))
return

for existing_object in existing_objects:
logger.info("Checking relationship of asset(%s)." % existing_object["name"])
relationships = freshservice.get_relationships_by_id(existing_object["display_id"])
for relationship in relationships:
if relationship["relationship_type_id"] == relationship_type["id"] and \
relationship["relationship_type"] == "forward_relationship":
remove_relationship = relationship
target_display_id = relationship["config_item"]["display_id"]
for source in sources:
if source[mapping["@key"]] == existing_object["name"]:
secondary_asset = find_object_by_name(existing_objects, source[mapping["@target-key"]])
if target_display_id == secondary_asset["display_id"]:
remove_relationship = None
break

if remove_relationship is None:
continue

freshservice.detach_relationship(existing_object["display_id"], remove_relationship["id"])
logger.info("detached relationship %d" % remove_relationship["id"])


def parse_config(url):
config = eTree.parse(url)
meta = config.getroot()
Expand Down Expand Up @@ -239,11 +391,22 @@ def task_execute(task, device42):
else:
sources = device42.request(source_url, method, _resource["@model"])

if "@delete" in _target and _target["@delete"]:
delete_objects_from_server(sources, _target, mapping)
return
if _type == "affinity_group":
if "@delete" in _target and _target["@delete"]:
delete_relationships_from_affinity_group(sources, _target, mapping)
else:
create_relationships_from_affinity_group(sources, _target, mapping)
elif _type == "business_app":
if "@delete" in _target and _target["@delete"]:
delete_relationships_from_business_app(sources, _target, mapping)
else:
create_relationships_from_business_app(sources, _target, mapping)
else:
if "@delete" in _target and _target["@delete"]:
delete_objects_from_server(sources, _target, mapping)
return

update_objects_from_server(sources, _target, mapping, doql=False)
update_objects_from_server(sources, _target, mapping)


def main():
Expand Down
51 changes: 45 additions & 6 deletions freshservice.py
Expand Up @@ -51,7 +51,7 @@ def _send(self, method, path, data=None):
is_getting_exist = True

if not is_getting_exist and self.last_time_call_api is not None and (
now - self.last_time_call_api).total_seconds() < self.period_call_api:
now - self.last_time_call_api).total_seconds() < self.period_call_api:
time.sleep(self.period_call_api - (now - self.last_time_call_api).total_seconds())

url = "%s/%s" % (self.base_url, path)
Expand Down Expand Up @@ -105,8 +105,8 @@ def _put(self, path, data):
path += '/'
return self._send("PUT", path, data=data)

def _delete(self, path):
return self._send("DELETE", path)
def _delete(self, path, data=None):
return self._send("DELETE", path, data)

def _log(self, message, level="DEBUG"):
if self.logger:
Expand Down Expand Up @@ -201,15 +201,18 @@ def get_id_by_name(self, model, name):
path = "/api/v2/%s" % model
models = self.request(path, "GET", model)
for model in models:
if "name" in model and model["name"] is not None and name is not None and model[
"name"].lower() == name.lower():
if "name" in model and model["name"] is not None and name is not None and \
model["name"].lower() == name.lower():
return model["id"]

return None

def insert_and_get_id_by_name(self, model, name, asset_type_id):
path = "/api/v2/%s" % model
data = {"name": name, "asset_type_id": asset_type_id}
if asset_type_id is not None:
data = {"name": name, "asset_type_id": asset_type_id}
else:
data = {"name": name}
models = self._post(path, data)
for key in models:
if model in self.server_data:
Expand Down Expand Up @@ -239,3 +242,39 @@ def request(self, source_url, method, model):
self.server_data[model] = models
return models
return []

def get_relationship_type_by_content(self, forward, backward):
path = "/cmdb/relationship_types/list.json"
relationships = None
if "relationships" in self.server_data:
relationships = self.server_data["relationships"]

if relationships is None:
relationships = self._get(path)
self.server_data["relationships"] = relationships

for relationship in relationships:
if relationship["forward_relationship"] == forward and relationship["backward_relationship"] == backward:
return relationship

return None

def get_relationships_by_id(self, asset_id):
path = "/cmdb/items/%d/relationships.json" % asset_id
result = self._get(path)
if "relationships" in result:
return result["relationships"]
return []

def insert_relationship(self, asset_id, data):
path = "/cmdb/items/%d/associate.json" % asset_id
relationships = self._post(path, data)
if len(relationships) > 0:
return relationships[0]["id"]

return -1

def detach_relationship(self, asset_id, relationship_id):
path = "/cmdb/items/%d/detach_relationship.json" % asset_id

return self._delete(path, {"relationship_id": relationship_id})

0 comments on commit 4370543

Please sign in to comment.