At http://bazaar.launchpad.net/~ndb-connectors/ndb-connectors/devel
------------------------------------------------------------
revno: 258
revision-id: mtaylor@stripped
parent: mtaylor@stripped
committer: Monty Taylor <mtaylor@stripped>
branch nick: devel
timestamp: Sat 2007-11-17 12:11:59 -0800
message:
Added a whole bunch of work on MGM API.
modified:
interface/mgmapi/NdbLogEvent.i ndblogevent.i-20070906065931-8drgzkovsy4cdn0b-1
interface/mgmapi/NdbLogEventManager.i
ndbmgmlogeventhandle-20070906065939-pj4qrhof8kkzg3d1-1
=== modified file 'interface/mgmapi/NdbLogEvent.i'
--- a/interface/mgmapi/NdbLogEvent.i 2007-11-06 22:59:00 +0000
+++ b/interface/mgmapi/NdbLogEvent.i 2007-11-17 20:11:59 +0000
@@ -49,18 +49,6 @@
/** Node ID of the node that reported the log event */
unsigned source_nodeid;
- union {
- /* CONNECT */
- /** Log event specific data for for corresponding NDB_LE_ log event */
- struct {
- unsigned node;
- } Connected;
-
- /** Log event specific data for for corresponding NDB_LE_ log event */
- struct {
- unsigned node;
- } Disconnected;
- };
};
%extend ndb_logevent {
@@ -102,9 +90,9 @@
};
- class Connected : public BaseEvent {
+ class ConnectedEvent : public BaseEvent {
public:
- Connected(ndb_logevent theEvent) {
+ ConnectedEvent(ndb_logevent theEvent) {
event=theEvent;
}
unsigned getNode() {
@@ -112,9 +100,9 @@
}
};
- class Disconnected : public BaseEvent {
+ class DisconnectedEvent : public BaseEvent {
public:
- Disconnected(ndb_logevent theEvent) {
+ DisconnectedEvent(ndb_logevent theEvent) {
event=theEvent;
}
unsigned getNode() {
@@ -122,9 +110,9 @@
}
};
- class CommunicationOpened : public BaseEvent {
+ class CommunicationOpenedEvent : public BaseEvent {
public:
- CommunicationOpened(ndb_logevent theEvent) {
+ CommunicationOpenedEvent(ndb_logevent theEvent) {
event=theEvent;
}
unsigned getNode() {
@@ -132,18 +120,18 @@
}
};
- class CommunicationClosed : public BaseEvent {
+ class CommunicationClosedEvent : public BaseEvent {
public:
- CommunicationClosed(ndb_logevent theEvent) {
+ CommunicationClosedEvent(ndb_logevent theEvent) {
event=theEvent;
}
unsigned getNode() {
return event.CommunicationClosed.node;
}
};
- class ConnectedApiVersion : public BaseEvent {
+ class ConnectedApiVersionEvent : public BaseEvent {
public:
- ConnectedApiVersion(ndb_logevent theEvent) {
+ ConnectedApiVersionEvent(ndb_logevent theEvent) {
event=theEvent;
}
unsigned getNode() {
@@ -154,22 +142,599 @@
}
};
-
- class BackupAborted : public BaseEvent {
-
- public:
- BackupAborted(ndb_logevent theEvent) {
- event=theEvent;
- }
- unsigned getBackupId() {
- return event.BackupAborted.backup_id;
- }
- unsigned getError() {
- return event.BackupAborted.error;
- }
- unsigned getStartingNode() {
- return event.BackupAborted.starting_node;
- }
-};
+
+ class GlobalCheckpointStartedEvent : public BaseEvent {
+ public:
+
+ unsigned gci;
+
+ GlobalCheckpointStartedEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ gci=event.GlobalCheckpointStarted.gci;
+ }
+ };
+
+ class GlobalCheckpointCompletedEvent : public BaseEvent {
+ public:
+
+ unsigned gci;
+
+ GlobalCheckpointCompletedEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ gci=event.GlobalCheckpointCompleted.gci;
+ }
+ };
+
+
+ class LocalCheckpointStartedEvent : public BaseEvent {
+ public:
+
+ unsigned lci;
+ unsigned keepGci;
+ unsigned restoreGci;
+
+ LocalCheckpointStartedEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ lci = event.LocalCheckpointStarted.lci;
+ keepGci = event.LocalCheckpointStarted.keep_gci;
+ restoreGci = event.LocalCheckpointStarted.restore_gci;
+
+ }
+ };
+
+ class LocalCheckpointCompletedEvent : public BaseEvent {
+ public:
+ unsigned lci;
+
+ LocalCheckpointCompletedEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ lci=event.LocalCheckpointCompleted.lci;
+ }
+ };
+
+ class LCPStoppedInCalcKeepGciEvent : public BaseEvent {
+ public:
+ unsigned data;
+
+ LCPStoppedInCalcKeepGciEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ data=event.LCPStoppedInCalcKeepGci.data;
+ }
+ };
+
+ class LCPFragmentCompletedEvent : public BaseEvent {
+ public:
+ unsigned node;
+ unsigned tableId;
+ unsigned fragmentId;
+
+ LCPFragmentCompletedEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ node=event.LCPFragmentCompleted.node;
+ tableId=event.LCPFragmentCompleted.table_id;
+ fragmentId=event.LCPFragmentCompleted.fragment_id;
+ }
+ };
+
+ class UndoLogBlockedEvent : public BaseEvent {
+ public:
+ unsigned accCount;
+ unsigned tupCount;
+
+ UndoLogBlockedEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ accCount=event.UndoLogBlocked.acc_count;
+ tupCount=event.UndoLogBlocked.tup_count;
+ }
+ };
+
+ class NDBStartStartedEvent : public BaseEvent {
+ public:
+ unsigned version;
+
+ NDBStartStartedEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ version=event.NDBStartStarted.version;
+ }
+ };
+
+ class NDBStartCompletedEvent : public BaseEvent {
+ public:
+ unsigned version;
+
+ NDBStartCompletedEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ version=event.NDBStartCompleted.version;
+ }
+ };
+
+ class STTORRYRecievedEvent : public BaseEvent {
+ public:
+ STTORRYRecievedEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ }
+ };
+
+ class StartPhaseCompletedEvent : public BaseEvent {
+ public:
+ unsigned phase;
+ unsigned startType;
+
+ StartPhaseCompletedEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ phase=event.StartPhaseCompleted.phase;
+ startType=event.StartPhaseCompleted.starttype;
+ }
+ };
+
+ class CmRegConfEvent : public BaseEvent {
+ public:
+ unsigned ownId;
+ unsigned presidentId;
+ unsigned dynamicId;
+
+ CmRegConfEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ ownId=event.CM_REGCONF.own_id;
+ presidentId=event.CM_REGCONF.president_id;
+ dynamicId=event.CM_REGCONF.dynamic_id;
+ }
+ };
+
+ class CmRegRefEvent : public BaseEvent {
+ public:
+ unsigned ownId;
+ unsigned otherId;
+ unsigned cause;
+
+ CmRegRefEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ ownId=event.CM_REGREF.own_id;
+ otherId=event.CM_REGREF.other_id;
+ cause=event.CM_REGREF.cause;
+ }
+ };
+
+ class FindNeighboursEvent : public BaseEvent {
+ public:
+ unsigned ownId;
+ unsigned leftId;
+ unsigned rightId;
+ unsigned dynamicId;
+
+ FindNeighboursEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ ownId=event.FIND_NEIGHBOURS.own_id;
+ leftId=event.FIND_NEIGHBOURS.left_id;
+ rightId=event.FIND_NEIGHBOURS.right_id;
+ dynamicId=event.FIND_NEIGHBOURS.dynamic_id;
+ }
+ };
+
+ class NDBStopStartedEvent : public BaseEvent {
+ public:
+ unsigned stopType;
+ NDBStopStartedEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ stopType=event.NDBStopStarted.stoptype;
+ }
+ };
+
+ class NDBStopCompletedEvent : public BaseEvent {
+ public:
+ unsigned action;
+ unsigned sigNum;
+
+ NDBStopCompletedEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ action=event.NDBStopCompleted.action;
+ sigNum=event.NDBStopCompleted.signum;
+ }
+ };
+
+ class NDBStopForcedEvent : public BaseEvent {
+ public:
+ unsigned action;
+ unsigned signum;
+ unsigned error;
+ unsigned sphase;
+ unsigned extra;
+
+ NDBStopForcedEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ action=event.NDBStopForced.action;
+ signum=event.NDBStopForced.signum;
+ error=event.NDBStopForced.error;
+ sphase=event.NDBStopForced.sphase;
+ extra=event.NDBStopForced.extra;
+ }
+ };
+
+ class NDBStopAbortedEvent : public BaseEvent {
+ public:
+ NDBStopAbortedEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ }
+ };
+
+ class StartREDOLogEvent : public BaseEvent {
+ public:
+ unsigned node;
+ unsigned keepGci;
+ unsigned completedGci;
+ unsigned restorableGci;
+
+ StartREDOLogEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ node=event.StartREDOLog.node;
+ keepGci=event.StartREDOLog.keep_gci;
+ completedGci=event.StartREDOLog.completed_gci;
+ restorableGci=event.StartREDOLog.restorable_gci;
+ }
+ };
+
+ class StartLogEvent : public BaseEvent {
+ public:
+ unsigned logPart;
+ unsigned startMb;
+ unsigned stopMb;
+ unsigned gci;
+
+ StartLogEvent(ndb_logevent theEvent) {
+ event=theEvent;
+
+ logPart=event.StartLog.log_part;
+ startMb=event.StartLog.start_mb;
+ stopMb=event.StartLog.stop_mb;
+ gci=event.StartLog.gci;
+ }
+ };
+
+ class UNDORecordsExecutedEvent : public BaseEvent {
+ public:
+ UNDORecordsExecutedEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ }
+ unsigned block;
+ unsigned data1;
+ unsigned data2;
+ unsigned data3;
+ unsigned data4;
+ unsigned data5;
+ unsigned data6;
+ unsigned data7;
+ unsigned data8;
+ unsigned data9;
+ unsigned data10;
+ };
+
+ class NRCopyDictEvent : public BaseEvent {
+ public:
+ NRCopyDictEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ }
+ };
+
+ class NRCopyDistrEvent : public BaseEvent {
+ public:
+ NRCopyDistrEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ }
+ };
+
+ class NRCopyFragsStartedEvent : public BaseEvent {
+ public:
+ NRCopyFragsStartedEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ }
+ unsigned dest_node;
+ };
+
+ class NRCopyFragDoneEvent : public BaseEvent {
+ public:
+ NRCopyFragDoneEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ }
+ unsigned dest_node;
+ unsigned table_id;
+ unsigned fragment_id;
+ };
+
+ class NRCopyFragsCompletedEvent : public BaseEvent {
+ public:
+ NRCopyFragsCompletedEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ }
+ unsigned dest_node;
+ };
+
+ class NodeFailCompletedEvent : public BaseEvent {
+ public:
+ NodeFailCompletedEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ }
+ unsigned block; /* 0 = all */
+ unsigned failed_node;
+ unsigned completing_node; /* 0 = all */
+ };
+
+ class NodeFAILREPEvent : public BaseEvent {
+ public:
+ NodeFAILREPEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ }
+ unsigned failed_node;
+ unsigned failure_state;
+ };
+
+ class ArbitStateEvent : public BaseEvent {
+ public:
+ ArbitStateEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ }
+ unsigned code; /* code & state << 16 */
+ unsigned arbit_node;
+ unsigned ticket_0;
+ unsigned ticket_1;
+ /* TODO */
+ };
+
+ class ArbitResultEvent : public BaseEvent {
+ public:
+ ArbitResultEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ }
+ unsigned code; /* code & state << 16 */
+ unsigned arbit_node;
+ unsigned ticket_0;
+ unsigned ticket_1;
+ /* TODO */
+ };
+
+ class GCPTakeoverStartedEvent : public BaseEvent {
+ public:
+ GCPTakeoverStartedEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ }
+ };
+
+ class GCPTakeoverCompletedEvent : public BaseEvent {
+ public:
+ GCPTakeoverCompletedEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ }
+ };
+
+ class LCPTakeoverStartedEvent : public BaseEvent {
+ public:
+ LCPTakeoverStartedEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ }
+ };
+
+ class LCPTakeoverCompletedEvent : public BaseEvent {
+ public:
+ LCPTakeoverCompletedEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ }
+ unsigned state;
+ };
+
+ class TransReportCountersEvent : public BaseEvent {
+ public:
+ TransReportCountersEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ }
+ unsigned trans_count;
+ unsigned commit_count;
+ unsigned read_count;
+ unsigned simple_read_count;
+ unsigned write_count;
+ unsigned attrinfo_count;
+ unsigned conc_op_count;
+ unsigned abort_count;
+ unsigned scan_count;
+ unsigned range_scan_count;
+ };
+
+ class OperationReportCountersEvent : public BaseEvent {
+ public:
+ OperationReportCountersEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ }
+ unsigned ops;
+ };
+
+ class TableCreatedEvent : public BaseEvent {
+ public:
+ TableCreatedEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ }
+ unsigned table_id;
+ };
+
+ class JobStatisticEvent : public BaseEvent {
+ public:
+ JobStatisticEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ }
+ unsigned mean_loop_count;
+ };
+
+ class SendBytesStatisticEvent : public BaseEvent {
+ public:
+ SendBytesStatisticEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ }
+ unsigned to_node;
+ unsigned mean_sent_bytes;
+ };
+
+ class ReceiveBytesStatisticEvent : public BaseEvent {
+ public:
+ ReceiveBytesStatisticEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ }
+ unsigned from_node;
+ unsigned mean_received_bytes;
+ };
+
+ class MemoryUsageEvent : public BaseEvent {
+ public:
+ MemoryUsageEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ }
+ int gth;
+ unsigned page_size_kb;
+ unsigned pages_used;
+ unsigned pages_total;
+ unsigned block;
+ };
+
+ class TransporterErrorEvent : public BaseEvent {
+ public:
+ TransporterErrorEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ }
+ unsigned to_node;
+ unsigned code;
+ };
+
+ class TransporterWarningEvent : public BaseEvent {
+ public:
+ TransporterWarningEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ }
+ unsigned to_node;
+ unsigned code;
+ };
+
+ class MissedHeartbeatEvent : public BaseEvent {
+ public:
+ MissedHeartbeatEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ }
+ unsigned node;
+ unsigned count;
+ };
+
+ class DeadDueToHeartbeatEvent : public BaseEvent {
+ public:
+ DeadDueToHeartbeatEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ }
+ unsigned node;
+ };
+
+ class WarningEventEvent : public BaseEvent {
+ public:
+ WarningEventEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ }
+ /* TODO */
+ };
+
+ class SentHeartbeatEvent : public BaseEvent {
+ public:
+ SentHeartbeatEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ }
+ unsigned node;
+ };
+
+ class CreateLogBytesEvent : public BaseEvent {
+ public:
+ CreateLogBytesEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ }
+ unsigned node;
+ };
+
+ class InfoEventEvent : public BaseEvent {
+ public:
+ InfoEventEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ }
+ /* TODO */
+ };
+
+ class EventBufferStatusEvent : public BaseEvent {
+ public:
+ EventBufferStatusEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ }
+ unsigned usage;
+ unsigned alloc;
+ unsigned max;
+ unsigned apply_gci_l;
+ unsigned apply_gci_h;
+ unsigned latest_gci_l;
+ unsigned latest_gci_h;
+ };
+
+
+ class BackupStartedEvent : public BaseEvent {
+ public:
+ BackupStartedEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ }
+ unsigned starting_node;
+ unsigned backup_id;
+ };
+
+ class BackupFailedToStartEvent : public BaseEvent {
+ public:
+ BackupFailedToStartEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ }
+ unsigned starting_node;
+ unsigned error;
+ };
+
+ class BackupCompletedEvent : public BaseEvent {
+ public:
+ BackupCompletedEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ }
+ unsigned starting_node;
+ unsigned backup_id;
+ unsigned start_gci;
+ unsigned stop_gci;
+ unsigned n_records;
+ unsigned n_log_records;
+ unsigned n_bytes;
+ unsigned n_log_bytes;
+ };
+
+ class BackupAbortedEvent : public BaseEvent {
+ public:
+ BackupAbortedEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ }
+ unsigned starting_node;
+ unsigned backup_id;
+ unsigned error;
+ };
+
+ class SingleUserEvent : public BaseEvent {
+ public:
+ SingleUserEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ }
+ unsigned type;
+ unsigned node_id;
+ };
+
+ class StartReportEvent : public BaseEvent {
+ public:
+ StartReportEvent(ndb_logevent theEvent) {
+ event=theEvent;
+ }
+ unsigned report_type;
+ unsigned remaining_time;
+ unsigned bitmask_size;
+ unsigned bitmask_data[1];
+ };
+
%}
=== modified file 'interface/mgmapi/NdbLogEventManager.i'
--- a/interface/mgmapi/NdbLogEventManager.i 2007-11-06 22:59:00 +0000
+++ b/interface/mgmapi/NdbLogEventManager.i 2007-11-17 20:11:59 +0000
@@ -23,7 +23,9 @@
#include <map>
#include <stdexcept>
#include "mgmapi.h"
+ %}
+%{
class NdbLogEventListener {
public:
virtual ~NdbLogEventListener() {}
@@ -89,12 +91,69 @@
if
(!((evtCategoryListeners.insert(std::make_pair<ndb_mgm_event_category,std::vector<NdbLogEventCategoryListener*>
>(theCategory,evtVec))).second)) {
return -1;
}
+ evtVec = evtCategoryListeners.at(theCategory);
};
evtVec.push_back(listener);
return 0;
}
+
+ BaseEvent * getLogEvent(unsigned timeout_in_milliseconds) {
+
+ //TODO: replace malloc with a pinned byte-array, until get_next_event()
+ // is called, then unpin it
+
+ /*ndb_logevent* event = (ndb_logevent*) malloc (sizeof(ndb_logevent));
+ if (event==0) {
+ return NULL;
+ }*/
+ ndb_logevent event;
+
+ //int r= ndb_logevent_get_next(handle,&(*event),timeout_in_milliseconds);
+ int r= ndb_logevent_get_next(handle,&event,timeout_in_milliseconds);
+
+ BaseEvent * theEvent = NULL;
+ //TODO: Figure out how to deal with no event properly
+ if (r<=0) {
+ //free(event);
+ return NULL;
+ }
+
+ switch(event.type) {
+ case NDB_LE_Connected:
+ theEvent = (BaseEvent *)(new ConnectedEvent(event));
+ break;
+ default:
+ break;
+ }
+ // r > 0, event exists;
+ // r==0, no event (timeout)
+ // r==-1, error
+
+
+ return theEvent;
+
+ }
};
+
+
+ /**
+ * Attempt to retrieve next log event and will fill in the supplied
+ * struct dst
+ *
+ * @param dst Pointer to struct to fill in event information
+ * @param timeout_in_milliseconds Timeout for waiting for event
+ *
+ * @return >0 if event exists, 0 no event (timed out), or -1 on error.
+ *
+ * @note Return value <=0 will leave dst untouched
+ */
+/* int getNext(ndb_logevent *dst,
+ unsigned timeout_in_milliseconds) {
+ return ndb_logevent_get_next($self, dst, timeout_in_milliseconds);
+ }*/
+
+
%}
@@ -119,6 +178,8 @@
virtual Ndb_logevent_type getEventType();
};
+%newobject NdbLogEventManager::getLogEvent;
+
class NdbLogEventManager {
NdbLogEventManager();
~NdbLogEventManager();
@@ -129,6 +190,7 @@
int registerListener(NdbLogEventTypeListener * listener);
bool unregisterListener(NdbLogEventTypeListener * listener);
int registerCategoryListener(NdbLogEventCategoryListener * listener);
+ BaseEvent * getLogEvent(unsigned timeout_in_milliseconds);
};
@@ -159,48 +221,6 @@
ndb_mgm_destroy_logevent_handle(&$self);
}
- ndb_logevent* getLogEvent(unsigned timeout_in_milliseconds) {
-
- //TODO: replace malloc with a pinned byte-array, until get_next_event()
- // is called, then unpin it
-
- ndb_logevent* event = (ndb_logevent*) malloc (sizeof(ndb_logevent));
- if (event==0) {
- return NULL;
- }
-
- int r= ndb_logevent_get_next($self,&(*event),timeout_in_milliseconds);
-
- // r > 0, event exists;
- // r==0, no event (timeout)
- // r==-1, error
-
- //TODO: Figure out how to deal with no event properly
- if (r<=0) {
- free(event);
- return NULL;
- }
- return event;
-
- }
-
-*/
- /**
- * Attempt to retrieve next log event and will fill in the supplied
- * struct dst
- *
- * @param dst Pointer to struct to fill in event information
- * @param timeout_in_milliseconds Timeout for waiting for event
- *
- * @return >0 if event exists, 0 no event (timed out), or -1 on error.
- *
- * @note Return value <=0 will leave dst untouched
- */
-/* int getNext(ndb_logevent *dst,
- unsigned timeout_in_milliseconds) {
- return ndb_logevent_get_next($self, dst, timeout_in_milliseconds);
- }
-*/
/**
* Retrieve laterst error code
*
| Thread |
|---|
| • Rev 258: Added a whole bunch of work on MGM API. in http://bazaar.launchpad.net/~ndb-connectors/ndb-connectors/devel | Monty Taylor | 17 Nov |