Alon Bar-Lev has posted comments on this change.

Change subject: fklsnr: Introduce standalone fence_kdump listener
......................................................................


Patch Set 5:

(27 comments)

thank you so much!

now I can understand the flow, implementation, termination and flow is much 
more simple.

first pass of comments, maybe I did not understand certain things.

the major issue I do not understand is why do we need to open a new database 
connection each time we use database and not reuse connection.

http://gerrit.ovirt.org/#/c/27201/5/ovirt-engine.spec.in
File ovirt-engine.spec.in:

Line 295: 
Line 296: %description backend
Line 297: The backend engine of %{ovirt_product_name_short}
Line 298: 
Line 299: %package fence-kdump-listener
any reason we want this to run on different host? if not, maybe better to merge 
this with tools as notifier.
Line 300: Summary:      %{ovirt_product_name_short} fence_kdump listener
Line 301: Group:                %{ovirt_product_group}
Line 302: Requires:     %{name}-lib >= %{version}-%{release}
Line 303: Requires:     python-dateutil


Line 936: %ghost %config(noreplace) %{engine_pki}/serial.txt
Line 937: 
Line 938: %files fence-kdump-listener
Line 939: 
Line 940: %{engine_data}/services/ovirt-fence-kdump-listener
please add / at suffix
Line 941: %{engine_etc}/ovirt-fence-kdump-listener.conf.d/
Line 942: 
Line 943: %if %{ovirt_install_systemd}
Line 944: %{_unitdir}/ovirt-fence-kdump-listener.service


http://gerrit.ovirt.org/#/c/27201/5/packaging/services/ovirt-fence-kdump-listener/db.py
File packaging/services/ovirt-fence-kdump-listener/db.py:

Line 114: 
Line 115:     def load_config_values(self):
Line 116:         config_values = {}
Line 117: 
Line 118:         with contextlib.closing(self.connect()) as connection:
please be consistent... if you have connect() then all functions should use 
pre-opened connection.
Line 119:             for name in (
Line 120:                 'FenceKdumpListenerHost',
Line 121:                 'FenceKdumpListenerPort',
Line 122:                 'FenceKdumpListenerHeartbeatInterval',


Line 149:             config_values,
Line 150:         )
Line 151:         return config_values
Line 152: 
Line 153:     def create_fence_kdump_message(self, connection, host, received, 
status):
better to have dao class that holds connection and has all these methods.

 with DbManager.connect() as dao:
     use dao.

drop the use of contextlib...

connect will return:

 new FenceDao(connection)

or similar.

then you separate between the lowlevel connection open, execute and application 
specific dao, and remove the 'connection' requirement for each statement.
Line 154:         self.execute(
Line 155:             connection=connection,
Line 156:             statement="""
Line 157:                 INSERT INTO fence_kdump_messages (host, received, 
status)


http://gerrit.ovirt.org/#/c/27201/5/packaging/services/ovirt-fence-kdump-listener/listener.py
File packaging/services/ovirt-fence-kdump-listener/listener.py:

Line 26: 
Line 27: # timedelta.total_seconds is included in Python >= 2.7
Line 28: _total_seconds = lambda td: (
Line 29:     (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 
10**6
Line 30: )
oh... sorry.

please move this to class level as function.

also, consider just use

 long(datetime.strftime("%s"))

so you have seconds since 1970 instead of using timedelta
Line 31: 
Line 32: _now = lambda: datetime.datetime.now(dateutil.tz.tzutc())
Line 33: 
Line 34: 


Line 28: _total_seconds = lambda td: (
Line 29:     (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 
10**6
Line 30: )
Line 31: 
Line 32: _now = lambda: datetime.datetime.now(dateutil.tz.tzutc())
please either put this in function or at utilities module... but avoid putting 
anything at global scope.
Line 33: 
Line 34: 
Line 35: class FenceKdumpListener(base.Base):
Line 36:     # Host kdump flow states


Line 41:     ) = (
Line 42:         'started',
Line 43:         'dumping',
Line 44:         'finished',
Line 45:     )
if you want symbolic names... easier to read:

 SESSION_STATE_INITIAL = 'started'
 SESSION_STATE_DUMPING = 'dumping'
 SESSION_STATE_CLOSED = 'finished'
Line 46: 
Line 47:     # Magic number for fence_kdump message version 1
Line 48:     _FK_MSG_MAGIC_V1 = 456141376
Line 49: 


Line 110:             (data, address) = self._recvfrom()
Line 111:             if address is None:
Line 112:                 self._house_keeping()
Line 113:             else:
Line 114:                 if self._verify_message(data):
this should go into _handle_message - one place to manage the message
Line 115:                     entry = self._sessions.get(address[0])
Line 116:                     if entry is None:
Line 117:                         entry = {
Line 118:                             'status': self.SESSION_STATE_INITIAL,


Line 121:                         }
Line 122:                     self._handle_message(entry)
Line 123: 
Line 124:                 else:
Line 125:                     self.logger.warning(
same
Line 126:                         _(
Line 127:                             "Received invalid message '{msg}'"
Line 128:                             " from host '{host}'."
Line 129:                         ).format(


Line 132:                         )
Line 133:                     )
Line 134: 
Line 135:     def _house_keeping(self):
Line 136:         with contextlib.closing(self._db.connect()) as connection:
not sure why you opening connections and not reuse single.
Line 137:             self._house_keeping_sessions(connection)
Line 138:             self._heartbeat(connection)
Line 139: 
Line 140:     def _parse_number(self, data, offset=0):


Line 136:         with contextlib.closing(self._db.connect()) as connection:
Line 137:             self._house_keeping_sessions(connection)
Line 138:             self._heartbeat(connection)
Line 139: 
Line 140:     def _parse_number(self, data, offset=0):
this can be inner function of _verify_message.
Line 141:         number = None
Line 142:         if data is not None:
Line 143:             number = struct.unpack_from("<I", data, offset)[0]
Line 144:         return number


Line 140:     def _parse_number(self, data, offset=0):
Line 141:         number = None
Line 142:         if data is not None:
Line 143:             number = struct.unpack_from("<I", data, offset)[0]
Line 144:         return number
why calling parse if data is None?
Line 145: 
Line 146:     def _verify_message(self, message):
Line 147:         valid = False
Line 148:         if message:


Line 144:         return number
Line 145: 
Line 146:     def _verify_message(self, message):
Line 147:         valid = False
Line 148:         if message:
why calling if message is nothing?

you should also verify message length before you start parsing.
Line 149:             magic = self._parse_number(message)
Line 150:             version = self._parse_number(message, 4)
Line 151:             if magic == self._FK_MSG_MAGIC_V1 and version == 1:
Line 152:                 valid = True


Line 147:         valid = False
Line 148:         if message:
Line 149:             magic = self._parse_number(message)
Line 150:             version = self._parse_number(message, 4)
Line 151:             if magic == self._FK_MSG_MAGIC_V1 and version == 1:
should be:

 valid = (
     magic == self._FK_MSG_MAGIC_V1 and
     version == 1
 )

strange that for one you have constant and for the other no :)
Line 152:                 valid = True
Line 153:         return valid
Line 154: 
Line 155:     def _handle_message(self, entry):


Line 155:     def _handle_message(self, entry):
Line 156:         received = _now()
Line 157: 
Line 158:         try:
Line 159:             with contextlib.closing(self._db.connect()) as connection:
why don't you keep connection open for the entire run?
Line 160:                 self._db.create_fence_kdump_message(
Line 161:                     connection=connection,
Line 162:                     host=entry['host'],
Line 163:                     received=received,


Line 174:                             )
Line 175:                         )
Line 176:                     )
Line 177:                     entry['status'] = self.SESSION_STATE_DUMPING
Line 178:                     self._sessions[entry['host']] = entry
why do you need to modify _sessions? it is already set for you.
Line 179: 
Line 180:                 elif entry['status'] == self.SESSION_STATE_DUMPING:
Line 181:                     self.logger.debug(
Line 182:                         'dumping %s',


Line 180:                 elif entry['status'] == self.SESSION_STATE_DUMPING:
Line 181:                     self.logger.debug(
Line 182:                         'dumping %s',
Line 183:                         entry,
Line 184:                     )
update database?
Line 185:         except StandardError as e:
Line 186:             self.logger.error(
Line 187:                 _(
Line 188:                     "Error updating host {host} status to {status}:"


Line 226:                 except StandardError as e:
Line 227:                     self.logger.error(
Line 228:                         _(
Line 229:                             "Error updating host {host} status to 
{status}:"
Line 230:                             " {error}."
we usually put the spaces as trailing spaces.
Line 231:                         ).format(
Line 232:                             host=session['host'],
Line 233:                             status=new_state,
Line 234:                             error=e,


Line 266:                 )
Line 267:                 self.logger.debug('Exception',  exc_info=True)
Line 268: 
Line 269:     def _init_sessions(self):
Line 270:         sessions = {}
I would have set the _sessions for {} and rename this function to 
_populate_in_progress() or something similar, to make it easier to understand 
that you actually access the dao.
Line 271: 
Line 272:         for record in self._db.get_unfinished_flows():
Line 273:             # if record is saved to db, in memory cache status
Line 274:             # is always 'dumping'


Line 271: 
Line 272:         for record in self._db.get_unfinished_flows():
Line 273:             # if record is saved to db, in memory cache status
Line 274:             # is always 'dumping'
Line 275:             record['status'] = self.SESSION_STATE_DUMPING
please use the term address as in my example, per true (ip, port), unless this 
fence changing source port(?), then we should think of something.

but even if we use only ip the term address is more correct.

you can put on session whatever other information you would like, like the vdsm 
uuid.
Line 276:             sessions[record['host']] = record
Line 277: 
Line 278:         return sessions
Line 279: 


Line 274:             # is always 'dumping'
Line 275:             record['status'] = self.SESSION_STATE_DUMPING
Line 276:             sessions[record['host']] = record
Line 277: 
Line 278:         return sessions
no need to return sessions, you can modify object state as any member.
Line 279: 
Line 280: 


http://gerrit.ovirt.org/#/c/27201/5/packaging/services/ovirt-fence-kdump-listener/ovirt-fence-kdump-listener.conf.in
File 
packaging/services/ovirt-fence-kdump-listener/ovirt-fence-kdump-listener.conf.in:

Line 2: # This is a default configuration file for oVirt/RHEV-M fence_kdump 
listener
Line 3: #
Line 4: TRACE_ENABLE=False
Line 5: TRACE_FILE=
Line 6: ENGINE_USR="@ENGINE_USR@"
at least two are missing:

1. listen port

2. listen address, default 0.0.0.0


http://gerrit.ovirt.org/#/c/27201/5/packaging/services/ovirt-fence-kdump-listener/ovirt-fence-kdump-listener.py
File 
packaging/services/ovirt-fence-kdump-listener/ovirt-fence-kdump-listener.py:

Line 88:         return (consoleLog, consoleLog)
Line 89: 
Line 90:     def daemonContext(self):
Line 91:         db_manager = db.DbManager(
Line 92:             host=self._config.get("ENGINE_DB_HOST"),
please attempt to use single quotes all over.
Line 93:             port=self._config.get("ENGINE_DB_PORT"),
Line 94:             database=self._config.get("ENGINE_DB_DATABASE"),
Line 95:             username=self._config.get("ENGINE_DB_USER"),
Line 96:             password=self._config.get("ENGINE_DB_PASSWORD"),


Line 93:             port=self._config.get("ENGINE_DB_PORT"),
Line 94:             database=self._config.get("ENGINE_DB_DATABASE"),
Line 95:             username=self._config.get("ENGINE_DB_USER"),
Line 96:             password=self._config.get("ENGINE_DB_PASSWORD"),
Line 97:             secured=self._config.get("ENGINE_DB_SECURED") == 'True',
do you want to load the engine config as well? see the notifier service for 
example.
Line 98:             secure_validation=(
Line 99:                 self._config.get("ENGINE_DB_SECURED_VALIDATION") == 
'True'
Line 100:             ),
Line 101:         )


Line 95:             username=self._config.get("ENGINE_DB_USER"),
Line 96:             password=self._config.get("ENGINE_DB_PASSWORD"),
Line 97:             secured=self._config.get("ENGINE_DB_SECURED") == 'True',
Line 98:             secure_validation=(
Line 99:                 self._config.get("ENGINE_DB_SECURED_VALIDATION") == 
'True'
please do not compare to 'True', use self._config.getboolean()
Line 100:             ),
Line 101:         )
Line 102: 
Line 103:         config_values = db_manager.load_config_values()


http://gerrit.ovirt.org/#/c/27201/5/packaging/services/ovirt-fence-kdump-listener/ovirt-fence-kdump-listener.systemd.in
File 
packaging/services/ovirt-fence-kdump-listener/ovirt-fence-kdump-listener.systemd.in:

Line 5: Type=notify
Line 6: User=@ENGINE_USER@
Line 7: Group=@ENGINE_GROUP@
Line 8: LimitNOFILE=65535
Line 9: LimitNPROC=2048
you do not need the above two
Line 10: 
ExecStart=@ENGINE_USR@/services/ovirt-fence-kdump-listener/ovirt-fence-kdump-listener.py
 --systemd=notify $EXTRA_ARGS start
Line 11: EnvironmentFile=-/etc/sysconfig/ovirt-fence-kdump-listener
Line 12: 
Line 13: [Install]


http://gerrit.ovirt.org/#/c/27201/5/packaging/services/ovirt-fence-kdump-listener/ovirt-fence-kdump-listener.sysv.in
File 
packaging/services/ovirt-fence-kdump-listener/ovirt-fence-kdump-listener.sysv.in:

Line 33:                        exit 4
Line 34:                fi
Line 35:                echo -n $"Starting $PROG: "
Line 36:                ulimit -n ${FILENO:-65535}
Line 37:                ulimit -n ${NPROC:-2048}
you do not need the above two
Line 38:                touch "${PIDFILE}"
Line 39:                chown "${USER}" "${PIDFILE}"
Line 40:                daemon --user "${USER}" --pidfile="${PIDFILE}" \
Line 41:                        "${ENGINE_USR}/services/${NAME}/${NAME}.py" \


-- 
To view, visit http://gerrit.ovirt.org/27201
To unsubscribe, visit http://gerrit.ovirt.org/settings

Gerrit-MessageType: comment
Gerrit-Change-Id: Ieec3bad47bbba860a52a9ff4e2eb7f61277f4e36
Gerrit-PatchSet: 5
Gerrit-Project: ovirt-engine
Gerrit-Branch: master
Gerrit-Owner: Martin Peřina <mper...@redhat.com>
Gerrit-Reviewer: Alon Bar-Lev <alo...@redhat.com>
Gerrit-Reviewer: Barak Azulay <bazu...@redhat.com>
Gerrit-Reviewer: Eli Mesika <emes...@redhat.com>
Gerrit-Reviewer: Martin Peřina <mper...@redhat.com>
Gerrit-Reviewer: Oved Ourfali <oourf...@redhat.com>
Gerrit-Reviewer: Saggi Mizrahi <smizr...@redhat.com>
Gerrit-Reviewer: automat...@ovirt.org
Gerrit-Reviewer: oVirt Jenkins CI Server
Gerrit-HasComments: Yes
_______________________________________________
Engine-patches mailing list
Engine-patches@ovirt.org
http://lists.ovirt.org/mailman/listinfo/engine-patches

Reply via email to