diff --git a/d42_sd_sync.py b/d42_sd_sync.py
index 28f840f..129565f 100755
--- a/d42_sd_sync.py
+++ b/d42_sd_sync.py
@@ -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.")
@@ -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
@@ -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()
@@ -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():
diff --git a/freshservice.py b/freshservice.py
index e0ec786..2eba3ce 100755
--- a/freshservice.py
+++ b/freshservice.py
@@ -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)
@@ -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:
@@ -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:
@@ -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})
diff --git a/mapping.xml.sample b/mapping.xml.sample
index e549640..5434db6 100644
--- a/mapping.xml.sample
+++ b/mapping.xml.sample
@@ -1,12 +1,12 @@
+ url="{url}.freshservice.com"
+ api_key="{api_key}"/>
+ url="https://{url}"
+ user="{user}"
+ pass="{pass}"/>
@@ -16,7 +16,7 @@
asset-type="Server"/>
@@ -43,6 +43,11 @@
+
+
-
-
@@ -82,8 +87,56 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
\ No newline at end of file